【问题标题】:UserControl's DependencyProperty is null when UserControl has a DataContext当 UserControl 有 DataContext 时,UserControl 的 DependencyProperty 为 null
【发布时间】: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 是因为您正在尝试实现反模式。停下来。这是我的答案,其中包含更多详细信息stackoverflow.com/a/25796096/1228
  • ... ... 什么? UserControl 不应该有它公开的命令,它应该有一个 ICommand 类型的公共 DP,它使用它来允许其他 VM 将 它们的 命令绑定到它。您不应该有 VMFactoryUserControl 和 VMFactoryViewModel。那就是那里的代码气味。所以我不清楚你在这里做什么,我猜。
  • 如果您开始遵循实际的 MVVM 模式,而不是创建一个看起来相似但实际上使事情更复杂的反模式,那么绑定将开始更好地工作。
  • 我不确定我是否同意这个@Will。复合 UI 是使用视图模型和用户控件构建的。 Microsoft 的 Patterns & Practices 团队使用用户控件和视图模型为 WPF 和 Windows Store 应用程序提供 Prism 以实现模块化。您的“文本框没有视图模型”不是一个公平的论点。文本框是控件,而不是用户控件。有区别。
  • @我已经做到了。使用 prism 和用户控件构建了两个企业应用程序。它们运行良好,维护它们没有任何问题。

标签: c# wpf xaml mvvm


【解决方案1】:

这是一个相当常见的绑定“陷阱”...

为了访问VMFactory,您需要使用...将您的UserControl 绑定到自身。

DataContext="{Binding RelativeSource={RelativeSource Self}}"

然后,您不会将 GenericView 项目上的 DataContext 绑定到其他任何地方。

但是,如果您打算将其他值绑定到UserControl 外部的VMFactory(即&lt;vw:GenericView VMFactory={Binding ...}"/&gt;),则应将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}}}" />
    

    请注意,只要设置了DataContextDependencyProperty 属性,这两个Bindings 就可以在同一个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。每个的语法都与您的示例相同?
    猜你喜欢
    • 2011-06-29
    • 2016-04-21
    • 2012-03-02
    • 2012-06-06
    • 2011-07-01
    • 2018-03-12
    • 1970-01-01
    • 2013-06-03
    • 1970-01-01
    相关资源
    最近更新 更多