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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15883637/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 08:31:35  来源:igfitidea点击:

Binding Dependency Property to Current DataContext Property

c#wpfmvvmbindingdependency-properties

提问by imdandman

I keep trying to make this hurdle in WPF, and I think I've found a solution, albeit an ugly one.

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

The scenario is as follows:

场景如下:

  • I have a custom user control with a custom dependency property.
  • The user controls can be nested inside of my other user controls.
  • Each of my user controls has a data context that is specified by a locator (I am following the MVVM pattern)
  • I want to bind the custom dependency property to a value in the parent view model.
  • 我有一个带有自定义依赖项属性的自定义用户控件。
  • 用户控件可以嵌套在我的其他用户控件中。
  • 我的每个用户控件都有一个由定位器指定的数据上下文(我遵循 MVVM 模式)
  • 我想将自定义依赖属性绑定到父视图模型中的值。

Code...

代码...

Parent View

父视图

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

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

</UserControl>

Parent Class View Model

父类视图模型

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

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

Child View

子视图

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

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

</UserControl>

Child View Code Behind

子视图背后的代码

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;
    }
}

Child View Model

子视图模型

public class ChildViewModel : BaseViewModel
{
    private string _someProperty;

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

Ok, so this WORKS. What I'm trying to achieve is better/ more elegant code, particularly as it regards to this statement.

好了,这个作品。我想要实现的是更好/更优雅的代码,尤其是在这句话方面。

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

Even that I could live with, as far as elegance goes, but one thing that is bothering me right now is that when I type

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

Path=DataContext.DemoTextAlpha

The intellisense drops when I try to drill down inside the DataContext. So I have to be extra careful to type everything right.

当我尝试在 DataContext 中向下钻取时,智能感知下降。因此,我必须格外小心地正确输入所有内容。

So - is there any different way to make the properties of the DataContext appear in intellisense, or is there an alternative way to achieve the same thing that I'm doing now?

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

Thanks.

谢谢。

EDIT to Clarify

编辑以澄清

When I put something like this instead of specifying the relative source as in the above examples...

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

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

I receive an error...

我收到一个错误...

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

回答by Federico Berasategui

The DataContext(along with a lot of other properties such as FontSize) is "Inherited"along the visual tree. Therefore this:

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>

Is exactly the same as this:

和这个完全一样:

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


With regards to the Intellisense support, I don't know what VS version you're using, but I'm using VS 2010 Pro with ReSharper 6.1 and it adds Intellisense support if you specify the d:DataContextvalue:

关于 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}">

Edit:

编辑:

Ok.. let's analize what you're doing here:

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

1- Binding the UserControl to the ParentVM:

1- 将 UserControl 绑定到 ParentVM:

ParentVM -> UserControl

2- Using RelativeSource To Grab some property from ParentVM and place it into a Custom DP you created in the Child control

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

    ParentVM -> UserControl -> Child Control

3- In the OnPropertyChanged of the custom DP, setting that same value to the ChildVM

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

    ParentVM -> UserControl -> Child Control -> ChildVM

Do you realize you're using the View (User Control, Child Control) as an intermediate to share some properties between 2 View Models? Why don't you just

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

    ParentVM -> ChildVM

Which would be easier, cleaner and really MVVM?

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

Either put a reference from the ParentVM directly to the ChildVM, or use something like a Messengerpattern to have indirect communication between them.

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

回答by Marc

DataContext is inherited:

DataContext 被继承:

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

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

</UserControl>

If, ina different scenario, your child control has a different DataContext specified and you still need to bind to a property of your parent control's DataContext, using ElementNameis probably nicer:

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

<UserControl x:Name="Parent" DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">

    <my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, ElementName=Parent}" />

</UserControl>