Suche

über alle News und Events

 

Alle News

 

Wir haben während unseres...

Weiterlesen

Azure Functions benötigen oft einen...

Weiterlesen

In der heutigen Zeit nehmen Erreichbarkeit,...

Weiterlesen

Viele unserer Kunden überlegen derzeit, was sie in...

Weiterlesen

Lösungsansatz – was benötigt man dafür:

  • Einen...
Weiterlesen

Es gibt im Preview die Möglichkeit Azure Blob...

Weiterlesen

Die wichtigste Konferenz zu Integration auf der...

Weiterlesen

In diesem Blogeintrag werden wir drei sichere und...

Weiterlesen

Im vergangenen Jahr war bei uns in Hamburg viel in...

Weiterlesen

Heutzutage werden Token für die Authentifizierung...

Weiterlesen

How-to: Überführung von asynchronen Methodenrückgaben an den UI-Dispatcher-Thread in WPF unter Verwendung des MVVM Patterns

Ich habe kürzlich ein kleines Helper-Tool für einen Kunden in WPF geschrieben. Dafür habe ich eine Ansammlung von Design Patterns für so ein technisch konzipiertes Programm umgesetzt, also ein MVVM (https://de.wikipedia.org/wiki/Model_View_ViewModel ), Dependency Injection, etc..

Das Tool ist dafür konzipiert, ein mehrschichtiges Valuelookup im Cache und in der Datenbank dahinter anzeigen, bearbeiten und updaten zu können. Innerhalb des Programmes gab es diverse langlaufende (20-30 Sekunden) Prozesse, wie zum Beispiel das Einlesen der Mappingdaten aus Excel-Files. Wegen der großen Menge an Daten aus verschiedenen Dateien und der Operation, die auf diesen Daten angewandt werden musste, lies sich dieser Prozess leider nicht mehr weiter technisch optimieren. 20-30 Sekunden war das Schnellste, was sich hier rausholen ließ. Meine „UserExperience“ sank jedoch immens bei einem Programm, das für so einen langen Zeitraum einfriert. Gerade dem geschuldet, dass einem nie klar war, ob sich die Oberfläche auf gehangen hat oder hinten dran wirklich noch gearbeitet wurde. Wie also damit umgehen? Aus Informatiker Sicht ganz einfach: Asynchrone Programmierung. 

Gesagt, getan: Die einzelnen Methoden über Tasks separat loslaufen lassen und über Events wieder zurück an meinen Main Thread zurück delegieren. In einem reinen C# Projekt wäre dies auch überhaupt kein Problem gewesen. In WPF mit MVVM Pattern sieht dies jedoch etwas anders aus. Daten, die an die Anzeige über Databinding gebunden sind, können nur von dem Main-(UI)-Thread verändert werden. Ich musste also eine Lösung finden, die Daten an meinen Hauptprozess zurückzugeben, der diese geänderten Daten dann schließlich einpflegt. Nach einer kurzen Recherche war klar, wie sich dies bewerkstelligen lässt: Eine in WPF geschriebene View hat einen sogenannten Dispatcherthread, über den sich Daten wieder an den Mainthread zurückgeben lassen. (Technisch kann man diesen Dispatcher-Instanz als Property der View wiederfinden: https://msdn.microsoft.com/de-de/library/system.windows.threading.dispatcher(v=vs.110).aspx)

So einfach diese Lösung jedoch scheint, meine Design Patterns machten mir auch hier einen Strich durch die Rechnung: Im MVVM Pattern wissen View(Anzeige) und ViewModel (Datenbearbeitung und -Speicherung) nichts voneinander. Wie also damit umgehen ohne das schöne Design Pattern zu brechen? Nach einiger Zeit und verzweifelten, nicht implementierbaren Lösungen von StackOverFlow, ergab sich die Lösung für mein Problem:

 

Technisch gesehen, muss es eine kleine Stelle im Programm geben, die die View erstellt und der View das Viewmodel als Datacontext übergibt. Dies ist die einzige Möglichkeit eine Verbindung zwischen beiden Komponenten herstellen zu können. In meinem Programm habe ich es so konzipiert, dass ich eine Singleton-Instanz eines, wie ich es nenne, Core-Operators habe, der mir in einer aufgerufenen Methode über Dependency Injection die benötigten Komponenten wie View und Viewmodel erstellt/besorgt, diese verheiratet und letztendlich startet. In dieser Methode mussten wir also beim Aufruf unsere Rückverbindung dauerhaft herstellen, umso später Daten an unsere View zurückgeben zu können, die diese dann wieder an unser ausgeführtes Viewmodel delegiert. Das richtige Stichwort: Delegate! Wir müssen also in unserem asynchronen Prozesszweig den auszuführenden Code über einen Delegaten an unseren Dispatcher Thread weitergeben, der diesen Code dann in unserem Main-UI-Thread ausführt. 

Technisch gesehen sieht das dann so aus:

 

-          Als erstes setzen wir in unserem Viewmodel einen Action-Platzhalter vom Typ Action.

               public Action<Action> UIAction { get; set; }

 

-          In unserer Erstellenden-Methode des “CoreOperators” müssen wir uns nun die Instanz des Dispatchers besorgen.

 

public void CreateMainView()

{

var viewModel = MwContainer.CreateMainViewModel();

_mainView = new MainView {DataContext = viewModel};

var instance = _mainView.Dispatcher;

 

-          Als letztes müssen wir unsere „UI-Action“ aus Schritt 1 an unseren Dispatcher übergeben und die Aktion dort wieder ausführen.

 

viewModel.UIAction = ((uiAction) => instance.BeginInvoke(new Action(() => { uiAction(); })));

            _mainView.Show();

        }

 

In diesem letzten Schritt definieren wir, dass für jede Action, die wir auf unsere „UIAction“ legen, genau diese im Dispatcher als Aktion angelegt und ausgeführt wird. 

 

Et Voila!  

Nun können wir fast jeden erdenklichen Code, den wir in unserem Haupt-Viewmodel ausführen könnten, einfach an unsere UI-Action weiterleiten und so ansynchrone „Returns“ wieder in unseren Hauptthread zurückbekommen.

 

UIAction(() =>

{

             SourceItem = backupEventArgs.Source;

             StatusReturnMessage = String.Format("New DB Setting successfully uploaded to {0}!", backupEventArgs.Source); 

});

 

Ich hoffe der Artikel kann euch vielleicht weiterhelfen und ihr hattet Spaß beim Lesen.

 

Benedikt Oettle

Developer QUIBIQ GmbH 

Ihre Kontaktmöglichkeiten

Sie haben eine konkrete Frage an uns


 

Bleiben Sie immer auf dem Laufenden


 

Mit meinem "Ja" erkläre ich mich mit der Verarbeitung meiner Daten zur Zusendung von Informationen einverstanden. Ich weiß, dass ich diese Erklärung jederzeit durch einfache Mitteilung widerrufen kann. Bei einem Nein an dieser Stelle erhalte ich zukünftig keine Informationen mehr.

© QUIBIQ GmbH · Impressum · Datenschutz