【问题标题】:Binding Dependency Property to Current DataContext Property将依赖属性绑定到当前 DataContext 属性
【发布时间】:2013-03-30 19:15:57
【问题描述】:

我一直在尝试在 WPF 中设置这个障碍,我想我已经找到了一个解决方案,尽管是一个丑陋的解决方案。

场景如下:

  • 我有一个带有自定义依赖属性的自定义用户控件。
  • 用户控件可以嵌套在我的其他用户控件中。
  • 我的每个用户控件都有一个由定位器指定的数据上下文(我遵循 MVVM 模式)
  • 我想将自定义依赖属性绑定到父视图模型中的一个值。

代码...

父视图

<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">

    <my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}}" />

</UserControl>

父类视图模型

public class ParentClassViewModel : BaseViewModel
{
    private string _demoTextAlpha = "Some Alpha text";

    public string DemoTextAlpha
    {
        get
        {
            return this._demoTextAlpha;
        }
        set
        {
            this._demoTextAlpha = value;
            this.NotifyPropertyChange("DemoTextAlpha");
        }
    }
}

子视图

<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ChildControlLocator}">

    <TextBlock Text="{Binding Path=SomeProperty}" />

</UserControl>

子查看代码背后

public partial class Child : UserControl
{
    public Child()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty DemoProperty =
           DependencyProperty.Register("Demo", 
                                       typeof(string), 
                                       typeof(Child),
                                       new FrameworkPropertyMetadata()
                                       {
                                           PropertyChangedCallback = OnDemoChanged,
                                           BindsTwoWayByDefault = true
                                       });

    public string Demo
    {
        get { return this.GetValue(DemoProperty).ToString(); }
        set { this.SetValue(DemoProperty, value); }
    }

    private static void OnDemoChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = (Child)d;
        var viewModel = (ChildViewModel)control.DataContext;

        viewModel.SomeProperty = (string)e.NewValue;
    }
}

子视图模型

public class ChildViewModel : BaseViewModel
{
    private string _someProperty;

    public string SomeProperty
    {
        get 
        { 
            return _someProperty; 
        }
        set
        {
            _someProperty = value;
            this.NotifyPropertyChange("SomeProperty");
        }
    }
}

好的,所以这有效。我想要实现的是更好/更优雅的代码,尤其是关于这个声明。

<my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}}" />

即使我可以忍受,就优雅而言,但现在困扰我的一件事是当我打字时

Path=DataContext.DemoTextAlpha

当我尝试深入 DataContext 时,智能感知下降。所以我必须格外小心才能正确输入所有内容。

那么 - 是否有任何不同的方法可以让 DataContext 的属性出现在智能感知中,或者是否有其他方法可以实现我现在正在做的同样事情?

谢谢。

编辑澄清

当我放这样的东西而不是像上面的例子中指定相对源时......

<my:Child Demo="{Binding DemoTextAlpha}"/>

我收到一个错误...

System.Windows.Data Error: 40 : BindingExpression path error: 'DemoTextAlpha' property not found on 'object' ''ChildViewModel' (HashCode=34126977)'. BindingExpression:Path=DemoTextAlpha; DataItem='ChildViewModel' (HashCode=34126977); target element is 'Child' (Name=''); target property is 'Demo' (type 'String')

【问题讨论】:

    标签: c# wpf mvvm binding dependency-properties


    【解决方案1】:

    DataContext(以及许多其他属性,例如FontSize)是视觉树中的"Inherited"。因此:

    <UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">
        <my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}}" />
    </UserControl>
    

    和这个完全一样:

    <UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">
        <my:Child Demo="{Binding DemoTextAlpha}"/>
    </UserControl>
    

    关于 Intellisense 支持,我不知道您使用的是哪个 VS 版本,但我使用的是带有 ReSharper 6.1 的 VS 2010 Pro,如果您指定 d:DataContext 值,它会添加 Intellisense 支持:

    <UserControl x:Class="...etc."
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="clr-namespace:TheViewModelNamespace"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 mc:Ignorable="d" d:DataContext="{d:DesignInstance local:ViewModel}">
    

    编辑:

    好吧..让我们分析一下你在这里做什么:

    1 - 将 UserControl 绑定到 ParentVM:

    ParentVM -> UserControl
    

    2 - 使用 RelativeSource 从 ParentVM 获取一些属性并将其放入您在 Child 控件中创建的自定义 DP 中

        ParentVM -> UserControl -> Child Control
    

    3 - 在自定义 DP 的 OnPropertyChanged 中,为 ChildVM 设置相同的值

        ParentVM -> UserControl -> Child Control -> ChildVM
    

    您是否意识到您正在使用视图(用户控件、子控件)作为在 2 个视图模型之间共享某些属性的中间体?你为什么不干脆

        ParentVM -> ChildVM
    

    哪个更容易、更干净、更真正的 MVVM?

    要么将 ParentVM 的引用直接指向 ChildVM,要么使用类似Messenger 的模式在它们之间进行间接通信。

    【讨论】:

    • 请检查我对 OP 的编辑以查看我的说明...我试图将其放在评论中,但无法正确格式化。
    • 该死!我遗漏的另一件事。你所说的是我一直在做的事情。我在构造函数中引用了其他虚拟机,当我需要填充数据时,我就是这样做的。但是,当我需要将 2x 的 ChildControl 放在同一个父级中时,我遇到了这个问题。这有意义吗?
    • +1,因为你提醒我绝对不需要将依赖属性绑定到 DataContext。您刚刚为我节省了很多工作和可维护性问题。
    【解决方案2】:

    DataContext 被继承:

    <UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">
    
        <my:Child Demo="{Binding DemoTextAlpha}" />
    
    </UserControl>
    

    如果在不同的场景中,您的子控件指定了不同的 DataContext,并且您仍需要绑定到父控件的 DataContext 的属性,则使用 ElementName 可能更好:

    <UserControl x:Name="Parent" DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">
    
        <my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, ElementName=Parent}" />
    
    </UserControl>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-27
      • 2014-09-22
      • 2016-01-22
      • 1970-01-01
      • 2011-03-28
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      相关资源
      最近更新 更多