我确信我们大多数人都会同意从父或自我上下文中用尽声明DataContext
来源。然后我们可以求助于绑定到父Tag
,然后使用元素绑定。因此源可能如下所示:
<Grid.Tag>
<Binding Path="MyProperty" Source="{StaticResource MySource}" />
</Grid.Tag>
当我们甚至不能这样做时会发生什么?我的下一个草率技巧是使用折叠的UserControl
元素:
<UserControl
x:Name="MySloppyControl"
DataContext="{StaticResource YetAnotherSourceInThisCrazyGrid}"
Foreground={Binding CrazyForegroundColor}
Visibility="Collapsed" />
现在我可以这样做:
<Grid.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="{Binding CrazyForegroundColor, ElementName=MySloppyControl}" />
</Style>
</Grid.Resources>
假设我已经充分解释了这个问题,这个崩溃的UserControl
模式是否被误导了?
答案 0 :(得分:3)
并非我自己没有这样做,但我不得不说“是” - 这种模式是错误的。
在我选择这样做的少数情况下,我的代码看起来像这样:
<Control x:Name="Whatever" DataContext="..." />
<Control x:Name="SomethingElse" DataContext="..." />
由于默认情况下控件不可见,因此使用较少的代码可以获得相同的效果。
话虽如此,让我解释为什么我认为这种做法是错误的:
WPF就是绑定数据。如果要绑定一些数据,它可以是模型(或视图模型)的一部分,也可以是外部数据。如果它是模型(或视图模型)的一部分,并且模型的结构定义良好,则应该能够通过当前的DataContext访问它。另一方面,如果它是静态的,您应该能够直接从目标访问它。
例如,假设您要使用模型中的所有可能的WidgetType填充ComboBox。如果您的模型构造良好,则绑定可以简单如下:
<ComboBox ItemsSource="{Binding DataSet.AllWidgetTypes}" />
这假设您的ComboBox的DataContext是“Widget”,并且“Widget”具有“DataSet”属性,可以访问其他相关数据。或者,如果可用类型列表可能会根据Widget的其他详细信息而更改,则绑定可能只是{Binding AppliableWidgetTypes}
。
在您调用它时可能需要“单独的DataContext”的另一种情况是引用静态对象时,例如通过x:Static或StaticResource。在这些情况下,有一种更好的方法:设置Binding.Source
。它看起来像这样:
Text="{Binding DefaultFontSize,Source={x:Static ApplicationProperties.Instance}}"
或者
Text="{Binding PropertyName,Source={StaticResource ResourceContainingProperty}}"
如果你的动机是避免创建一个viewmodel来组合多个模型对象,那么使用C#anonymoust类型构建一个穷人的viewmodel。例如:
DataContext = new { something = 123, whatever = "Test" };
另一种常见模式是在控件上创建属性,并使用ControlTemplate和TemplateBinding绑定它们。
这留下了最后一种情况,即你真正想要一个共享值,而你根本不需要代码隐藏。在这种情况下,我实际上已经使用了前面显示的隐形<Control>
。所以很少有情况可以适用。