I should probably preface this answer with this statement:
"If you need to order the changes made to a DependencyProperty
by arranging/ordering the sequence of DependencyPropertyChangedCallbacks
, you're probably doing it wrong."
That said, here's some idle thrown-together code that kinda does what you're talking about:
The object:
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);
}
}
}
}
And the test:
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();
}
}
Output:
Starting queue tender...
Changing Status...
Changing Status...
Changing Status...
Changing Data...
Changing Data...
Changing Data...
Changing Data...
Changing Data...
Changing Data...
Changing Status...
Processing change on Status from to 1
Processing change on Status from 1 to 73
Processing change on Status from 73 to 57
Processing change on Status from 57 to 33
Processing change on Data from to 10
Processing change on Data from 10 to 67
Processing change on Data from 67 to 40
Processing change on Data from 40 to 64
Processing change on Data from 64 to 47
Processing change on Data from 47 to 81