为使自己熟悉WPF和MVVM概念,我构建了Sudoku板的视觉表示。
我的(简化的)设置是这样的(在任何地方的视图中都没有自定义代码):
我有一个MainWindow.xaml:
<Window x:Class="Sudoku.WPF.MainWindow">
<Window.DataContext>
<models:MainWindowViewModel/>
</Window.DataContext>
<ctrl:SudokuBoard DataContext="{Binding Path=GameViewModel}"/>
</Window>
我的MainWindowViewModel:
class MainWindowViewModel
{
public MainWindowViewModel()
{
IGame g = new Game(4);
this.GameViewModel = new GameViewModel(g);
}
public IGameViewModel GameViewModel { get; private set; }
}
SudokuBoard是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
是在CellViewModel
的构造函数中生成的GameViewModel
的集合。
现在有一个问题: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
会得到一个UniformGrid
,其表示为ToString()
。我的假设是,这是因为样式是从CellViewModel
引用的,并将其绑定设置为单个ItemTemplate
。这个假设有效吗?
问题2:当我省略CellViewModel
部分时,我也得到ElementName
的{{1}}表示形式。现在我真的很困惑。为什么需要它?
答案 0 :(得分:0)
PS:如果要调试wpf UI以查看运行时DataContext是什么,则可以使用Snoop之类的实用程序。
答案 1 :(得分:0)
Datacontext是一个依赖项属性,标记为继承。这意味着它是在视觉树下继承的。
绑定默认位置时,它将在数据上下文中查找源。
这是简单的情况。
假设您有一个窗口,并且其datacontext设置为WindowViewmodel,并在该窗口中粘贴了一个文本框。您将其文本绑定到FooText。这意味着文本框会在WindowViewmodel的该实例中查找FooText属性。
到目前为止,一切都很简单。
下一步...
您使用元素名称。
这就是说去看看这个元素。寻找一个财产。如果您在上方的文本框中执行了此操作,那么无论您指向哪个对象,都应该具有依赖项属性FooText。
Datacontext是依赖项属性。
当您这样做时:
"{Binding FooProperty
这是以下简称:
"{Binding Path=FooProperty
其中FooProperty是属性路径,而不只是属性名称。
也许值得一探究竟,但这意味着您可以使用“点符号”浏览对象图并获取对象上的属性(在对象上...)。
因此DataContext.Foo或Tag。无论如何(由于tag是控件的另一个依赖项属性)。
让我们继续讨论其他一些复杂问题。
datcontext是从可视树继承的,但是这里有一些陷阱。以来 有些东西看起来像是控件,但不是(例如datagridtextcolumn)。模板化的东西可能很棘手。项目控件是一种明显且相关的特殊情况。
对于项控件,每行中任何内容的数据上下文都是从项源呈现给它的任何项。通常,您将rowviewmodel的可观察集合绑定到该itemsource。因此,(显然)列表框或数据网格会向您显示在每一行中提供的每个rowviewmodel中的数据。
如果您随后想获取该Rowview模型中没有的属性,则需要告诉它在其他地方查找。