1

2 つの DependencyProperties を持つユーザー コントロールがあります。各 DependencyProperty には PropertyChangedCallback があります。プロパティ値が設定される順序でコールバックが呼び出されることが重要です。だから私が書くなら

Status = MyStatus.DataIsImporting;

var data = AsynchronouslyImportData();

Data = data;

Data のプロパティが変更されたコールバックの前に、Status のプロパティが変更されたコールバックが呼び出されるようにします。しかし、デバッグによると(それに関するドキュメントが見つかりません)、コールバック呼び出しの順序は未定義です。それを解決する方法はありますか?

アップデート。上記のステータスとデータは、ユーザー コントロール インスタンスに直接設定されていません。これらは、バインディングを通じてユーザーがプロパティを制御するための ViewModel プロパティです。

Update2 . 私は今この問題で遊んでいて、非常に奇妙な修正がありました。以前のユーザー コントロールの使用方法は次のとおりです。

<MyUserControl Status={Binding Status, Mode=TwoWay} Data={Binding Data}/>

バインディングの順序を変更したところ、うまくいきました!

<MyUserControl Data={Binding Data} Status={Binding Status, Mode=TwoWay}/>

まだ非同期で動作しますが (バインド システム内に一種のメッセージ ループがあるように見えます)、PropertyChangedCallback ハンドラーを正しい順序で呼び出すようになりました。

私はバインディングの順序についてグーグルで調べており、時々同様の問題(たとえば、これ)を見つけますが、なぜそれが起こるのかはまだ不明です.

アップデート 3.問題の真の原因を発見しました。私のコントロールを使用するアプリケーションには、いくつかの DataTemplates を持つ ContentControl があります (ViewModel タイプによって異なります)。説明されている動作は、コントロールが配置されている DataTemplate が最新でない場合 (または、他の DataTemplate に切り替えて元に戻す場合) に発生します。詳細についてはまだ解明中です。

4

2 に答える 2

0

おそらく、この回答の前に次のステートメントを付ける必要があります。

DependencyProperty「のシーケンスを配置/順序付けしてに加えられた変更を順序付ける必要がある場合DependencyPropertyChangedCallbacksは、おそらく間違っています。」

そうは言っても、ここにあなたが話していることをちょっとしたアイドルスローされたコードがあります:

オブジェクト:

public class SomeThing : DependencyObject, IDisposable
{
    public static readonly DependencyProperty StatusProperty = 
        DependencyProperty.Register(
            "Status", 
            typeof(string), 
            typeof(SomeThing), 
            new FrameworkPropertyMetadata(OnStatusChanged));
    public static readonly DependencyProperty DataProperty = 
        DependencyProperty.Register(
            "Data", 
            typeof(string), 
            typeof(SomeThing), 
            new FrameworkPropertyMetadata(OnDataChanged));

    // The OrderedBag is from the Wintellect.PowerCollections, 
    // as I was too lazy to write my own PriorityQueue-like implementation
    private static OrderedBag<Tuple<int, DependencyObject, DependencyPropertyChangedEventArgs>> _changeQueue = 
        new OrderedBag<Tuple<int, DependencyObject, DependencyPropertyChangedEventArgs>>((l,r) => l.Item1.CompareTo(r.Item1));

    private static object _syncRoot = new object();
    private static Task queueTenderTask;
    private static CancellationTokenSource canceller;

    static SomeThing()
    {
        canceller = new CancellationTokenSource();
        queueTenderTask = Task.Factory.StartNew(queueTender);
    }

    public string Status
    {
        get { return (string)this.GetValue(StatusProperty); }
        set { this.SetValue(StatusProperty, value); }
    }
    public string Data
    {
        get { return (string)this.GetValue(DataProperty); }
        set { this.SetValue(DataProperty, value); }
    }

    public void Dispose()
    {
        if(canceller != null)
        {
            canceller.Cancel();
            if(queueTenderTask != null)
            {
                queueTenderTask.Wait();
            }
        }
    }

    private static void OnStatusChanged(
        DependencyObject dobj, 
        DependencyPropertyChangedEventArgs args)
    {
        lock(_syncRoot)
        {
            _changeQueue.Add(Tuple.Create(0, dobj, args));
        }
    }
    private static void OnDataChanged(
        DependencyObject dobj, 
        DependencyPropertyChangedEventArgs args)
    {
        lock(_syncRoot)
        {
            _changeQueue.Add(Tuple.Create(1, dobj, args));
        }
    }
    private static void ProcessChange(
        Tuple<int, DependencyObject,DependencyPropertyChangedEventArgs> pair)
    {
        // do something useful?
        Console.WriteLine(
            "Processing change on {0} from {1} to {2}", 
            pair.Item3.Property.Name, 
            pair.Item3.OldValue, 
            pair.Item3.NewValue);
    }

    private static void queueTender()
    {
        Console.WriteLine("Starting queue tender...");
        var shouldCancel = canceller.IsCancellationRequested;
        while(!shouldCancel)
        {
            lock(_syncRoot)
            {
                if(_changeQueue.Count > 0)
                {
                    var nextUp = _changeQueue[0];
                    _changeQueue.RemoveFirst();    
                    ProcessChange(nextUp);
                }
            }
            for(int i=0;i<10;i++)
            {
                shouldCancel = canceller.IsCancellationRequested;
                if(shouldCancel) break;
                Thread.Sleep(10);
            }
        }
    }
}

そしてテスト:

void Main()
{
    var rnd = new Random();
    using(var ob = new SomeThing())
    {
        for(int i=0;i<10;i++)
        {
            if(rnd.NextDouble() > 0.5)
            {
                Console.WriteLine("Changing Status...");
                ob.Status = rnd.Next(0, 100).ToString();
            }
            else
            {
                Console.WriteLine("Changing Data...");
                ob.Data = rnd.Next(0, 100).ToString();
            }
        }
        Console.ReadLine();
    }
}

出力:

キュー入札を開始しています...
ステータスを変更しています...
ステータスを変更しています...
ステータスを変更しています...
データを変更しています...
データを変更しています...
データを変更しています...
データを変更しています...
データを変更しています...
データを変更しています...
ステータスを変更しています...
ステータスを から 1 に変更中
1 から 73 へのステータスの変更の処理
73 から 57 へのステータスの変更の処理
ステータスの 57 から 33 への変更の処理
から 10 へのデータの変更の処理
データの処理を 10 から 67 に変更
データの処理を 67 から 40 に変更
データの処理を 40 から 64 に変更
データの処理を 64 から 47 に変更
データの処理を 47 から 81 に変更
于 2013-03-20T18:03:24.660 に答える
-1

コメントによると、これらのプロパティは である必要はありませんDependencyProperties。それらはTwoWayBinding のソースですが、そうである必要はありませんDependencyPropertiesTwoWayBinding を任意の標準プロパティに使用しINotifyPropertyChanged、ソース値が変更されたときにターゲットに通知するために実装できます。

バインディングの場合でも、ターゲットは である必要がありますが、ソースではありません (デフォルトBindingDependencyPropertyはbind であり、それらを標準のプロパティにバインドできます) 。TwoWayTextBoxesTwoWay

プロパティ変更通知を手動で発生させる場合、これらのアクションは同じスレッドで順次発生し、通知が順番に発生することが保証されます。フレームワークにDependencyProperties値と通知を管理させているため、順序の保証はありません。

DependencyPropertiesViewModel ではめったに必要ありません。ほとんどの場合、必要なのは ViewModel を実装INotifyPropertyChangedしてプロパティ変更通知を発生させることだけです。

于 2013-03-20T22:07:30.767 に答える