1

我一直试图在 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 时,智能感知下降。所以我必须格外小心才能正确输入所有内容。

那么 - 是否有任何不同的方法可以使 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')
4

2 回答 2

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>

与此完全相同:

<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模式的东西在它们之间进行间接通信。

于 2013-04-08T15:57:38.777 回答
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>
于 2013-04-08T15:58:46.110 回答