6

我有一个嵌入了地理空间地图的程序。地图的事件处理在单独的线程上处理以保持地图响应(例如,单击地图时触发的事件)。

我遇到的问题是当地图触发一个事件时,我的程序需要更新它的 gui 中的一些内容,并且还需要回调到地图中以处理在地图上放置图片。

我尝试将整个事件处理程序方法包装在 this.Dispatcher.Invoke 中,这让我回到了主 UI 线程。这对于更新我的 GUI 非常有用,但是当我回调到地图时,我仍然在 UI 线程上,这可能会导致地图中出现一些问题。

基本上,为了完成这项工作,每次我想更改我的 gui 上的控件时,我都必须运行 dispatcher.invoke。有没有一种方法可以自动执行此操作,而无需将每个调用都包装在 dispatcher.invoke 中?我希望这一切都有意义。

这是我正在谈论的事件的一些示例代码..

    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
    }
4

2 回答 2

6

如果您需要将每个相应的操作保留在自己的同步上下文中,那么不幸的是,这是最好的方法。每当您更新您的 GUI 时,您都需要使用 Dispatcher 调用。

以下是一些使这更容易的建议:

  1. 尝试批处理您的 GUI 操作。除了需要更少的代码(通过更少的调用调用)之外,您还将获得更好的性能。每个 Dispatcher.Invoke 调用都会带来相当多的开销,因为它会将消息发布到必须处理的 Dispatcher 的消息队列中。

  2. 考虑使用 Dispatcher.BeginInvoke 来避免阻塞,除非你真的需要等待。

  3. 如果您可以使用 .NET 4 中的任务并行库(或在 Rx 框架中向后移植到 3.5sp1),您可能需要考虑重新设计它以使用与 GUI 线程同步的任务实例。通过使用 FromCurrentSynchronizationContext 创建 TaskScheduler,您可以比 Dispatcher Invoke 调用更容易地安排任务在 GUI 上运行。这也可以让您对批处理进行一些控制,因为您可以非常轻松地安排它们,并根据需要阻止/等待。

于 2010-02-05T23:02:52.560 回答
2

您可以使用PostSharp 之类的东西,或者尝试将您的 UI 更新压缩到您调用一次并执行很多操作的单个方法调用中。甚至是这样的模式(它是 Winforms,但想法是一样的):

private void UpdateToolStripItemText(ToolStripItem toolStripItem, string text)
{
    if (InvokeRequired)
    {
        Invoke(new UpdateToolStripItemTextDelegate(UpdateToolStripItemText), new object[] { toolStripItem, text });
    }
    else
    {
        if (text != null)
        {
            toolStripItem.Text = text;
        }
    }
}
于 2010-02-05T23:05:25.720 回答