Partager via


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");
                 }
             }
         }
     }
 }