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

时间:2013-04-08 15:52:41

标签: c# wpf mvvm binding dependency-properties

我一直试图在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");
        }
    }
}

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

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

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

Path=DataContext.DemoTextAlpha

当我尝试在DataContext中向下钻取时,intellisense掉线。所以我必须格外小心地输入一切。

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

感谢。

编辑以澄清

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

<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')

2 个答案:

答案 0 :(得分:5)

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版本,但我正在使用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}">

修改

好的..让我们分析你在这里做的事情:

1 - 将UserControl绑定到ParentVM:

ParentVM -> UserControl

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

    ParentVM -> UserControl -> Child Control

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

    ParentVM -> UserControl -> Child Control -> ChildVM

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

    ParentVM -> ChildVM

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

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

答案 1 :(得分:1)

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>