我最近用 Binding Mode = OneWayToSource 做了很多测试,但我仍然不知道为什么会发生某些事情。

例如,我在类构造函数中的 dependency property 上设置了一个值。现在,当 Binding 初始化时,Target 属性被设置为其默认值。意味着 dependency property 被设置为 null 并且我丢失了我在 constructor 中初始化的值。

为什么会这样? Binding Mode 没有按照名称描述的方式工作。它应该只更新 Source 而不是 Target
这是代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MyViewModel();
    }

    private void OnClick(object sender, RoutedEventArgs e)
    {
        this.DataContext = new MyViewModel();
    }
}

这是 XAML:
<StackPanel>
        <local:MyCustomControl Txt="{Binding Str, Mode=OneWayToSource}"/>
        <Button Click="OnClick"/>
</StackPanel>

这是 MyCustomControl:
    public class MyCustomControl : Control
    {
        public static readonly DependencyProperty TxtProperty =
            DependencyProperty.Register("Txt", typeof(string), typeof(MyCustomControl), new UIPropertyMetadata(null));

        static MyCustomControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
        }

        public MyCustomControl()
        {
           this.Txt = "123";
        }

        public string Txt
        {
           get { return (string)this.GetValue(TxtProperty); }

           set { this.SetValue(TxtProperty, value); }
        }
     }

这是 View 模型:
    public class MyViewModel : INotifyPropertyChanged
    {
        private string str;

        public string Str
        {
            get { return this.str; }
            set
            {
                if (this.str != value)
                {
                    this.str = value; this.OnPropertyChanged("Str");
                }
            }
         }

        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null && propertyName != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
     }

最佳答案

this.Txt = "123";

这是用本地值替换您的绑定(bind)。见 dependency property value precedence 。当您真的想要 DependencyObject.SetValue 时,您实际上是在调用 DependencyProperty.SetCurrentValue 。此外,您需要等到生命周期的后期才能执行此操作,否则 WPF 将更新 Str 两次:一次是“123”,然后是 null :
protected override void OnInitialized(EventArgs e)
{
    base.OnInitialized(e);
    this.SetCurrentValue(TxtProperty, "123");
}

如果您在用户控件的构造函数中执行此操作,它会在 WPF 实例化它时执行,但会在 WPF 加载和反序列化并应用您的 BAML 时立即替换。

更新:抱歉,我误解了你的确切问题,但现在有一个重现,复制在下面。我错过了您随后更新 DataContext 的部分。我通过在数据上下文更改时设置当前值来解决此问题,但在单独的消息中。否则,WPF 会忽略将更改转发到新数据源。
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

namespace SO18779291
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.setNewContext.Click += (s, e) => this.DataContext = new MyViewModel();
            this.DataContext = new MyViewModel();
        }
    }

    public class MyCustomControl : Control
    {
        public static readonly DependencyProperty TxtProperty =
            DependencyProperty.Register("Txt", typeof(string), typeof(MyCustomControl), new UIPropertyMetadata(OnTxtChanged));

        static MyCustomControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
        }

        public MyCustomControl()
        {
            this.DataContextChanged += (s, e) =>
            {
                this.Dispatcher.BeginInvoke((Action)delegate
                {
                    this.SetCurrentValue(TxtProperty, "123");
                });
            };
        }

        public string Txt
        {
            get { return (string)this.GetValue(TxtProperty); }

            set { this.SetValue(TxtProperty, value); }
        }

        private static void OnTxtChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            Console.WriteLine("Changed: '{0}' -> '{1}'", e.OldValue, e.NewValue);
        }
    }

    public class MyViewModel : INotifyPropertyChanged
    {
        private string str;

        public string Str
        {
            get { return this.str; }
            set
            {
                if (this.str != value)
                {
                    this.str = value; this.OnPropertyChanged("Str");
                }
            }
        }

        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null && propertyName != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

XAML:
<Window x:Class="SO18779291.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:SO18779291"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <local:MyCustomControl Txt="{Binding Str, Mode=OneWayToSource}"/>
        <Button x:Name="setNewContext">New Context</Button>
        <TextBlock Text="{Binding Str, Mode=OneWay}"/>
    </StackPanel>
</Window>

关于c# - 绑定(bind) OneWayToSource - 奇怪的行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18779291/

10-12 06:42