简体   繁体   English

如何通过绑定在 WPF UserControl 中引用自定义属性值?

[英]How do I reference a custom property value in a WPF UserControl via Binding?

I'm building a WPF app with custom UserControls, and I'm trying to understand how property bindings are supposed to work.我正在使用自定义 UserControl 构建 WPF 应用程序,并且我正在尝试了解属性绑定应该如何工作。 I can't get even the most basic binding to work, and it's simple enough to distill into a tiny example, so I figured someone with more WPF experience might be able to put me on the right track.我连最基本的绑定都无法工作,而且它很简单,可以提炼成一个小例子,所以我认为有更多 WPF 经验的人可能能让我走上正轨。

I've defined a custom UserControl called TestControl , which exposes a Foo property, which is intended to be set in XAML whenever a UserControl is placed.我定义了一个名为TestControl的自定义 UserControl ,它公开了一个Foo属性,该属性旨在在放置 UserControl 时在 XAML 中设置。

TestControl.xaml.cs测试控件.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();
        }
    }
}

The markup for TestControl just defines it as a control with a single button, whose label text displays the current value of the Foo property: TestControl 的标记只是将其定义为具有单个按钮的控件,其标签文本显示 Foo 属性的当前值:

TestControl.xaml测试控件.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>

In my MainWindow class, I just place a single instance of TestControl with its Foo property set to "Hello".在我的MainWindow类中,我只放置了一个 TestControl 实例,其 Foo 属性设置为“Hello”。

MainWindow.xaml主窗口.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>

I would expect that when I build and launch this app, I'd see a window with a single button reading "Hello".我希望当我构建和启动这个应用程序时,我会看到一个带有一个按钮的窗口,上面写着“你好”。 However, the button is blank: the Binding doesn't seem to work.但是,按钮是空白的:绑定似乎不起作用。

If I add a click handler to the TestControl's button, I can verify that the value is being updated behind the scenes:如果我向 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" />

When I click the button, I get Button clicked; Foo is 'Hello'当我点击按钮时,我Button clicked; Foo is 'Hello' Button clicked; Foo is 'Hello' , but the GUI never updates. Button clicked; Foo is 'Hello' ,但 GUI 永远不会更新。 I've tried using Path=Foo , XPath=Foo , etc., as well as setting UpdateSourceTrigger=PropertyChanged and verifying updates with NotifyOnTargetUpdated=True ... nothing seems to result in the text in the UI being updated to match the underlying property value, even though the property value seems to be getting updated just fine.我试过使用Path=FooXPath=Foo等,以及设置UpdateSourceTrigger=PropertyChanged并使用NotifyOnTargetUpdated=True验证更新......似乎没有任何内容导致 UI 中的文本被更新以匹配底层属性值,即使属性值似乎更新得很好。

What am I doing wrong?我究竟做错了什么? I feel like there's just a simple and fundamental misunderstanding in how I'm approaching this.我觉得我如何处理这个问题只是一个简单而根本的误解。

edit:编辑:

Poking around a bit more and reading similar questions has led me to a potential fix: namely, adding a name to the root UserControl element in TestControl.xaml ( x:Name="control" ), and changing the binding to explicitly specify that control ( {Binding Foo, ElementName=control} ).多探索一下并阅读类似的问题让我找到了一个潜在的解决方法:即,向 TestControl.xaml 中的根 UserControl 元素添加一个名称( x:Name="control" ),并更改绑定以显式指定该控件( {Binding Foo, ElementName=control} )。

I'm guessing that by default, {Binding Foo} on the Button element just means "find a property named 'Foo' on this Button control" , whereas I'd assumed it'd mean "find a property named 'Foo' in the context that this Button is being declared in, ie on the TestControl" .我猜默认情况下,Button 元素上的{Binding Foo}仅表示“在此 Button 控件上找到名为 'Foo' 的属性” ,而我认为它的意思是“在声明此 Button 的上下文,即在 TestControl" 上

Is specifying an explicit ElementName the best fix here?在这里指定一个显式的 ElementName 是最好的解决方法吗?

You have to set the source object of the Binding to the UserControl instance, eg like this:您必须将 Binding 的源对象设置为 UserControl 实例,例如:

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

or或者

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

If you have many such Bindings, you may also set the DataContext of the top level element in the UserControl's XAML to the UserControl instance:如果您有很多这样的绑定,您还可以将 UserControl 的 XAML 中顶级元素的 DataContext 设置为 UserControl 实例:

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

You must however avoid to set the DataContext of the UserControl (which is often recommend by "expert" bloggers), because that would break DataContext-based Bindings of the UserControl properties like但是,您必须避免设置 UserControl 的 DataContext(“专家”博主通常建议这样做),因为这会破坏 UserControl 属性的基于 DataContext 的绑定,例如

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM