绑定到UserControl DependencyProperty

Buc*_*ter 12 c# wpf binding user-controls dependency-properties

我创建了一个带有一些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>
Run Code Online (Sandbox Code Playgroud)

守则背后是:

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

  }
}
Run Code Online (Sandbox Code Playgroud)

当我在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>
Run Code Online (Sandbox Code Playgroud)

但这不起作用:

<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>
Run Code Online (Sandbox Code Playgroud)

标签的行为是正确的,因此属性"文本"没有问题

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

Bri*_*n S 15

使用以下绑定UserControl:

<Label Content="{Binding MyText}"/>
Run Code Online (Sandbox Code Playgroud)

我不确定如何直接将文本设置为MyText属性.你必须DataContextUserControl某个地方设置它才能工作.

无论如何,这个绑定是问题 - 因为我理解你的场景,你不想绑定到它DataContext,UserControl因为它不一定有MyText属性.您想要绑定到UserControl自身,特别是DependencyProperty您创建的.为此,您需要使用RelativeSource绑定,如下所示:

<Label Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MyUserControl}}, Path=MyText}"/>
Run Code Online (Sandbox Code Playgroud)

这将向上导航可视树到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"
         ...>
Run Code Online (Sandbox Code Playgroud)

你的第二个例子应该在那一点上起作用.

  • 哦,是的,我错过了.但是如果你正在创建一个`UserControl`,我认为手动设置`DataContext`并不是一个好主意.`DataContext`旨在表示从容器继承或分配的上下文.`RelativeSource`绑定允许您实现所需的结果(绑定到`DependencyProperty`),同时不会中断标准的`DataContext`继承流.如果你的`UserControl`的使用者设置他们自己的`DataContext`,试图覆盖`DataContext`就会失败. (4认同)
  • 这正是问题所在.我没有将MyUserControl的DataContext设置为自身,而是在`MyUserControl`的开始标记中使用`x:Name ="MyName"`并且Binding变为:`<Label Content ="{Binding ElementName = MyName,Path = MyText }"/>`我认为你的解决方案也应该有效,但它有点笨拙. (3认同)

Ωme*_*Man 9

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

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

让我解释


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

当您在主页上绑定时,例如它会在主页 DataContext 上<local:MyUserControl MyText="{Binding Path=Text}"/>查找属性并将依赖属性设置为该值。这就是您所期望的并且它有效!TextMyText

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

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


解决

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

<User Control x:Class="TestUserControBinding.MyUserControl"
             ...
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             x:Name="ucMyUserControl"
Run Code Online (Sandbox Code Playgroud)

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

  <Label Content="{Binding MyText, ElementName=ucMyUserControl }"/>
Run Code Online (Sandbox Code Playgroud)

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


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

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

<User Control x:Class="TestUserControBinding.MyUserControl"
             mc:Ignorable="d" 
             d:DesignHeight="30" d:DesignWidth="100">
 <Grid>
    <Label Content="{Binding Text}"/>
Run Code Online (Sandbox Code Playgroud)

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

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