views:

181

answers:

1

Ok, so I found something weird over the weekend. I have a WPF app that spawns off some threads to perform background work. Those background threads then Post work items to my Synchronization Context. This is all working fine except for one case. When my threads finish sometimes they will post an action onto the dispatcher that will open up a Popup window. What ends up happening is that if 2 threads both post an action on the Dispatcher it starts processing one, then if I open up a Popup window with Window.ShowDialog(); the current execution path pauses waiting for feedback from the dialog box as it should. But the problem arises that when the dialog box opens the Dispatcher then goes and starts immediately starts running the second action that was posted. This results in two code paths being executed. The first one with a message box being held open, while the second one is running wild because my application state is unknown because the first action never completed.

I've posted some example code to demonstrate the behavior I'm talking about. What should happen is that if I post 2 actions and the 1st one opens up a dialog box the second action shouldn't run until after the 1st action has been completed.

public partial class Window1 : Window {

    private SynchronizationContext syncContext;
    public Window1() {
        InitializeComponent();
        syncContext = SynchronizationContext.Current;
    }

    private void Button_ClickWithout(object sender, RoutedEventArgs e) {
        // Post an action on the thread pool with the syncContext
        ThreadPool.QueueUserWorkItem(BackgroundCallback, syncContext);
    }

    private void BackgroundCallback(object data) {
        var currentContext = data as SynchronizationContext;

        System.Console.WriteLine("{1}: Thread {0} started", Thread.CurrentThread.ManagedThreadId, currentContext);

        // Simulate work being done
        Thread.Sleep(3000);

        currentContext.Post(UICallback, currentContext);

        System.Console.WriteLine("{1}: Thread {0} finished", Thread.CurrentThread.ManagedThreadId, currentContext);
    }

    private void UICallback(object data) {
        System.Console.WriteLine("{1}: UI Callback started on thread {0}", Thread.CurrentThread.ManagedThreadId, data);

        var popup = new Popup();

        var result = popup.ShowDialog();

        System.Console.WriteLine("{1}: UI Callback finished on thread {0}", Thread.CurrentThread.ManagedThreadId, data);
    }
}

The XAML is just a Window with a button that calls Button_ClickWithout OnClick. If you push the button twice and wait 3 seconds, you will see you get 2 dialogs popping up one over the other, where the expected behavior would be the first one pops up, then once it's closed the second one will pop up.

So my question is: Is this a bug ? or how do I mitigate this so I can have only one action be processed at a time when the first action halts execution with a Window.ShowDialog() ?

Thanks, Raul

A: 

A modal dialog box does not prevent the owner window from processing messages, otherwise you'd see it fail to redraw as the modal dialog was moved over its surface (just as an example).

In order to achieve what you want, you have to implement your own queue on the UI thread, possibly with some synchronization to "wake it up" when the first work item arrives.

EDIT:

Also if you examine the call stack of the UI thread while the 2nd modal dialog box is up, you might find out that it has the first ShowDialog call above it in the stack.

EDIT #2:

There might be an easier way of doing this, without implementing your own queue. If instead of the SynchronizationContext, you would use the Dispatcher object, you would be able to call BeginInvoke on it with a priority of DispatcherPriority.Normal, and it will get queued properly (check).

Aviad P.
I did create my own synchronization context queue but I was looking for a more elegant solution. I tried using the dispatcher with priority Normal but I got the same result. I think at the end of the day I'm just going to change my UI to not use modal dialogs. You would think there would be some way of pulling this off, but I gusss not.
HaxElit