为了熟悉WPF和MVVM的概念,我构建了一个数独板的可视化表示。
我的(简化(设置如下所示(任何地方的视图中都没有自定义代码隐藏(:
我有一个 MainWindow.xaml:
<Window x:Class="Sudoku.WPF.MainWindow">
<Window.DataContext>
<models:MainWindowViewModel/>
</Window.DataContext>
<ctrl:SudokuBoard DataContext="{Binding Path=GameViewModel}"/>
</Window>
我的主窗口视图模型:
class MainWindowViewModel
{
public MainWindowViewModel()
{
IGame g = new Game(4);
this.GameViewModel = new GameViewModel(g);
}
public IGameViewModel GameViewModel { get; private set; }
}
数独板是一个UserControl
。其DataContext
设置为GameViewModel
如上所述。GameViewModel
的相关部分,Elements
填充在 ctor 中,Possibilities
通过以下命令设置:
public IList<CellViewModel> Elements { get; private set; }
private bool _showPossibilities;
public bool ShowPossibilities
{
get { return _showPossibilities; }
set
{
_showPossibilities = value;
OnPropertyChanged();
}
}
SudokuBoard.xaml
我有:
<ItemsControl x:Name="SudokuGrid" ItemsSource="{Binding Path=Elements}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Style="{StaticResource ToggleContentStyle}"
Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Elements
是在GameViewModel
的构造函数中生成的CellViewModel
的集合。
现在来问问题:我的ToggleContentStyle
如<UserControl.Resources>
中定义:
<Style x:Key="ToggleContentStyle" TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.ShowPossibilities, ElementName=SudokuGrid}" Value="False">
<Setter Property="ContentTemplate" Value="{StaticResource valueTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=DataContext.ShowPossibilities, ElementName=SudokuGrid}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource possibilityTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
(两个ContentTemplate
都只是在不同的表示中显示单个CellViewModel
的其他属性(
问题 1:我必须显式引用DataContext
才能访问ShowPossibilities
属性。如果我把它排除在外,这样Path=ShowPossibilities
,我会得到一个CellViewModel
的ToString()
表示UniformGrid
。我的假设是,这是因为样式是从ItemTemplate
引用的,其绑定设置为单个CellViewModel
。这个假设成立吗?
问题2:当我省略ElementName
部分时,我也得到了CellViewModel
的ToString()
表示。现在我真的很困惑。为什么需要它?
Datacontext 是一个标记为继承的依赖属性。 这意味着它继承了可视化树。
当您绑定默认位置时,它将在数据上下文中查找源。
这是简单的情况。
假设您有一个窗口,并且将数据上下文设置为窗口视图模型,并在该窗口中粘贴一个文本框。您将它的文本绑定到 FooText。这意味着文本框将转到该 WindowViewmodel 实例中查找 FooText 属性。
到目前为止,一切都很简单。
下一个。。。
使用元素名。
它的作用是说去看看这个元素。寻找一个属性。如果你用上面的文本框这样做了,那么它就会期望一个依赖属性FooText,无论你指向什么。
数据上下文是一个依赖属性。
当您这样做时:
"{Binding FooProperty
这是以下的简写:
"{Binding Path=FooProperty
其中 FooProperty 是一个属性路径,而不仅仅是一个属性的名称。
这可能值得谷歌搜索,但意味着您可以使用"点符号"沿着对象图向下移动并获取对象(在对象上...(的属性。
因此DataContext.Foo或Tag.Anything(因为tag是控件将具有的另一个依赖属性(。
让我们继续讨论其他一些复杂情况。
datcontext沿可视化树继承,但这里有一些陷阱。因为 有些东西看起来像是控件,但不是(如数据网格文本列(。模板化的东西可能很棘手。项目控件是一种明显且相关的特殊情况。
对于 itemscontrol,每行中任何内容的数据上下文都是它从 itemssource 呈现给的任何项。通常,您将行视图模型的可观察集合绑定到该项源。因此(有点明显(列表框或数据网格显示您在每行中提供给它的每个行视图模型的数据。
如果你想去获取一个属性不在那个行视图模型中,你需要告诉它去别的地方看。
- 当您在绑定中指定元素(例如 ElementName=SudokuGrid(时,路径必须引用该元素的任何属性。由于此元素是 wpf 控件,因此 DataContext 是其属性之一,但 ShowPossibilities 不是。因此,如果您只执行 Path=ShowPossibilities ,它将根本无法找到该路径。
- 如果根本不在绑定中指定元素,则它默认为与控件关联的数据上下文。如果关联的 DataContext 没有属性 ShowPossibilities 它,则无法找到它。
PS:如果你想调试wpf UI以查看运行时的数据上下文是什么,你可以使用像Snoop这样的实用程序。