Search code examples
wpfsystem.reactivedispatcher

How to tell Dispatcher is initialized and running?


In my console app I've been trying to start an STA thread and show a WPF window. I've succeeded showing the window, but I had issues with a library using Dispatcher (System.Reactive.Windows.Threading to be precised). I've fixed my problems using code from this doc - what I was missing was calling System.Windows.Threading.Dispatcher.Run() in the right moment.

But after reading this article carefully (and others) and examining Dispatcher's API I still don't know: how to tell WPF Dispatcher is correctly initialized and running? It'd be very useful for libraries requiring Dispatcher, if they could check it.

-- EDIT --

// Extending my question after @Peter Duniho remarks

Having C# console application I wanted to create a WPF window, where I'll observe, on Dispatcher, some data. The full code is here

So I have my program, where Main class looks like that:

static void Main(string[] args)
    {
        var observable = Observable
            .Interval(TimeSpan.FromMilliseconds(500))
            .TakeWhile(counter => counter < 10);

        var thread = new Thread(() =>
        {
            new TestWindow(observable);
            Dispatcher.Run();
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.IsBackground = true;
        thread.Start();

        Console.ReadKey();
    }

I create here an observable with Interval operator, which ticks every 500 milliseconds, and I pass it to a TestWindow (code below) which I run on a separate thread. After 10 ticks I end the observable sequence.

TestWindow class:

public class TestWindow : Window
    {
        public TestWindow(IObservable<long> observable)
        {
            var isDispatcherInitialized = false;

            Dispatcher.Invoke(() => isDispatcherInitialized = true, DispatcherPriority.ApplicationIdle);

            if (!isDispatcherInitialized)
                throw new ApplicationException("Dispatcher not initialized");

            observable
                .ObserveOnDispatcher()
                .Window(TimeSpan.FromMilliseconds(600))
                .Subscribe(_ => Console.WriteLine($"OnNext, observed on dispatcher with Window operator"));
        }
    }

In TestWindow I observe my observable on Dispatcher (ObserveOnDispatcher()), and I use Window operator.

PROBLEM with that code (tested on .NET Framework and on .NET Core 3.0 preview):

  • if I don't call Dispatcher.Run(); when starting STA thread, the validation where I call Dispatcher.Invoke() will be passed, but ObserveOnDispatcher() won't work correctly - subscription never stops, and the message: "OnNext, observed on dispatcher with Window operator" goes forever.

That's why I was wondering if I could detect Dispatcher's state.


Solution

  • It would be helpful if you would elaborate on this statement:

    It'd be very useful for libraries requiring Dispatcher, if they could check it.

    That is, why would it be useful?

    For one, if you are using a third-party library (such as the Reactive Extensions (Rx) for .NET you mentioned, how would you knowing how to check for the dispatcher state help that library?

    For another, what scenario doesn't work for you? Lacking a specific problem to solve, your question is fairly open-ended. It's not clear what type of answer would actually address your question.

    That said, two things come to mind:

    1. If you want to know if a dispatcher has been created for a given thread, you should call System.Windows.Threading.Dispatcher.FromThread(System.Threading.Thread.CurrentThread); This will return null if not dispatcher has been created yet for that thread, or a reference to the dispatcher if it has been.
    2. If you want to know that the dispatcher has completed initialization and is ready to dispatch things, it seems to me that the easiest thing to do is ask it to dispatch something, and when it does, it's ready. Using an overload of one of the invoking methods (BeginInvoke(), Invoke(), or InvokeAsync()) that takes a DispatcherPriority value, you can get fine-grained information regarding just what level of initialization has happened. For example, if you pass DispatcherPriority.Normal or DispatcherPriority.Send, when your delegate is invoked you'll know that the dispatcher is running. But if you pass DispatcherPriority.ApplicationIdle or DispatcherPriority.SystemIdle, you'll know that not only is the dispatcher running, but it's cleared its backlog of initial events to dispatch and the application is sitting waiting for user input.