当 UserControl 有 DataContext 时,UserControl 的 DependencyProperty 为 null
Posted
技术标签:
【中文标题】当 UserControl 有 DataContext 时,UserControl 的 DependencyProperty 为 null【英文标题】:UserControl's DependencyProperty is null when UserControl has a DataContext 【发布时间】:2015-02-16 01:42:56 【问题描述】:我已将 DependencyProperty 添加到我的视图中,绑定到 DependencyProperty 有效,但前提是我没有同时设置 DataContext。
GenericView.xaml
<UserControl x:Class="GenericProject.View.GenericView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Button Command="Binding VMFactory.CreateViewModelCommand, RelativeSource=RelativeSource AncestorType=x:Type UserControl" />
<TextBox IsEnabled="False" Text="Binding SomeProperty, Mode=OneWay" />
</StackPanel>
</UserControl>
GenericView.xaml.cs
public partial class GenericView : UserControl
// The DependencyProperty for VMFactory.
public static readonly DependencyProperty VMFactoryProperty = DependencyProperty.Register("VMFactory", typeof(VMFactoryViewModel<GenericViewModel>), typeof(GenericView));
public VMFactoryViewModel<GenericViewModel> VMFactory
get return (VMFactoryViewModel<GenericViewModel>)GetValue(VMFactoryProperty);
set SetValue(VMFactoryProperty, value);
public GenericView()
InitializeComponent();
在这里,我创建了两个视图来说明手头的问题。第一个视图中的 VMFactory 绑定将失败,因为我设置了 DataContext。第二个视图会成功,这个行为的原因是什么?
MainPage.xaml
<vw:GenericView DataContext="Binding Generic" VMFactory="Binding GenericFactory" />
<vw:GenericView VMFactory="Binding GenericFactory" />
【问题讨论】:
(如果它不适用,请忽略这个粗俗的评论!)您没有创建 ViewModel 来封装您的 UserControls 的逻辑,是吗?因为没有。您不会为您的 UserControls 创建 ViewModel。 UserControls 应该只包含 UI 逻辑,并且它们应该像任何其他控件一样设计。 TextBox 有 TextBoxViewModel 吗? 否。 您遇到的 PITA 是因为您正在尝试实现反模式。停下来。这是我的答案,其中包含更多详细信息***.com/a/25796096/1228 ... ... 什么? UserControl 不应该有它公开的命令,它应该有一个 ICommand 类型的公共 DP,它使用它来允许其他 VM 将 它们的 命令绑定到它。您不应该有 VMFactoryUserControl 和 VMFactoryViewModel。那就是那里的代码气味。所以我不清楚你在这里做什么,我猜。 如果您开始遵循实际的 MVVM 模式,而不是创建一个看起来相似但实际上使事情更复杂的反模式,那么绑定将开始更好地工作。 我不确定我是否同意这个@Will。复合 UI 是使用视图模型和用户控件构建的。 Microsoft 的 Patterns & Practices 团队使用用户控件和视图模型为 WPF 和 Windows Store 应用程序提供 Prism 以实现模块化。您的“文本框没有视图模型”不是一个公平的论点。文本框是控件,而不是用户控件。有区别。 @我已经做到了。使用 prism 和用户控件构建了两个企业应用程序。它们运行良好,维护它们没有任何问题。 【参考方案1】:这是一个相当常见的绑定“陷阱”...
为了访问VMFactory
,您需要使用...将您的UserControl
绑定到自身。
DataContext="Binding RelativeSource=RelativeSource Self"
然后,您不会将 GenericView
项目上的 DataContext
绑定到其他任何地方。
但是,如果您打算将其他值绑定到UserControl
外部的VMFactory
(即<vw:GenericView VMFactory=Binding ..."/>
),则应将RelativeSource
与模式FindAncestor
一起用于UserControl
类型。
<!-- Shortened to show pertinent Binding -->
<ctrl:CommandTextBox Command="Binding VMFactory.CreateViewModelCommand, RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type UserControl"/>
【讨论】:
您的第二个示例看起来像我最初的尝试,但对我不起作用,我再次尝试并了解到它确实有效,但前提是我没有同时设置 DataContext 和 VMFactory 属性用户控件。想法? DataContext 有意义,但不是 VMFactory。你试过只设置 VMFactory 吗? 这就是我试图传达的,如果我只设置 VMFactory 就可以了。如果我还设置了 DataContext 则命令被破坏。 您需要记住 UserControl 级别的 DataContext 是继承的,因此一旦绑定它,您可以将 Command 绑定视为Binding UserControl.DataContext.VMFactory.CreateViewModelCommand...
。为什么要绑定 VMFactory 和 DataContext?
我以为我理解继承?我的印象是,RelativeSource、ElementName 等允许我将我的命令设置为 Binding UserControl.VMFactory.CreateViewModelCommand... 并且仍然将 Binding UserControl.DataContext.SomeProperty 用于文本框和其他项目。【参考方案2】:
我有一个可行的解决方案,似乎控件的属性是相对于控件的 DataContext 绑定的?
我当然知道控件中的项目将相对于 DataContext 绑定,但我显然以前从未以这种方式使用过控件,并且不明白控件的属性也会继承设置的 DataContext 的范围。基本上我的视图中的所有内容都是正确的,但是绑定到我的 DependencyProperty 失败了。
GenericView.xaml
<!-- Everything in here was correct. -->
<UserControl x:Class="GenericProject.View.GenericView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Button Command="Binding VMFactory.CreateViewModelCommand, RelativeSource=RelativeSource AncestorType=x:Type UserControl" />
<TextBox IsEnabled="False" Text="Binding SomeProperty, Mode=OneWay" />
</StackPanel>
</UserControl>
MainPage.xaml
<!-- This is where I messed up earlier, VMFactory requires a relative binding. -->
<vw:GenericView DataContext="Binding Generic"
VMFactory="Binding DataContext.GenericFactory, RelativeSource=RelativeSource AncestorType=x:Type Page" />
【讨论】:
【参考方案3】:正如@toadflakz 所说,这是 WPF 中一个非常常见的问题,当我学习 WPF 时,我花了一段时间才弄清楚。幸运的是,解决方案很简单。假设我们有一个 UserControl
有一个对象集作为它的 DataContext
和另一个集作为在 UserControl
中声明的 DependencyProperty
的值......你的情况。
从在UserControl
XAML 中,您可以像往常一样将数据绑定到设置为DataContext
的对象的属性:
<TextBlock Text="Binding PropertyFromDataContextObject" />
如果你想将数据绑定到来自对象集的对象为DependencyProperty
的值,你可以简单地使用RelativeSource Binding
:
<TextBlock Text="Binding PropertyFromDependencyPropertyObject, RelativeSource=
RelativeSource AncestorType=x:Type YourPrefix:YourUserControl" />
请注意,只要设置了DataContext
和DependencyProperty
属性,这两个Binding
s 就可以在同一个UserControl
中一起使用。
【讨论】:
我了解绑定,但它没有按建议工作?如果设置了 DataContext,DependencyProperty(VMFactory) 似乎为空?如果您查看我的 MainPage.xaml,您会看到我已经创建了两个 GenericView 实例以进行故障排除,如果我将 Loaded 事件处理程序添加到 UserControl,我可以看到具有 DataContext 绑定的实例将具有空 VMFactory 并且不会运行正如预期的那样。而没有 DataContext 的实例将具有 VMFactory 对象,并且命令将按预期执行。想法? 如果您只是按照我的示例进行操作,它应该可以工作......您显然已经过度复杂了您的代码。从DataContext
对象访问属性时,不应使用任何RelativeSource Binding
,因为这会将数据源更改为远离DataContext
。在一个新项目中进行实验。此外,您应该在 Visual Studio 的输出窗口中出现 Binding
错误,告诉您问题所在。
我没有使用RelativeSource 来访问DataContext? TextBox 绑定访问 DataContext,Command 绑定访问 VMFactory。每个的语法都与您的示例相同?以上是关于当 UserControl 有 DataContext 时,UserControl 的 DependencyProperty 为 null的主要内容,如果未能解决你的问题,请参考以下文章