将依赖属性绑定到当前DataContext属性

imd*_*man 1 c# wpf binding dependency-properties mvvm

我一直试图在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>
Run Code Online (Sandbox Code Playgroud)

父类视图模型

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

    public string DemoTextAlpha
    {
        get
        {
            return this._demoTextAlpha;
        }
        set
        {
            this._demoTextAlpha = value;
            this.NotifyPropertyChange("DemoTextAlpha");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

儿童观

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

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

</UserControl>
Run Code Online (Sandbox Code Playgroud)

儿童观点代码背后

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

儿童视图模型

public class ChildViewModel : BaseViewModel
{
    private string _someProperty;

    public string SomeProperty
    {
        get 
        { 
            return _someProperty; 
        }
        set
        {
            _someProperty = value;
            this.NotifyPropertyChange("SomeProperty");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

好了,这WORKS.我想要实现的是更好/更优雅的代码,特别是对于这个陈述.

<my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}}" />
Run Code Online (Sandbox Code Playgroud)

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

Path=DataContext.DemoTextAlpha
Run Code Online (Sandbox Code Playgroud)

当我尝试在DataContext中向下钻取时,intellisense掉线.因此,我必须格外小心地输入正确的一切.

那么 - 是否有任何不同的方法使DataContext的属性出现在intellisense中,或者是否有另一种方法来实现我现在正在做的同样的事情?

谢谢.

编辑澄清

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

<my:Child Demo="{Binding DemoTextAlpha}"/>
Run Code Online (Sandbox Code Playgroud)

我收到一个错误......

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')
Run Code Online (Sandbox Code Playgroud)

Fed*_*gui 5

DataContext(与很多其它性能如沿FontSize)的"继承"沿着视觉树.因此:

<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">
    <my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}}" />
</UserControl>
Run Code Online (Sandbox Code Playgroud)

与此完全相同:

<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">
    <my:Child Demo="{Binding DemoTextAlpha}"/>
</UserControl>
Run Code Online (Sandbox Code Playgroud)

关于Intellisense支持,我不知道你正在使用什么VS版本,但我正在使用VS 2010 Pro和ReSharper 6.1,如果你指定了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}">
Run Code Online (Sandbox Code Playgroud)

编辑:

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

1 - 将UserControl绑定到ParentVM:

ParentVM -> UserControl
Run Code Online (Sandbox Code Playgroud)

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

    ParentVM -> UserControl -> Child Control
Run Code Online (Sandbox Code Playgroud)

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

    ParentVM -> UserControl -> Child Control -> ChildVM
Run Code Online (Sandbox Code Playgroud)

您是否意识到您正在使用View(用户控件,子控件)作为中间件来共享2个View Models之间的某些属性?你为什么不这么做

    ParentVM -> ChildVM
Run Code Online (Sandbox Code Playgroud)

哪个更容易,更干净,真的是MVVM?

将ParentVM中的引用直接放到ChildVM上,或者使用类似于Messenger模式的引用在它们之间进行间接通信.