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


Comments (0)

Skip to main content