21

我已经为此工作了大约一个小时,并查看了所有相关的 SO 问题。

我的问题很简单:

我有 HomePageVieModel:

HomePageVieModel
+IList<NewsItem> AllNewsItems
+ICommand OpenNews

我的标记:

<Window DataContext="{Binding HomePageViewModel../>
<ListBox ItemsSource="{Binding Path=AllNewsItems}">
 <ListBox.ItemTemplate>
   <DataTemplate>
       <StackPanel>
        <TextBlock>
           <Hyperlink Command="{Binding Path=OpenNews}">
               <TextBlock Text="{Binding Path=NewsContent}" />
           </Hyperlink>
        </TextBlock>
      </StackPanel>
    </DataTemplate>
</ListBox.ItemTemplate>

该列表显示所有项目都很好,但是对于我的生活,无论我为命令尝试什么都行不通:

<Hyperlink Command="{Binding Path=OpenNewsItem, RelativeSource={RelativeSource AncestorType=vm:HomePageViewModel, AncestorLevel=1}}">
<Hyperlink Command="{Binding Path=OpenNewsItem, RelativeSource={RelativeSource AncestorType=vm:HomePageViewModel,**Mode=FindAncestor}**}">
<Hyperlink Command="{Binding Path=OpenNewsItem, RelativeSource={RelativeSource AncestorType=vm:HomePageViewModel,**Mode=TemplatedParent}**}">

我总是得到:

System.Windows.Data 错误:4:找不到与参考绑定的源.....

更新 我正在这样设置我的 ViewModel?没想到这很重要:

 <Window.DataContext>
        <Binding Path="HomePage" Source="{StaticResource Locator}"/>
    </Window.DataContext>

我使用了 MVVMLight 工具包中的 ViewModelLocator 类,它具有魔力。

4

7 回答 7

31

稍微不同的示例,但我发现通过在绑定中引用父容器(使用 ElementName),您可以使用 Path 语法访问它的 DataContext 及其后续属性。如下所示:

<ItemsControl x:Name="lstDevices" ItemsSource="{Binding DeviceMappings}">
 <ItemsControl.ItemTemplate>
  <DataTemplate>
   <Grid>
    <ComboBox Text="{Binding Device}" ItemsSource="{Binding ElementName=lstDevices, Path=DataContext.AvailableDevices}" />
    ...
   </Grid>
  </DataTemplate>
 </ItemsControl.ItemTemplate>
</ItemsControl>
于 2012-11-13T04:57:00.880 回答
14

这里有两个问题对你不利。

DataContextforDataTemplate设置为模板正在显示的项目。这意味着您不能只使用绑定而不设置源。

另一个问题是模板意味着该项目在技术上不是逻辑树的一部分,因此您无法搜索DataTemplate节点之外的祖先。

要解决这个问题,您需要让绑定到达逻辑树之外。您可以使用此处定义的 DataContextSpy 。

<ListBox ItemsSource="{Binding Path=AllNewsItems}">
    <ListBox.Resources>
        <l:DataContextSpy x:Key="dataContextSpy" />
    </ListBox.Resources>

    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock>
                   <Hyperlink Command="{Binding Source={StaticResource dataContextSpy}, Path=DataContext.OpenNews}" CommandParameter="{Binding}">
                       <TextBlock Text="{Binding Path=NewsContent}" />
                   </Hyperlink>
               </TextBlock>
           </StackPanel>
       </DataTemplate>
   </ListBox.ItemTemplate>
</ListBox>
于 2011-02-21T08:20:01.533 回答
8

所以看起来你正试图为 HyperLink 提供正确的 DataContext 以触发 ICommand。我认为一个简单的元素名称绑定可以解决这个问题。

<Window x:Name="window" DataContext="{Binding HomePageViewModel../>
 <ListBox ItemsSource="{Binding Path=AllNewsItems}">
 <ListBox.ItemTemplate>
  <DataTemplate>
   <StackPanel>
    <TextBlock>
       <Hyperlink DataContext="{Binding DataContext ,ElementName=window}" Command="{Binding Path=OpenNews}">
           <TextBlock Text="{Binding Path=NewsContent}" />
       </Hyperlink>
    </TextBlock>
  </StackPanel>
</DataTemplate>

AncestorType 仅检查 Visual-Types 而不是 ViewModel 类型。

于 2011-02-21T08:01:56.540 回答
8

尝试这样的事情

<Button Command="{Binding DataContext.YourCommand,RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"

他在列表框中找不到您的命令绑定,因为您设置的数据上下文与该列表框的视图模型不同

于 2011-04-29T09:07:27.783 回答
2

嗯,有点晚了,我知道。但我最近才遇到同样的问题。由于架构原因,我决定使用静态视图模型定位器而不是 dataContextSpy。

<UserControl x:Class="MyView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:locator="clr-namespace: MyNamespace"
             DataContext="{Binding Source={x:Static locator:ViewModelLocator.MyViewModel}}" >
    <ListBox ItemsSource="{Binding Path=AllNewsItems}">        

        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock>
                        <Hyperlink Command="{Binding Source={x:Static locator:ViewModelLocator.MyViewModel}, 
                                                     Path=OpenNews}" 
                                   CommandParameter="{Binding}">
                            <TextBlock Text="{Binding Path=NewsContent}" />
                        </Hyperlink>
                    </TextBlock>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</UserControl>

静态视图模型定位器实例化视图模型:

namespace MyNamespace
{
    public static class ViewModelLocator
    {
        private static MyViewModelType myViewModel = new MyViewModelType();
        public static MyViewModelType MyViewModel 
        {
            get
            {
                return myViewModel ;
            }
        }
    }
}

使用此解决方法是将数据模板绑定到视图模型中的命令的另一种方法。

于 2012-12-14T11:47:58.567 回答
1

@Darren 的答案在大多数情况下效果很好,如果可能的话应该是首选方法。但是,在以下(利基)条件都发生的情况下,它不是一个可行的解决方案:

  • 带有DataGridTemplateColumn的DataGrid
  • .NET 4
  • 视窗

...可能在其他情况下也是如此。理论上它应该在所有版本的 Windows 上都失败;但根据我的经验,ElementName方法适用于 Windows 7 以上的DataGrid,但不适用于 XP。

在以下虚构示例中,我们尝试在UserControl.DataContext(即 ViewModel)上绑定一个名为ShowThingCommand的ICommand :

<UserControl x:Name="ThisUserControl" DataContext="whatever...">
    <DataGrid ItemsSource="{Binding Path=ListOfThings}">
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Thing">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button
                            Command="{Binding ElementName=ThisUserControl, Path=ShowThingCommand}"
                            CommandParameter="{Binding Path=ThingId}"
                            Content="{Binding Path=ThingId}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</UserControl>

由于DataTemplate与主控件不在同一个 VisualTree 中,因此无法通过ElementName引用回控件。

为了解决这个问题,可以使用鲜为人知的 .NET 4 及更高版本{x:Reference}。修改上面的例子:

<UserControl x:Name="ThisUserControl" DataContext="whatever...">
    <DataGrid ItemsSource="{Binding Path=ListOfThings}">
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Thing">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button
                            Command="{Binding Source={x:Reference ThisUserControl}, Path=ShowThingCommand}"
                            CommandParameter="{Binding Path=ThingId}"
                            Content="{Binding Path=ThingId}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</UserControl>

作为参考,请参阅以下 stackoverflow 帖子:

问题 19244111

问题 5834336

...关于为什么ElementName在这种情况下不起作用的解释,请参阅包含 pre-.NET 4 解决方法的博客文章。

于 2016-02-29T21:27:26.043 回答
1
<ListBox xmlns:model="clr-namespace:My.Space;assembly=My.Assembly"
         ItemsSource="{Binding Path=AllNewsItems, Mode=OneWay}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock>
                    <Hyperlink Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.(model:MyNewsModel.OpenNews), Mode=OneWay}"
                               CommandParameter="{Binding Path=., Mode=OneWay}">
                        <TextBlock Text="{Binding Path=NewsContent, Mode=OneWay}" />
                    </Hyperlink>
                </TextBlock>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
于 2020-06-27T08:13:05.393 回答