PropertyChangedCallbacks执行的顺序

问题描述 投票:1回答:2

我有一个具有两个DependencyProperty的用户控件。每个DependencyProperty都有PropertyChangedCallback。重要的是要在设置顺序属性值时调用回调。所以如果我写

Status = MyStatus.DataIsImporting;

var data = AsynchronouslyImportData();

Data = data;

我希望在数据的属性更改回调之前调用状态的属性更改回调。但是根据调试(找不到任何文档),回调调用的顺序是不确定的。有什么办法可以解决?

更新。上面看到的状态和数据未直接设置为用户控件实例。这些是ViewModel属性,可以通过绑定来控制用户控制属性。

Update2。我现在正在玩这个问题,并且有一个非常奇怪的解决方法。这是以前使用我的用户控件的方式:

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

我刚刚更改了绑定顺序,它起作用了!

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

它仍然表现为异步(看起来像绑定系统中存在某种消息循环),但是现在它以正确的顺序调用PropertyChangedCallback处理程序。

我正在搜索绑定顺序,有时会发现类似的问题(例如this one,但仍不清楚为什么会发生。

Update 3。我找到了一个真正的问题根源。使用我的控件的应用程序具有带有多个DataTemplates的ContentControl(取决于ViewModel类型)。当放置我的控件的DataTemplate不是最新的时(或者当您切换到其他DataTemplate并返回时),将发生描述的行为。我仍在澄清细节。

.net wpf mvvm
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


-1
投票
根据您的评论,这些属性不必为DependencyProperties。它们是TwoWay绑定的源,但是不要求它们是DependencyProperties。您可以使用TwoWay绑定到任何标准属性,并实现INotifyPropertyChanged以便在源值更改时通知目标。
© www.soinside.com 2019 - 2024. All rights reserved.