【问题标题】:How do I reference a custom property value in a WPF UserControl via Binding?如何通过绑定引用 WPF UserControl 中的自定义属性值?
【发布时间】:2020-06-17 09:31:08
【问题描述】:

我正在使用自定义 UserControls 构建一个 WPF 应用程序,并且我试图了解属性绑定应该如何工作。我什至无法让最基本的绑定工作,而且它很简单,可以提炼成一个小例子,所以我认为有更多 WPF 经验的人可能能够让我走上正轨。

我定义了一个名为 TestControl 的自定义 UserControl,它公开了一个 Foo 属性,该属性旨在在放置 UserControl 时在 XAML 中设置。

TestControl.xaml.cs

using System.Windows;
using System.Windows.Controls;

namespace BindingTest
{
    public partial class TestControl : UserControl
    {
        public static readonly DependencyProperty FooProperty = DependencyProperty.Register("Foo", typeof(string), typeof(TestControl));
        public string Foo
        {
            get { return (string)GetValue(FooProperty); }
            set { SetValue(FooProperty, value); }
        }

        public TestControl()
        {
            InitializeComponent();
        }
    }
}

TestControl 的标记只是将它定义为一个带有单个按钮的控件,其标签文本显示 Foo 属性的当前值:

TestControl.xaml

<UserControl x:Class="BindingTest.TestControl"
             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" 
             xmlns:local="clr-namespace:BindingTest"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Button Content="{Binding Foo}" />
    </Grid>
</UserControl>

在我的 MainWindow 类中,我只需放置一个 TestControl 实例,并将其 Foo 属性设置为“Hello”。

MainWindow.xaml

<Window x:Class="BindingTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BindingTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <local:TestControl Foo="Hello" />
    </Grid>
</Window>

我希望当我构建和启动这个应用程序时,我会看到一个带有单个按钮的窗口,上面写着“你好”。但是,按钮是空白的:绑定似乎不起作用。

如果我向 TestControl 的按钮添加点击处理程序,我可以验证值是否在后台更新:

// Added to TestControl.xaml.cs:
private void Button_Click(object sender, RoutedEventArgs e)
{
    Console.WriteLine("Button clicked; Foo is '{0}'", Foo);
}

// Updated in TestControl.xaml:
// <Button Content="{Binding Foo}" Click="Button_Click" />

当我点击按钮时,我得到Button clicked; Foo is 'Hello',但 GUI 永远不会更新。我尝试过使用Path=FooXPath=Foo 等,以及设置UpdateSourceTrigger=PropertyChanged 并使用NotifyOnTargetUpdated=True 验证更新...似乎没有任何结果导致UI 中的文本被更新以匹配底层属性值,即使属性值似乎更新得很好。

我做错了什么?我觉得我的处理方式只是一个简单而根本的误解。

编辑:

四处逛逛并阅读similar questions 使我找到了一个潜在的解决方法:即,向 TestControl.xaml (x:Name="control") 中的根 UserControl 元素添加一个名称,并更改绑定以明确指定该控件({Binding Foo, ElementName=control})。

我猜默认情况下,Button 元素上的 {Binding Foo} 仅表示 “在此 Button 控件上找到一个名为 'Foo' 的属性”,而我认为它的意思是"在声明此 Button 的上下文中,即在 TestControl 上找到名为 'Foo' 的属性"

在这里指定一个明确的 ElementName 是最好的解决方法吗?

【问题讨论】:

    标签: c# wpf xaml binding user-controls


    【解决方案1】:

    您必须将 Binding 的源对象设置为 UserControl 实例,例如像这样:

    <Button Content="{Binding Foo, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
    

    <UserControl ... x:Name="theControl">
    ...
    <Button Content="{Binding Foo, ElementName=theControl}"/>
    

    如果你有很多这样的 Bindings,你也可以将 UserControl 的 XAML 中顶层元素的 DataContext 设置为 UserControl 实例:

    <Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
        <Button Content="{Binding Foo}" />
        <Button Content="{Binding Bar}" />
    </Grid>
    

    但是,您必须避免设置 UserControl 的 DataContext(“专家”博主通常建议这样做),因为这会破坏 UserControl 属性的基于 DataContext 的绑定,例如

    <local:TestControl Foo="{Binding SomeFoo}" />
    

    【讨论】:

    • 漂亮!覆盖整个控件的 DataContext 是我希望可行的方法。而且,一般来说,具有“DataContext”的绑定的概念是我所缺少的一块拼图。感谢您提供有关 UserControl/DataContext 陷阱的修复、词汇和提示。
    • 为整个控件覆盖 DataContext 正是您应该避免的!见上面的解释。
    • 啊,我知道我的措辞会产生误导。我只是指您的第二个示例,即在根控件下方的顶级元素(即本示例中的 Grid)上设置 DataContext。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 2015-04-18
    • 1970-01-01
    • 2017-08-21
    相关资源
    最近更新 更多