2010-02-05 8 views
6

Ich habe ein Programm mit einer eingebetteten Geospace-Map. Die Ereignisbehandlung für die Karte wird in einem separaten Thread behandelt, um die Reaktion der Karte zu steuern (z. B. die Ereignisse, die beim Klicken auf die Karte ausgelöst werden).C#, WPF, Dispatcher automatisch anrufen. Bei Bedarf anrufen?

Das Problem, das ich habe, ist, wenn die Karte ein Ereignis auslöst, mein Programm einige Dinge in seiner GUI aktualisieren muss, und auch zurück in die Karte rufen, um Bilder auf der Karte zu platzieren.

Ich habe versucht, die gesamte Event-Handler-Methode in this.Dispatcher.Invoke zu verpacken, was mich wieder in den Haupt-UI-Thread versetzt. Dies funktioniert hervorragend zum Aktualisieren meiner GUI, aber wenn ich in die Map zurückrufe, bin ich immer noch im UI-Thread, was einige Probleme in der Map verursachen kann.

Grundsätzlich, um diese Arbeit zu machen, muss ich Dispatcher.invoke jedes Mal, wenn ich ein Steuerelement auf meinem GUI ändern möchten. Gibt es eine Möglichkeit, dies automatisch zu tun, ohne jeden Anruf in dispatcher.invoke einzuwickeln? Ich hoffe, das macht alles Sinn.

Heres einig Beispiel-Code für den Fall, ich rede ..

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 
    } 

Antwort

6

Wenn Sie jede jeweilige Operation in seinem eigenen Synchronisationskontext halten müssen, ist dies leider der beste Ansatz. Sie müssen den Dispatcher immer dann aufrufen, wenn Sie Ihre GUI aktualisieren.

Hier sind ein paar Vorschläge für die Herstellung dieser einfacher:

  1. Operationen Batch Ihre GUI Versuchen. Zusätzlich zu weniger Code (über weniger Aufrufe) erhalten Sie eine bessere Leistung. Jeder Dispatcher.Invoke-Aufruf ist mit einem erheblichen Aufwand verbunden, da er eine Nachricht in die Nachrichtenwarteschlange des Dispatchers sendet, die verarbeitet werden muss.

  2. Verwenden Sie Dispatcher.BeginInvoke, um Blockierungen zu vermeiden, es sei denn, Sie müssen wirklich warten.

  3. Wenn Sie die Task Parallel Library von .NET 4 (oder den Backport zu 3.5sp1 im Rx Framework) verwenden können, sollten Sie in Betracht ziehen, dies zu bearbeiten, um Task instances synchronized to the GUI thread zu verwenden. Indem Sie einen TaskScheduler mit FromCurrentSynchronizationContext erstellen, können Sie Tasks so planen, dass sie auf der GUI einfacher ausgeführt werden als die Aufrufe von Dispatcher Invoke. Dies kann Ihnen auch eine gewisse Kontrolle über die Stapelverarbeitung geben, da Sie sie sehr einfach planen und warten können.

2

Man könnte so etwas wie PostSharp verwenden oder versuchen, Ihre UI-Updates zu kondensieren auf einzelne Methode aufruft, wo Sie einmal aufrufen und tun viel. Oder sogar ein Muster wie dieses (es ist Winforms, aber die Idee ist die gleiche):

private void UpdateToolStripItemText(ToolStripItem toolStripItem, string text) 
{ 
    if (InvokeRequired) 
    { 
     Invoke(new UpdateToolStripItemTextDelegate(UpdateToolStripItemText), new object[] { toolStripItem, text }); 
    } 
    else 
    { 
     if (text != null) 
     { 
      toolStripItem.Text = text; 
     } 
    } 
}