绑定到 UserControl DependencyProperty

Posted

技术标签:

【中文标题】绑定到 UserControl DependencyProperty【英文标题】:Binding to UserControl DependencyProperty 【发布时间】:2013-06-03 20:04:16 【问题描述】:

我创建了一个带有一些 DependencyProperties 的 UserControl(在此示例中只有一个字符串属性)。当我实例化 UserControl 时,我可以设置 UserControl 的属性并按预期显示。当我尝试通过 Binding 替换静态文本时,没有显示任何内容。

我的 UserControl 如下所示:

<User Control x:Class="TestUserControBinding.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" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="30" d:DesignWidth="100">
    <Grid>
    <Label Content="Binding MyText"/>
  </Grid>
</UserControl>

背后的代码是:

namespace TestUserControBinding 

  public partial class MyUserControl : UserControl 
    public MyUserControl() 
      InitializeComponent();
      this.DataContext = this;
    

    public static readonly DependencyProperty MyTextProperty = 
                   DependencyProperty.Register(
                         "MyText", 
                          typeof(string), 
                          typeof(MyUserControl));

    public string MyText 
      get 
        return (string)GetValue(MyTextProperty);
      
      set 
        SetValue(MyTextProperty, value);
      
    // MyText

  

当我在 MainWindow 中尝试此操作时,一切都按预期进行:

<Window x:Class="TestUserControBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestUserControBinding"
        Title="MainWindow" Height="350" Width="525">
  <StackPanel>
    <local:MyUserControl MyText="Hello World!"/>
  </StackPanel>
</Window>

但这不起作用:

<Window x:Class="TestUserControBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestUserControBinding"
        Title="MainWindow" Height="350" Width="525">
  <StackPanel>
    <local:MyUserControl MyText="Binding Path=Text"/>
    <Label Content="Binding Path=Text"/>
  </StackPanel>
</Window>

标签的行为是正确的,所以属性“文本”没有问题

我的错误是什么?我思考了好几个小时,但找不到我忘记的任何东西。

【问题讨论】:

【参考方案1】:

在您的UserControl 中使用以下绑定:

<Label Content="Binding MyText"/>

我不确定如何将文本直接设置为 MyText 属性。您必须在 UserControl 上设置 DataContext 才能使其正常工作。

无论如何,此绑定是问题所在 - 据我了解您的情况,您不想绑定到 UserControlDataContext,因为它不一定具有 MyText 属性。您想绑定到 UserControl 本身,特别是您创建的 DependencyProperty。为此,您需要使用RelativeSource 绑定,如下所示:

<Label Content="Binding RelativeSource=RelativeSource AncestorType=x:Type local:MyUserControl, Path=MyText"/>

这会将可视化树向上导航到 MyUserControl,然后在那里找到 MyText 属性。它不会依赖于DataContext,它会根据您放置UserControl 的位置而改变。

在这种情况下,local 指的是您需要在 UserControl 中定义的命名空间:

<UserControl x:Class="TestUserControBinding.MyUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:TestUserControBinding"
         ...>

此时您的第二个示例应该可以工作。

【讨论】:

@Brian... 代码“this.DataContext = this;”应该注意将 DataContext 设置为本地。不应该吗? 哦,是的,我错过了。但是,如果您要创建UserControl,我认为手动设置DataContext 不是一个好主意。 DataContext 旨在表示从容器继承或分配的上下文。 RelativeSource 绑定允许您在不中断标准DataContext 继承流程的情况下实现所需的结果(绑定到DependencyProperty)。如果您的UserControl 的使用者设置了自己的DataContext,那么尝试覆盖DataContext 将会失败。 这正是问题所在。我现在在 MyUserControl 的开始标记中使用 x:Name="MyName" 而不是将 MyUserControl 的 DataContext 设置为自身,并且绑定更改为:&lt;Label Content="Binding ElementName=MyName, Path=MyText"/&gt;我认为您的解决方案也应该可以工作,但它有点笨拙。 是的,使用ElementName 绑定将实现相同的结果,并且不那么冗长,特别是如果您确信其他人不会修改您的代码并错误地更改/删除名称。 这个答案与问题完全无关。我想知道为什么这会受到如此多的赞成甚至被接受。【参考方案2】:

DataContexts 的设置方式存在误解。这对你不利...

最终绑定到用户控件上的MyText未绑定到控件的MyText 依赖属性,而是到页面的DataContext,并且没有MyText 属性。

让我解释一下


说明当用户控件放在您的主页上时,它会继承其控件父级的DataContextStackPanel)。如果未设置父级的DataContext,它将向上移动到StackPanel 的父级DataContext(ad Infinium),直到到达页面的DataContext在您的示例中设置并且有效)。

当您在主页面(例如&lt;local:MyUserControl MyText="Binding Path=Text"/&gt;)上绑定时,它会在主页面DataContext 上查找Text 属性并将依赖属性MyText 设置为该值。 这是你所期望的,它有效!

当前状态 所以代码中用户控件的状态是这样的,它的DataContext 绑定到页面的DataContext 并且设置了MyText 依赖属性。 但是内部控制绑定到MyText 失败。 为什么?

用户控件具有父级的数据上下文,并且您要求控件绑定到 那个 数据上下文上的MyText 属性。没有这样的属性,它失败了。


分辨率

要绑定到控件的instance并从MyText属性中获取值,只需在控件上放一个名称(元素名称),例如

<User Control x:Class="TestUserControBinding.MyUserControl"
             ...
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             x:Name="ucMyUserControl"

然后正确地将绑定从默认的DataContext 转移到名为ucMyUserControlelementnamed 命名实例。如:

  <Label Content="Binding MyText, ElementName=ucMyUserControl "/>

请注意,在您命名控件后,VS2017/2019 实际上会智能感知 ElementName


仅使用父母数据上下文的副作用

没有提及解决方案的原始情况的副作用是,您可以将用户控件的绑定绑定到Text,它会起作用,因为绑定默认为页面的数据上下文。 微妙...

<User Control x:Class="TestUserControBinding.MyUserControl"
             mc:Ignorable="d" 
             d:DesignHeight="30" d:DesignWidth="100">
 <Grid>
    <Label Content="Binding Text"/>

这可行,从技术上讲,您可以删除依赖属性。如果控件没有在项目之外使用,它也可以设计为绑定到其他命名属性而不会产生不良影响。

然后所有用户控件都可以成为主页的事实上的子控件,就像您刚刚将内部 XAML 粘贴到页面上。

【讨论】:

您混淆了控件及其数据上下文。你应该改写你的帖子来解决这个困惑。

以上是关于绑定到 UserControl DependencyProperty的主要内容,如果未能解决你的问题,请参考以下文章

数据绑定到 WPF 中的 UserControl

绑定到 UserControl 中声明的 Storyboard 的属性

绑定到包含 ItemsControl 数据的 UserControl

WPF - 将 UserControl 可见性绑定到属性

UserControl:如何转换依赖属性并绑定到结果

将 Silverlight UserControl 自定义属性绑定到其元素