4

最近、多くのテストを行いましたBinding Mode = OneWayToSourceが、特定のことがなぜ起こるのかまだわかりません。

dependency property例として、クラス コンストラクターに値を設定します。バインディングが初期化されると、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); }
        }
     }

これはViewModelです:

    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;
     }
4

2 に答える 2

4
this.Txt = "123";

これにより、バインディングがローカル値に置き換えられます。依存関係プロパティの値の優先順位を参照してください。DependencyObject.SetValue基本的に、本当に必要なときに呼び出していますDependencyProperty.SetCurrentValue。さらに、これを行うには、ライフサイクルの後半まで待つ必要があります。そうしないと、WPF は 2 回更新されます。1Str回目は "123" で、次に 2 回目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>
于 2013-09-13T08:29:02.817 に答える