WPF 用户控件中的数据绑定

Posted

技术标签:

【中文标题】WPF 用户控件中的数据绑定【英文标题】:Data Binding in WPF User Controls 【发布时间】:2022-01-05 22:42:54 【问题描述】:

我正在为多个窗口共享的一系列控件创建一个 UserControl。其中一个控件是一个标签,它以“协议号”的形式显示其他一些流程的流程。

我正在尝试使用此标签提供 DataBinding,以便窗口在协议编号变量更改时自动反映进程的状态。

这是用户控件 XAML:

<UserControl Name="MainOptionsPanel"
    x:Class="ExperienceMainControls.MainControls"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="Binding RelativeSource=RelativeSource Self"
    >
<Label Height="Auto" Name="numberLabel">Protocol:</Label>
<Label Content="Binding Path=ProtocolNumber" Name="protocolNumberLabel"/>
(...)
</UserControl>

这是代码隐藏:

public partial class MainControls 

    public MainControls()
    
        InitializeComponent();
    

    public int ProtocolNumber
    
        get  return (int)GetValue(ProtocolNumberProperty); 
        set  SetValue(ProtocolNumberProperty, value); 
    

    public static DependencyProperty ProtocolNumberProperty = 
       DependencyProperty.Register("ProtocolNumber", typeof(int), typeof(MainControls));

这似乎有效,因为如果我在构造函数中将 ProtocolNumber 设置为任意值,它会反映在用户控件中。

但是,当在最终窗口上使用此用户控件时,数据绑定会中断。​​

XAML:

<Window x:Class="UserControlTesting.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:expControl="clr-namespace:ExperienceMainControls;assembly=ExperienceMainControls"
    DataContext="Binding RelativeSource=RelativeSource Self"
    >
    <StackPanel>
        <expControl:MainControls ProtocolNumber="Binding Path=Number, Mode=TwoWay" />
    </StackPanel>

</Window>

窗口的代码隐藏:

public partial class Window1 : Window

    public Window1()
    
        Number= 15;
        InitializeComponent();
    

    public int Number  get; set; 

这会将协议编号设置为零,忽略设置为编号的值。

我读过例子

【问题讨论】:

在您的输出窗口中,您将看到一个绑定错误,例如对象 MainOptionsPanel 没有属性编号 - 这是真的。只需将您的用户控件 xaml 更改为我的答案。 【参考方案1】:

如果您查看输出窗口,您应该会看到绑定异常。

您遇到的问题如下:在您的用户控件中,您会将标签绑定到您的用户控件的 DP ProtocolNumber 而不是DataContext,因此您必须将元素名称添加到绑定中。

<UserControl Name="MainOptionsPanel"
    x:Class="ExperienceMainControls.MainControls"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="uc"
    >
<Label Height="Auto" Name="numberLabel">Protocol:</Label>
<Label Content="Binding Path=ProtocolNumber, ElementName=uc" Name="protocolNumberLabel"/>
(...)
</UserControl>

编辑:为了清除一些事情,如果您更改 MainWindow 中的绑定,您的用户控件也可以工作。但是您必须使用 RelativeSource 绑定到 MainWindow 的 DataContext。

    <expControl:MainControls ProtocolNumber="Binding Path=Number, RelativeSource=RelativeSource AncestorType=x:Type Window" />

【讨论】:

【参考方案2】:

如果您没有指定绑定的RelativeSource,请尝试在构造函数中设置DataContext

    public Window1()
    
        Number= 15;
        DataContext = this;
        InitializeComponent();
    

【讨论】:

说实话,这种方法很糟糕,因为您可以从外部更改数据上下文。【参考方案3】:

你有什么效果:

<expControl:MainControls DataContext="Binding RelativeSource=RelativeSource Self"
                         ProtocolNumber="Binding Path=Number, Mode=TwoWay"/>

=> 不要不要UserControl 声明中设置DataContext,而是使用RelativeSourceElementName 绑定。

【讨论】:

为什么不设置DataContext?我知道它可以通过多种方式传播到子控件(我认为),但到目前为止,我们的应用程序还没有遇到任何问题。 @mizipzor:这是不好的做法,像这样设置 DataContext 是“从外部”不可见的,并且不切实际,因为 DataContext 的继承通常是您想要和期望的。 实际上,DataContext 的继承(到目前为止)不是我想要的。 CustomControls 是另一回事。但对于 UserControls,我认为它是一种很好的做法。 那么您的意思是从 Usercontrol 定义中删除 DataContext 行?因为在那里我也将它定义为RelativeSource。 @kelmer:是的。它不是同一个对象,您在定义中所做的任何事情都适用于您的实例,在设置 DataContext 的情况下,正如您刚刚展示的那样,这是一个坏主意,因为它会覆盖继承的 DataContext。

以上是关于WPF 用户控件中的数据绑定的主要内容,如果未能解决你的问题,请参考以下文章

WPF 用户控件将数据绑定到用户控件属性

wpf 自定义控件的自定义属性的数据绑定问题

WPF入门教程系列十五——WPF中的数据绑定

wpf 控件属性通过数据绑定到某个集合的某一个数据上。

数据绑定到 WPF 中的 UserControl

WPF 数据验证中的绑定失败