C#, WPF, Automatically call Dispatcher.Invoke when needed?
I have a program with a Geospace map embedded into it. The event handling for the map is handled on a separate thread to keep the map responsive (for example, the events that fire when th开发者_运维技巧e map is clicked).
The problem I am having is when the map fires an event, my program needs to update some things in it's gui, and also call back into the map to handle placing pictures on the map.
I tried wrapping the entire event handler method in this.Dispatcher.Invoke, which puts me back on the main UI thread. This works great for updating my GUI, but when i call back into the map, I'm still on the UI thread which can cause some problems in the map.
Basically, in order to make this work, I'm going to have to run dispatcher.invoke each time I want to change a control on my gui. Is there a way I can automatically do this without wrapping each call in dispatcher.invoke? I hope this all makes sense.
Heres some example code for the event I'm talking about..
private void Map_OnMapClicked(object sender, MapClickedEventArgs e)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
{
// Do something to update my gui
}));
Map.DoSomethingInTheMap();
this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
{
// Do something to update my gui
}));
//etc etc etc
}
If you need to keep each respective operation in its own synchronization context, this is unfortunately the best approach. You'll need to Invoke using the Dispatcher whenever you update your GUI.
Here are a couple of suggestions for making this easier:
Try to batch your GUI operations. In addition to requiring less code (via less invoke calls), you'll get better performance. Each Dispatcher.Invoke call carries a fair amount of overhead, since it posts a message into the Dispatcher's message queue which must be processed.
Consider using Dispatcher.BeginInvoke to avoid blocking, unless you really need to wait.
If you can use the Task Parallel Library from .NET 4 (or the backport to 3.5sp1 in the Rx Framework), you might want to consider reworking this to use Task instances synchronized to the GUI thread. By creating a TaskScheduler using FromCurrentSynchronizationContext, you can schedule tasks to run on the GUI easier than the Dispatcher Invoke calls. This also can give you some control over batching, since you can schedule them, and block/wait as needed, very easily.
You could use something like PostSharp or try to condense your UI updates to single method calls where you invoke once and do a lot. Or even a pattern like this (it's Winforms but the idea is the same):
private void UpdateToolStripItemText(ToolStripItem toolStripItem, string text)
{
if (InvokeRequired)
{
Invoke(new UpdateToolStripItemTextDelegate(UpdateToolStripItemText), new object[] { toolStripItem, text });
}
else
{
if (text != null)
{
toolStripItem.Text = text;
}
}
}
精彩评论