Windows Forms UI über einen anderen Thread erstellen?

2 Antworten

Also in WinForms ist der UI-Thread der einzige Thread, der das UI manipulieren darf.

Wenn du aus einem anderen Thread auf das UI zugreifen willst, musst du sicherstellen, dass das auf sichere Weise geschieht.

Da kannst du z.B. mit Invoke, bzw. BeginInvoke arbeiten.

In diesem Beispiel wird mithilfe eines Background-Workers gearbeitet:

 public partial class FormOne : Form
{
   private BackgroundWorker backgroundWorker;

   public FormOne()
   {
       InitializeComponent();

       this.Load += (sender, e) =>
       {
           backgroundWorker = new BackgroundWorker();
           backgroundWorker.DoWork += (senderBg, eBg) =>
           {
               for (int i = 0; i < 10; i++)
               {
                   string newText = $"Iteration {i + 1}";

                   // Auf das UI-Thread sicher zugreifen
                   this.Invoke((MethodInvoker)delegate
                   {
                       label1.Text = newText;
                   });

                   Thread.Sleep(1000);
               }
           };

           backgroundWorker.RunWorkerCompleted += (senderBg, eBg) =>
           {
               MessageBox.Show("Hintergrundarbeit abgeschlossen!");
           };

           backgroundWorker.RunWorkerAsync();
       };
   }
}

In diesem Beispiel habe ich den Load-Event-Handler verwendet, um sicherzustellen, dass das Fensterhandle erstellt wurde, bevor der BackgroundWorker gestartet wird.

Wenn du ohne einen Backgroundwoker arbeiten willst, kannst du das natürlich auch machen:

public partial class FormOne : Form
    {
        public FormOne()
        {
            InitializeComponent();

            Thread thread = new Thread(UpdateLabelInBackground);
            thread.Start();
        }

        private void UpdateLabelInBackground()
        {
            for (int i = 0; i < 10; i++)
            {
                string newText = $"Iteration {i + 1}";

                // Auf den UI-Thread sicher zugreifen
                this.BeginInvoke((MethodInvoker)delegate
                {
                    label1.Text = newText;
                });

                Thread.Sleep(1000);
            }

            this.BeginInvoke((MethodInvoker)delegate
            {
                MessageBox.Show("Hintergrundarbeit abgeschlossen!");
            });
        }
    }

An dieser Stelle müsstest du dann natürlich auch wieder schauen, dass es sich mit dem Load-Event verträgt. Aber so funktioniert das grundsätzlich mit dem Zugreifen auf UI-Elemente aus anderen Threads.

Woher ich das weiß:eigene Erfahrung – Leidenschaftlicher C# Entwickler und Foren Admin
Palladin007  11.12.2023, 23:11

Soweit korrekt, aber:

  • BackgroundWorker sind tot und sollten nicht (mehr) genutzt werden. Der Nachfolger sind die Tasks bzw. async/await. Beide Beispiele kann man auf eine asynchrone Arbeitsweise umschreiben, dann braucht es auch kein Invoke/BeginInvoke mehr, das passiert automatisch.
  • Mit Threads sollte man nur in Ausnahmefällen arbeiten, und wenn man einen guten Grund hat. Besser ist es, das Thread-Handling von den Tasks oder einer der Timer-Implementierungen abnehmen zu lassen, das macht einiges einfacher und Microsoft kann besser optimieren.
1

Der UI-Thread ist der einzige Thread, der UI-Komponenten beeinflussen darf, das gilt für alle mir bekannten UI-Frameworks im .NET-Umfeld. Dafür gibt's bei Windows Forms Control.Invoke oder Control.BeginInvoke.

Abgesehen davon:

Windows Forms ist stein alt und wird nur noch mehr schlecht als recht supportet, weil die Community sonst bei Microsoft auf's Dach steigt. In der .NET-Neuentwicklung sollte es ursprünglich auch gar nicht unterstützt werden, das haben sie erst nach viel Druck nachgerüstet. Oder anders gesagt: Windows Forms ist eine laufende Leiche. Wenn Du nicht aus anderen Gründen dazu gezwungen wirst, damit zu arbeiten, solltest Du dir etwas moderneres suchen ;) Z.B. WPF, das ist zwar ebenfalls sehr alt, aber um Welten moderner, alternativ gibt's noch MAUI oder aus der Community z.B. Avalonia.
Die tatsächliche Zukunft gehört aber dem Web, also entweder Du baust eine Webseite oder PWA (z.B. mit Blazor) und einem Server dahinter, oder eine Desktop-App, die intern Web-Technologien (Blazor) nutzt, mit MAUI, Electron (kann offiziell noch kein .NET 8) oder Photino.NET.

BackgroundWorker sind ebenso tot, die gibt's nur noch aus Kompatibilitätsgründen, und nicht, weil man sie nutzen sollte. Der Nachfolger ist async/await, das solltest Du stattdessen nutzen und alle mehr oder weniger modernen Frameworks (offiziell oder inoffiziell) nutzen das auch (zumindest sofern nötig).

Und nur sehr selten solltest Du direkt mit Threads arbeiten, am besten Du nutzt async/await, oder Du greifst auf eine der vielen Timer-Varianten zurück. Da steht am Ende trotzdem nur ein Thread im Hintergrund, allerdings wird das durchaus sehr komplexe Thread-Handling mit seinen ganzen Feinheiten für dich übernommen, damit Du dich nur noch um die Synchronisation in den UI-Thread beschäftigen musst. Dafür ist dann die Invoke-Methode da.

Woher ich das weiß:Berufserfahrung – C#.NET Senior Softwareentwickler