How to host and use a DependencyObject in a threaded Console app
Recently, I developed some business objects that I needed to use both in WPF world (with vanilla data binding), as well as within a WCF web service. Of course, from the WPF books we learn that a DependenyProperty can only be directly accessed from the thread where it was created. So the implementation does that in the appropriate thread. The problem I still had was that my messages were dispatched to my business object, but that they remained in the queue with a DispatcherOperationStatus Pending. Thanks to the guys from the WPF team (Dwayne Need and a bunch of other folks) and their "did you actually start the Dispatcher?" question, here's a small sample. Maybe it helps ...
Christian
namespace Microsoft.EMIC.ChGeuer.BlogSample
{
using System;
using System.Windows;
using System.Threading;
using System.Windows.Threading;
using System.Diagnostics;
public class HostingDependencyObjectInOwnApplicationRepro
{
[STAThread]
static void Main(string[] args)
{
Thread.CurrentThread.Name = "MyServiceHostApplication";
Dispatcher d = Dispatcher.CurrentDispatcher;
SomeDataObject data = new SomeDataObject() { Id = "Foo" };
data.DataChanged += new DataChangedEventHandler(ProgramDataChanged);
Console.WriteLine(data.DoStuff("New value 1"));
new Thread((ThreadStart)delegate()
{
// This is the thread that ocntinuously works with the data,
// like a WCF service
Thread.CurrentThread.Name = "MyWCFService";
for (var i = 0; i < 10; i++)
{
Thread.Sleep(500);
var res = data.DoStuff(string.Format("New value {0}", i));
if (res == null)
{
break;
}
Console.WriteLine("The result from the thread is '{0}'", res);
}
}).Start();
Dispatcher mainDispatcher = Dispatcher.CurrentDispatcher;
new Thread((ThreadStart)delegate()
{
Console.WriteLine("Press <Return> to close the application");
Console.ReadLine();
AppShutdownDispatcher(mainDispatcher);
}).Start();
Dispatcher.Run();
}
public static void AppShutdownDispatcher(Dispatcher theMainDispatcher)
{
if (theMainDispatcher.CheckAccess())
{
theMainDispatcher.InvokeShutdown();
}
else
{
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Send, (Action)(() =>
{
theMainDispatcher.InvokeShutdown();
}));
}
}
static void ProgramDataChanged(object sender, DataChangedEventArgs e)
{
Console.WriteLine("Data changed to {0}", e.NewValue);
}
}
public delegate void DataChangedEventHandler(object sender, DataChangedEventArgs e);
public class DataChangedEventArgs : EventArgs
{
internal DataChangedEventArgs(string newValue)
{
this.NewValue = newValue;
}
public string NewValue { get; private set; }
}
public class SomeDataObject : DependencyObject
{
public SomeDataObject()
{
this.Dispatcher.Hooks.OperationPosted += new DispatcherHookEventHandler(DispatcherOperationPosted);
}
public event DataChangedEventHandler DataChanged;
public static readonly DependencyProperty IdProperty =
DependencyProperty.Register("Id", typeof(string), typeof(SomeDataObject),
new PropertyMetadata() { DefaultValue = string.Empty, PropertyChangedCallback = OnDataChanged });
public string Id
{
get { return (string)this.GetValue(SomeDataObject.IdProperty); }
set { this.SetValue(SomeDataObject.IdProperty, value); }
}
private static void OnDataChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Debug.Assert(sender is SomeDataObject);
DataChangedEventHandler handler = ((SomeDataObject)sender).DataChanged;
if (handler != null)
{
handler(sender, new DataChangedEventArgs((string)e.NewValue));
}
}
private void DispatcherOperationPosted(object sender, DispatcherHookEventArgs e)
{
Console.Out.WriteLine("Posted operation with prio {0}", e.Operation.Priority.ToString());
e.Dispatcher.UnhandledException += new DispatcherUnhandledExceptionEventHandler(Dispatcher_UnhandledException);
}
void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
Console.Error.WriteLine("Unhandled {0}", e.Exception.GetType().Name);
}
public string DoStuff(string someNewValue)
{
Console.WriteLine("Called by {0}", Thread.CurrentThread.Name);
if (this.Dispatcher.HasShutdownStarted)
{
return null;
}
if (this.Dispatcher.CheckAccess())
{
this.Id = someNewValue;
return this.Id + " computed";
}
else
{
Func<string, string> doIt = (arg) =>
{
this.Id = arg;
return this.Id + " computed";
};
// return (string)this.Dispatcher.Invoke(DispatcherPriority.Send,
// TimeSpan.FromMilliseconds(500), doIt, someNewValue);
DispatcherOperation op = base.Dispatcher.BeginInvoke(DispatcherPriority.Send, doIt, someNewValue);
var status = op.Wait(TimeSpan.FromMilliseconds(500));
Console.WriteLine("Status: " + status.ToString());
if (status == DispatcherOperationStatus.Completed)
{
return (string)op.Result;
}
else if (status == DispatcherOperationStatus.Aborted)
{
return "Cancelling";
}
else
{
throw new Exception("Houston, we have a problem");
}
}
}
}
}