Tipps und Tricks

Frage (553) zu , WPF:

WPF Window in separatem Thread nutzen

Antwort:
Es wird von microsoft empfohlen, die Nutzeroberfläche im Main Thread zu belassen und Hintergrund-Threads für die "Arbeit" zu nutzen. Es kann aber erforderlich sein, WPF-Fenster auch in Hintergrund-Threads zu erzeugen und zu nutzen. Das aber richtig zu machen, kann kompliziert sein.

In der MSDN gab es dafür das folgende Beispiel, welches in den meisten Fällen auch funktioniert:

[c#]

// Create a thread
Thread newWindowThread = new Thread(new ThreadStart( () =>
{
    // Create and show the Window
    Window1 tempWindow = new Window1();
    tempWindow.Show();
    // Start the Dispatcher Processing
    System.Windows.Threading.Dispatcher.Run();
}));
// Set the apartment state
newWindowThread.SetApartmentState(ApartmentState.STA);
// Make the thread a background thread
newWindowThread.IsBackground = true;
// Start the thread
newWindowThread.Start();
[/c#]

Dieses Beispiel erzeugt einen Thread, markiert ihn als STA und startet den Dispatcher des Treads. Der Thread läuft kontinuirlich bist die Anwendung beendet wird. Das Problem dabei ist, das der genutzte ThreadStart delegate wird mit beendigung des Dispatchers beendet. Niemand stoppt aber den Dispatcher. Der Thread wurde als Hintergrund-Thread erzeugt, was ihm ermöglicht, solange zu arbeiten wie die Anwednung lebt. Der Dispatcher verarbeitet (pump) damit die dispatcher frames bis die Anwendung beendet wird. Um das zu ändern, ist Dispatcher.InvokeShutdown aufzurufen, wenn das fenster geschlossen wird.

[c#]
// Create a thread
Thread newWindowThread = new Thread(new ThreadStart( () =>
{
    Window1 tempWindow = new Window1();
    // When the window closes, shut down the dispatcher
    tempWindow.Closed += (s,e) => 
       Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);

    tempWindow.Show();
    // Start the Dispatcher Processing
    System.Windows.Threading.Dispatcher.Run();
}));
// Setup and start thread as before
[/c#]

Dieser Code arbeitet korrekt in den meisten Situationen. In Abhängigkeit von den in Window1 enthaltenen Funktionen kann es aber zu fehlerhaften Verhalten kommen. Das passiert in dem fall, wenn im Konstruktor von Window1 Code ausgeführt wird, der einen Context voraussetzt. Wenn beispielsweise der BackgroundWorker im Konstruktor gestartet wird oder ein TaskScheduler erzeugt wird mittels TaskScheduler.FromCurrentSynchronizationContext() bevor Dispatcher.Run ausgeführt wurde, dann gibt SynchronizationContext.Current ein null zurück, was zu unvohersehbarem Verhalten führen kann. Um das zu verhindern ist der SynchronizationContext zu setzen.

[c#]
// Create a thread
Thread newWindowThread = new Thread(new ThreadStart( () =>
{
    // Create our context, and install it:
    SynchronizationContext.SetSynchronizationContext(
        new DispatcherSynchronizationContext(
            Dispatcher.CurrentDispatcher));

    Window1 tempWindow = new Window1();
    // When the window closes, shut down the dispatcher
    tempWindow.Closed += (s,e) => 
       Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);

    tempWindow.Show();
    // Start the Dispatcher Processing
    System.Windows.Threading.Dispatcher.Run();
}));
// Setup and start thread as before
[/c#]

Stand des Beitrages: 02.02.16 06:56, zuletzt geändert: 02.02.16 07:23



Bitte wählen sie den Themenbereich aus
Bitte geben sie einen Suchbegriff ein

Die hier dargestellten Tipps und Tricks sind das Ergebnis selbst ersteller Lösungsvarianten, die für Projekte und Schulungen erarbeitet wurden.