Note that there are some explanatory texts on larger screens.

plurals
  1. POHow do UI events get to the WPF Dispatcher?
    primarykey
    data
    text
    <p>The current application I'm developing is a plug-in for another commercial application. In my plug-in I am able to create a custom <code>Command</code> class with an <code>Execute()</code> method which is called by the parent application when the user decides to run the custom command.</p> <p>The objects provided by this API are not thread safe, and thus I can only access them from the <code>Execute()</code> thread started by the application. However, some of the tasks I'm trying to perform take a while to complete and I'd like to show the user a progress window.</p> <p>I am able to create a functioning and responsive progress window by starting another thread to show the window. This required that I manually start the WPF dispatcher for this thread as described on in <a href="http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/" rel="nofollow">this blog post</a>.</p> <p>I'm able to drag this window while the process is going. However, if interact with anything inside the window while the task is running, such as clicking on the window, it will freeze.</p> <p>I found that if I instantiate and show a WPF window in the actual <code>Command.Execute()</code> thread the window will remain visible and responsive after the command exits. So it seems that the native program has a WPF dispatcher running on the command thread. I verified this by hooking into <code>Dispatcher.CurrentDispatcher.Hooks.OperationPosted</code> from <code>Execute()</code>. This continued to send messages after the <code>Execute()</code> method exited as I interacted with the native application.</p> <p>So what I think is happening is when I click on the window this click is being handled by the native application's Dispatcher. This dispatcher does not know about my window, or even have access to it since it was created in another thread. In addition, that dispatcher can't do anything anyway because it's associated thread is busy in my <code>Execute()</code> method performing my time consuming task.</p> <p>So can someone explain how clicking on or otherwise interacting with a WPF object ultimately get's added to associated Dispatcher's queue? Is there some way I can redirect this so that when a user clicks on my progress window the event get's sent to the correct dispatcher?</p> <h1>Update</h1> <p>Here's some sample code I've been using trying to figure out what is going on.</p> <p><code>Command.Execute()</code> is called by the native application when the user evokes my custom command. ProgressWindow is just a simple window with a ProgressBar and a button. The button simply has an empty onClick handler where I've set a breakpoint.</p> <p><code>WorkerClass.DoWork()</code> open a <code>ProgressWindow</code> on another thread. It then does some fake work by looping in a for loop, updating the progress bar in the window on each iteration. </p> <pre><code>public class Command { public void Execute() { Thread.CurrentThread.Name = "Command Thread"; WorkerClass.DoWork(); } } public class WorkerClass { //Creates the new progress window thread public static void DoWork() { Thread newprogWindowThread = new Thread(new ThreadStart(ShowProgressWindow)); newprogWindowThread.Name = "Progress Window Thread"; newprogWindowThread.SetApartmentState(ApartmentState.STA); newprogWindowThread.IsBackground = true; //Starts New Progress Window Thread using (_progressWindowWaitHandle = new AutoResetEvent(false)) { //Starts the progress window thread newprogWindowThread.Start(); //Wait for progress window thread to notify that the window is up _progressWindowWaitHandle.WaitOne(); } //Do some fake work for (int i = 1; i &lt;= 100; i++) { //Do Some work Thread.Sleep(100); //Updates the progress window //This method queues the update to the //actual Dispatcher of the window. progWindow.UpdateStatus("Item " + i.ToString(), i, 100); } MessageBox.Show("Work Finished"); } private static ProgressWindow progWindow; internal static EventWaitHandle _progressWindowWaitHandle; private static void ShowProgressWindow() { //Creates and shows progress window progWindow = new ProgressWindow("Running Task..."); progWindow.Show(); //Subscribes to window closed event progWindow.Closed += progWindow_Closed; //Notifies other thread the progress window is open when the dispatcher starts up System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Func&lt;bool&gt;(_progressWindowWaitHandle.Set)); //Starts the WPF dispatcher System.Windows.Threading.Dispatcher.Run(); } private static void progWindow_Closed(object sender, EventArgs e) { //When window is closed shuts down WPF dispatcher //this will release thread from Dispatcher.Run() above //and exit the ShowProgressWindow() method and exit the secondary thread ProgressWindow window = (ProgressWindow)sender; window.Dispatcher.BeginInvokeShutdown(DispatcherPriority.Normal); } } </code></pre> <p>Like I said before if I just let it run it works fine. The progress bar updates correctly as the "work" is done. </p> <p>Yet if I click the button on the progress window it will freeze. In addition, the break point I've set in the <code>button_OnClick()</code> handler is not hit.</p> <p>At this point I can set a break point and see that the original "Command Thread" is still looping and doing work even though the progress window is frozen. Once this completes I see the "Work Finished" message box, and the "Command Thread" thread exits out of <code>Command.Execute()</code> back to the native application.</p> <p>So here's what's weird. Right after the "Command Thread" exits back to the native application my breakpoint in <code>button_OnClick()</code> is hit by the "Progress Window Thread". In addition, just after that all the subsequent calls to <code>progWindow.UpdateStatus()</code> that I had previously queued from the "Command Thread" go through and update the progress bar all the way to 100%</p> <p>So it appears that my original hypothesis was wrong. The click is in fact being registered with the correct dispatcher. However, for some reason the "Progress Window Thread" dispatcher get stuck on this click event until my <code>Execute()</code> method exits and frees up the "Command Thread" dispatcher.</p> <p>Does this help at all? Can anyone explain what's going on? I thought the Dispatchers of two different threads were independent from one another? It's behaving as if the click event on the "Progress Window Thread" dispatcher is calling Invoke() on the "Command Thread" dispatcher and blocking until that dispatcher is free. </p> <p>I should also mention that I can't recreate this problem in my own stand alone WPF application. If I make a standard WPF application in VS2012 and make a button on <code>MainWindow</code> that calls <code>WorkerClass.DoWork()</code> it works fine. Clicking the button on <code>ProgressWindow</code> does not freeze the window, and the <code>button_OnClick</code> handler is called immediately.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload