Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p><strong>The immediate problem</strong></p> <p>Your immediate problem is that <code>SynchronizationContext.Current</code> is not automatically set for WPF. To set it you will need to do something like this in your TheUISync code when running under WPF:</p> <pre><code>var context = new DispatcherSynchronizationContext( Application.Current.Dispatcher); SynchronizationContext.SetSynchronizationContext(context); UISync = context; </code></pre> <p><strong>A deeper problem</strong></p> <p><code>SynchronizationContext</code> is tied in with the COM+ support and is designed to cross threads. In WPF you cannot have a Dispatcher that spans multiple threads, so one <code>SynchronizationContext</code> cannot really cross threads. There are a number of scenarios in which a <code>SynchronizationContext</code> can switch to a new thread - specifically anything which calls <code>ExecutionContext.Run()</code>. So if you are using <code>SynchronizationContext</code> to provide events to both WinForms and WPF clients, you need to be aware that some scenarios will break, for example a web request to a web service or site hosted in the same process would be a problem.</p> <p><strong>How to get around needing SynchronizationContext</strong></p> <p>Because of this I suggest using WPF's <code>Dispatcher</code> mechanism exclusively for this purpose, even with WinForms code. You have created a "TheUISync" singleton class that stores the synchronization, so clearly you have some way to hook into the top level of the application. However you are doing so, you can add code which creates adds some WPF content to your WinForms application so that <code>Dispatcher</code> will work, then use the new <code>Dispatcher</code> mechanism which I describe below.</p> <p><strong>Using Dispatcher instead of SynchronizationContext</strong></p> <p>WPF's <code>Dispatcher</code> mechanism actually eliminates the need for a separate <code>SynchronizationContext</code> object. Unless you have certain interop scenarios such sharing code with COM+ objects or WinForms UIs, your best solution is to use <code>Dispatcher</code> instead of <code>SynchronizationContext</code>.</p> <p>This looks like:</p> <pre><code>public class Foo { public event EventHandler FooDoDoneEvent; public void DoFoo() { //stuff OnFooDoDone(); } private void OnFooDoDone() { if(FooDoDoneEvent!=null) Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Normal, new Action(() =&gt; { FooDoDoneEvent(this, new EventArgs()); })); } } </code></pre> <p>Note that you no longer need a TheUISync object - WPF handles that detail for you.</p> <p>If you're more comfortable with the older <code>delegate</code> syntax you can do it that way instead:</p> <pre><code> Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Normal, new Action(delegate { FooDoDoneEvent(this, new EventArgs()); })); </code></pre> <p><strong>An unrelated bug to fix</strong></p> <p>Also note that there is a bug in your original code that is replicated here. The problem is that FooDoneEvent can be set to null between the time OnFooDoDone is called and the time the <code>BeginInvoke</code> (or <code>Post</code> in the original code) calls the delegate. The fix is a second test inside the delegate:</p> <pre><code> if(FooDoDoneEvent!=null) Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Normal, new Action(() =&gt; { if(FooDoDoneEvent!=null) FooDoDoneEvent(this, new EventArgs()); })); </code></pre>
 

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