C #, WPF, автоматически вызывать Dispatcher.Invoke при необходимости? - PullRequest
6 голосов
/ 06 февраля 2010

У меня есть программа со встроенной в нее картой геопространства.Обработка событий для карты обрабатывается в отдельном потоке, чтобы обеспечить отзывчивость карты (например, события, которые возникают при нажатии на карту).

Проблема, с которой я столкнулся, заключается в том, что карта запускает событиемоя программа должна обновить некоторые вещи в своем графическом интерфейсе, а также вызвать обратно карту, чтобы обработать размещение изображений на карте.

Я попытался обернуть весь метод обработчика событий в this.Dispatcher.Invoke, который помещаетя вернулся в основной поток пользовательского интерфейса.Это прекрасно работает для обновления моего графического интерфейса, но когда я перезваниваю на карту, я все еще нахожусь в потоке пользовательского интерфейса, который может вызвать некоторые проблемы на карте.

По сути, для того, чтобы это работало, ямне придется запускать 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
    }

Ответы [ 2 ]

6 голосов
/ 06 февраля 2010

Если вам нужно сохранить каждую соответствующую операцию в своем собственном контексте синхронизации, это, к сожалению, лучший подход. Вам нужно будет вызывать Dispatcher всякий раз, когда вы обновляете свой графический интерфейс.

Вот несколько советов, как сделать это проще:

  1. Попробуйте выполнить пакетную обработку вашего графического интерфейса. В дополнение к меньшему количеству кода (с помощью меньшего количества вызовов) вы получите лучшую производительность. Каждый вызов Dispatcher.Invoke несет значительные накладные расходы, поскольку он отправляет сообщение в очередь сообщений диспетчера, которое необходимо обработать.

  2. Подумайте об использовании Dispatcher.BeginInvoke, чтобы избежать блокировки, если только вам действительно не нужно ждать.

  3. Если вы можете использовать параллельную библиотеку задач из .NET 4 (или backport к 3.5sp1 в Rx Framework), вы можете рассмотреть возможность доработки этого для использования Экземпляры задач, синхронизированные с потоком GUI. . Создав TaskScheduler с помощью FromCurrentSynchronizationContext, вы можете запланировать выполнение задач в графическом интерфейсе проще, чем вызовы Dispatcher Invoke. Это также может дать вам некоторый контроль над пакетированием, так как вы можете легко планировать их и блокировать / ждать, когда это необходимо.

2 голосов
/ 06 февраля 2010

Вы можете использовать что-то вроде PostSharp или попытаться сузить обновления своего интерфейса до отдельных вызовов методов, когда вы вызываете один раз и делаете много. Или даже такой шаблон (это 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;
        }
    }
}
...