如何在 属性 上实现 two-way 绑定?

How to implement two-way binding on a property?

我知道有很多关于依赖属性的问题,我也看过很多,但是 none 似乎解决了我的问题。

我有一个 Window 这样的:

<Window x:Class="WpfBindingTest.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:WpfBindingTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <StackPanel>
        <local:TextInputWrapper MyText="{Binding MyTextValue, Mode=TwoWay}" />
        <TextBox Text="{Binding MyTextValue, Mode=TwoWay}"/>
    </StackPanel>
</Window>

其中 MyTextValue 只是一个字符串 属性,在更改时发出通知:

    private string _myTextValue = "Totally different value";
    public string MyTextValue { get { return _myTextValue; } set { _myTextValue = value; OnPropertyChanged(); } }
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

TextInputWrapper 也相当简单:

<UserControl x:Class="WpfBindingTest.TextInputWrapper"
             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:WpfBindingTest"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <TextBox Text="{Binding MyText}"></TextBox>
</UserControl>

Code-behind:

public partial class TextInputWrapper : UserControl
{
    public static readonly DependencyProperty MyTextProperty = DependencyProperty.Register("MyText",
        typeof(string), typeof(TextInputWrapper), new PropertyMetadata("Empty"));
    public TextInputWrapper()
    {
        InitializeComponent();
    }
    public string MyText
    {
        get { return (string)GetValue(MyTextProperty); }
        set { SetValue(MyTextProperty, value); }
    }

}

据我所知,我的 Window 现在应该有 2 个彼此绑定的 TextBox 控件。就像我改变其中一个的值一样,另一个应该更新。

但是,我最终得到了 2 个单独的文本框,其中第一个以文本 "Empty" 开头,下一个以文本 "Totally different value" 开头。像这样:

并且更改其中任何一个的文本不会在另一个中重现。

我希望它们都以文本 "Totally different value" 开始并与它们的值同步(通过传播对 MainWindow 上的 MyTextValue 属性 的更改,以及通过通知那里的更改,更改将传播到另一个文本框)。在我的控制中正确实施 data-binding 我缺少什么?

尝试更改此行:

public static readonly DependencyProperty MyTextProperty = DependencyProperty.Register("MyText",
typeof(string), typeof(TextInputWrapper), new PropertyMetadata("Empty"));

为此:

public static readonly DependencyProperty MyTextProperty = DependencyProperty.Register("MyText",
    typeof(string), typeof(TextInputWrapper), new FrameworkPropertyMetadata("Empty", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

删除分配

DataContext="{Binding RelativeSource={RelativeSource Self}}"

来自 UserControl 的 XAML。相反,将 "internal" 绑定的 RelativeSource 设置为控件实例:

<UserControl x:Class="WpfBindingTest.TextInputWrapper" ...>
    <TextBox Text="{Binding MyText,
                    RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</UserControl>

显式设置 UserControl 的 DataContext 可防止从其父控件继承 DataContext,即像

这样的绑定
<local:TextInputWrapper MyText="{Binding MyTextValue, Mode=TwoWay}" />

将使用 UserControl 作为源对象,而不是当前的 DataContext。


作为一般规则,从不显式设置 UserControl 的 DataContext。