WPF UserControl中将内容绑定到属性的不同方式有哪些优缺点?

3
当开始使用WPF UserControls时,我发现有几种方法可以将UserControl的内容绑定到其属性之一。
以下是我的控件示例C#代码:
 public sealed partial class MyUserControl : UserControl
 {
    public MyUserControl()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty TheTextProperty =
        DependencyProperty.Register("TheText",
                                    typeof (string),
                                    typeof(MyUserControl),
                                    new FrameworkPropertyMetadata(0, 
                                        FrameworkPropertyMetadataOptions.
                                             BindsTwoWayByDefault)
            );

    public string TheText
    {
        get { return (string)GetValue(TheTextProperty); }
        set { SetValue(TheTextProperty, value); }
    }
}

以下是我发现的将内容绑定到此属性的不同方法:

使用RelativeSource/AncestorType进行绑定的内容

<UserControl x:Class="MyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
    <StackPanel>
        <TextBox Text="{Binding TheText,
                        RelativeSource={RelativeSource
                                        AncestorType={x:Type UserControl}}}" />
    </StackPanel>
</UserControl>

在XAML中,可视树的根DataContext被设置为UserControl。
<UserControl x:Class="MyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
    <StackPanel DataContext="{Binding
                              RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
        <TextBox Text="{Binding TheText}" />
    </StackPanel>
</UserControl>

构造函数中将可视树根的DataContext设置为UserControl
<UserControl x:Class="MyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
    <StackPanel x:Name="VisualTreeRoot">
        <TextBox Text="{Binding TheText}" />
    </StackPanel>
</UserControl>

这是构造函数:
    public MyUserControl()
    {
        InitializeComponent();
        VisualTreeRoot.DataContext = this;
    }

最后但并非最不重要:针对WPF中新手编程UserControls的警告

第一次想要将UserControl的内容绑定到其属性之一时,我认为“嘿,让我们直接将UserControl的DataContext设置为自身”:

<UserControl x:Class="MyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             DataContext="{Binding RelativeSource={RelativeSource Self}}">

或者:

    public MyUserControl()
    {
        InitializeComponent();
        this.DataContext = this;
    }

然而,如果UserControl的用户想要将其属性绑定到其他绑定源,则此方法无法实现。该UserControl需要从其父级继承DataContext才能使其正常工作。通过上述所示的重写,绑定将无法找到其源。
我的最后几个问题:
  • 每种方法的优缺点是什么?
  • 应该在什么情况下使用哪种方法?
  • 还有更多的方法吗?
2个回答

2

我在绑定用户控件的自有依赖属性时使用元素绑定。

<UserControl x:Class="MyUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         x:Name="uc">
   <StackPanel>
    <TextBox Text="{Binding ElementName=uc,Path=TheText}" />
</StackPanel>
</UserControl>

好的,感谢指出这个方法。我忘记在上面的问题中包括它了。这种方法有什么优点/缺点? - Hauke P.
我看不到任何缺点 :) - blindmeis

2
  • 在第一种情况下,TextBoxDataContext未设置为其任何父级之一。因此,您需要直接告诉TextBox在VisualTree中哪个控件具有该属性。
  • 第二种情况是StackPanel上设置了DataContextTextBox相应地继承了它。如果StackPanel中有多个控件,则这比方法一更好。
  • UserControl本身上设置DataContext并不总是错误的(通过构造函数或xaml)。我之所以这样说,是因为如果您有20个控件,其中15个需要使用其当前UserControl类中定义的属性,而5个需要使用UserControl的父级的DataContext,那么您始终可以在少数控件上使用RelativeSource FindAncestor绑定。

我能想到的仅能显示我提到的第3点的“方法”是:

<!--  Can change type to another userControl too and specify Ancestorlevel  -->
<TextBlock Text="{Binding TheText, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />

即使TextBlock的父级UserControl自己作为其DataContext,这也可以正常工作。

至于何时使用什么。

这只是一个逻辑选择,如果您有多个需要相同DataContext的兄弟节点,则将DataContext设置为它们的父级是正确的答案。我总是倾向于在可能的最高元素上设置DataContext,如果有任何一个或两个项目需要变化,则相应地绑定它们。

如果在MVVM中,您的VM几乎总是成为视图的顶层项的DataContext。其他所有内容直接绑定到它们需要的属性的元素。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接