通过 MVVM 模式创建 UserControl - DataContext 并绑定到父级
Posted
技术标签:
【中文标题】通过 MVVM 模式创建 UserControl - DataContext 并绑定到父级【英文标题】:Creating UserControl via MVVM pattern - DataContext and binding to parent 【发布时间】:2012-10-21 00:34:45 【问题描述】:我使用 MVVM 模式,所以我自己的控件包含 View 和 ViewModel。 ViewModel 通过 DataContext 属性与 View 连接。这会导致绑定问题。为什么?
假设这种情况: 我创建了新的用户控件——例如——“SuperTextBox”。它有一个属性“SuperValue”。现在我做了这样的事情:
<Window>
<Window.DataContext>
<vm:WindowViewModel/>
</Window.DataContext>
<local:SuperTextBox SuperValue="Binding Test"/>
</Window>
我认为“绑定过程”将 SuperTextBox.SuperValue 与 Window.DataContext.Test 结合起来,但不是,“绑定过程”将 SuperTextBox.SuperValue 与 SuperTextBox.DataContext.Test 结合起来,这对我来说是不自然和误导的。
其他控件,如“TextBox”,我可以通过上述方式使用,因为它们没有 DataContext。
如何使用 MVVM 模式来创建保持自然绑定的用户控件(到父控件的 DataContext)?
编辑:
我得到了很多关于如何绑定到父级的答案,但我很早就知道这一点。问题是 - 我如何通过 MVVM 模式(具有 ViewModel)创建 UserControl 并保持自然绑定 - 默认为父 DataContext。
我想要 ViewMoldel 并且仍然可以像这样绑定:
<local:SuperTextBox SuperValue="Binding Test"/>
有可能吗?
【问题讨论】:
当您创建一个具有依赖属性的真实用户控件时,就像我发布的那样。但我会编辑我的答案以显示另一种“不自然”的方式:) 【参考方案1】:应用于任何control always first look for the binding in its DataContext
的所有绑定。如果没有为控件设置 DataContext,则它walks up the Visual Tree
直到它的父级,除非它找到 DataContext。
即使您将 textBox 上的 DataContext
设置为与 Window 的 DatContext 不同的某个值,它也会始终在该特定 DataContext 上搜索属性 Test
而不是在您的 Window's DataContext
上。
<TextBox>
<TextBox.DataContext>
<vm:ViewModelForTextBox/>
</TextBox.DataContext>
<TextBox.Text>
<Binding Path="Test"/>
</TextBox.Text>
</TextBox>
现在,xaml 将在 ViewModelForTextBox
类中查找 Test
属性,而不是在 WindowViewModel
类中查找,如果在 ViewModelForTextBox
、binding will fail silently
类中找不到 Test
属性,并且不会查找Window 的 DataContext 类。
如果您仍想为自定义 UserControl 设置 DataContext 但仍想绑定到父级(窗口)的 dataContext,您必须像这样在绑定中使用 RelativeSource MarkupExtension
-
<local:SuperTextBox SuperValue="Binding Path=DataContext.Test,
RelativeSource=RelativeSource Mode=FindAncestor,
AncestorType=x:Type Window">
有关更多说明,请参阅 MSDN 文章 here。
【讨论】:
【参考方案2】:你应该发布你的 SuperTextBox 代码,因为有你的错误。
通常你创建一个具有依赖属性的用户控件 - 在你的例子中是“SuperValue” - 现在最重要的是你没有将你的 SuperTextBox 的数据上下文设置为它自己。
你必须在你的 SuperTextBox 中使用 elementname 绑定来绑定到“SuperValue”
<SuperTextBox x:Name="uc">
<TextBox Text="Binding ElementName=uc, Path=SuperValue/>
</superTextBox>
如果你这样做 - 你的
<local:SuperTextBox SuperValue="Binding Test"/>
应该可以工作并且应该绑定到您的 vm:WindowViewModel 的测试属性。这是编写上述绑定的唯一方法。
编辑:如果你想为你的用户控件创建一个视图模型,可以说 SuperTextViewmodel。那么它将有一个属性“SuperValue”。现在你不能设置 datacontext 两次所以我建议你必须添加一个属性到你的 SuperTextViewmodel 类型的 WindowViewmodel 并按照你想要的方式处理属性。
你的绑定看起来像这样
<local:SuperTextBox DataContext="Binding MySuperTextViewmodelInstanceOnWindowViewmodel"/>
我会回答我的第一部分:) 我总是说视图需要一个视图模型但一个用户控件依赖属性。
【讨论】:
感谢您的回答,但是当我使用 MVVM 模式创建 UserControl 时,我必须拥有(或者我想要拥有)ViewModel。因此,要将 ViewModel 注入 View,我必须使用 DataContext,这会使绑定变得不自然。我的问题是如何避免这种情况 - 我可以以其他方式加入 ViewModel(不会破坏绑定)吗?【参考方案3】:您需要“查找”它的窗口祖先的数据上下文。您的绑定将如下所示:
<local:SuperTextBox SuperValue="Binding Path=DataContext.Test, RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=x:Type Window">
【讨论】:
谢谢,但我知道。 :) 我需要有关通过 MVVM 模式创建 UserControl 的“模式和实践”的信息。我可以使用 MVVM 并且仍然像在“TextBox”控件中那样正常/自然地出价吗? 这没有什么不自然的。您必须对具有来自其 ItemsSource 的单个项目的数据上下文的任何控件元素执行相同的操作。例如,如果您想将 ListViewItem 的可见性绑定到 viewmodel 中的值,但 ListView 设置了 ItemsSource,那么您必须使用这种绑定方法。【参考方案4】:我觉得回答我的问题很奇怪,但是... 在我自己的控制下,我做了类似的事情:
<UserControl>
<Grid>
<Grid.DataContext>
<vm:UserControlViewModel />
</Grid.DataContext>
// here realy code of control
</Grid>
</UserControl>
现在我可以不受控制地使用“自然”绑定。 :)
【讨论】:
以上是关于通过 MVVM 模式创建 UserControl - DataContext 并绑定到父级的主要内容,如果未能解决你的问题,请参考以下文章
如何从作为wpf mvvm模式中的窗口打开的视图模型中关闭用户控件?
关于在MVVM架构下WPF中UserControl的 visibility Binding问题。 UserControl MVVM
WPF MVVM 通过事件将 UserControl 从一个 ObservableCollection 移动到另一个