it-swarm.com.de

Wie aktualisiere ich die GUI von einem anderen Thread?

Was ist der einfachste Weg, um ein Label von einem anderen Thread zu aktualisieren?

Ich habe ein Form auf thread1, und von diesem beginne ich einen anderen Thread (thread2). Während thread2 einige Dateien verarbeitet, möchte ich ein Label auf dem Form mit dem aktuellen Status von thread2 aktualisieren.

Wie kann ich das machen?

1304
CruelIO

Für .NET 2.0 ist hier ein netter Code, den ich geschrieben habe, der genau das tut, was Sie wollen, und der für jede Eigenschaft auf einem Control funktioniert:

private delegate void SetControlPropertyThreadSafeDelegate(
    Control control, 
    string propertyName, 
    object propertyValue);

public static void SetControlPropertyThreadSafe(
    Control control, 
    string propertyName, 
    object propertyValue)
{
  if (control.InvokeRequired)
  {
    control.Invoke(new SetControlPropertyThreadSafeDelegate               
    (SetControlPropertyThreadSafe), 
    new object[] { control, propertyName, propertyValue });
  }
  else
  {
    control.GetType().InvokeMember(
        propertyName, 
        BindingFlags.SetProperty, 
        null, 
        control, 
        new object[] { propertyValue });
  }
}

Nenne es so:

// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);

Wenn Sie .NET 3.0 oder höher verwenden, können Sie die obige Methode als Erweiterungsmethode der Klasse Control umschreiben, wodurch der Aufruf von:

myLabel.SetPropertyThreadSafe("Text", status);

PDATE 05/10/2010:

Für .NET 3.0 sollten Sie diesen Code verwenden:

private delegate void SetPropertyThreadSafeDelegate<TResult>(
    Control @this, 
    Expression<Func<TResult>> property, 
    TResult value);

public static void SetPropertyThreadSafe<TResult>(
    this Control @this, 
    Expression<Func<TResult>> property, 
    TResult value)
{
  var propertyInfo = (property.Body as MemberExpression).Member 
      as PropertyInfo;

  if (propertyInfo == null ||
      [email protected]().IsSubclassOf(propertyInfo.ReflectedType) ||
      @this.GetType().GetProperty(
          propertyInfo.Name, 
          propertyInfo.PropertyType) == null)
  {
    throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
  }

  if (@this.InvokeRequired)
  {
      @this.Invoke(new SetPropertyThreadSafeDelegate<TResult> 
      (SetPropertyThreadSafe), 
      new object[] { @this, property, value });
  }
  else
  {
      @this.GetType().InvokeMember(
          propertyInfo.Name, 
          BindingFlags.SetProperty, 
          null, 
          @this, 
          new object[] { value });
  }
}

die LINQ- und Lambda-Ausdrücke ermöglichen eine viel sauberere, einfachere und sicherere Syntax:

myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile

Der Eigenschaftsname wird jetzt nicht nur zur Kompilierungszeit überprüft, sondern auch der Typ der Eigenschaft. Daher ist es (beispielsweise) unmöglich, einer booleschen Eigenschaft einen Zeichenfolgewert zuzuweisen, was zu einer Laufzeitausnahme führt.

Leider hindert dies niemanden daran, dumme Dinge zu tun, wie die Weitergabe der Eigenschaft und des Werts eines anderen Control, sodass Folgendes problemlos kompiliert werden kann:

myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);

Daher habe ich die Laufzeitprüfungen hinzugefügt, um sicherzustellen, dass die übergebene Eigenschaft tatsächlich zu der Control gehört, auf die die Methode zugreift. Nicht perfekt, aber immer noch viel besser als die .NET 2.0-Version.

Wenn jemand weitere Vorschläge zur Verbesserung dieses Codes zur Sicherheit beim Kompilieren hat, kommentieren Sie dies bitte!

751
Ian Kemp

Der einfachste Weg ist eine anonyme Methode, die an Label.Invoke übergeben wird:

_// Running on the worker thread
string newText = "abc";
form.Label.Invoke((MethodInvoker)delegate {
    // Running on the UI thread
    form.Label.Text = newText;
});
// Back on the worker thread
_

Beachten Sie, dass Invoke die Ausführung blockiert, bis sie abgeschlossen ist - dies ist synchroner Code. Die Frage stellt keine Fragen zu asynchronem Code, aber es gibt eine Menge Inhalt in Stack Overflow zum Schreiben von asynchronem Code, wenn Sie mehr darüber erfahren möchten.

1033
Marc Gravell

Lange Arbeit erledigen

Seit . NET 4.5 und C # 5. sollten Sie aufgabenbasiertes asynchrones Muster (TAP) verwenden zusammen mit async - wait Schlüsselwörter in allen Bereichen (einschließlich der GUI ):

TAP ist das empfohlene asynchrone Entwurfsmuster für die Neuentwicklung

anstelle von Asynchronous Programming Model (APM) und Event-based Asynchronous Pattern (EAP) (letzteres enthält die BackgroundWorker Class ).

Dann lautet die empfohlene Lösung für die Neuentwicklung:

  1. Asynchrone Implementierung eines Event-Handlers (Ja, das ist alles):

    private async void Button_Clicked(object sender, EventArgs e)
    {
        var progress = new Progress<string>(s => label.Text = s);
        await Task.Factory.StartNew(() => SecondThreadConcern.LongWork(progress),
                                    TaskCreationOptions.LongRunning);
        label.Text = "completed";
    }
    
  2. Implementierung des zweiten Threads, der den UI-Thread benachrichtigt:

    class SecondThreadConcern
    {
        public static void LongWork(IProgress<string> progress)
        {
            // Perform a long running work...
            for (var i = 0; i < 10; i++)
            {
                Task.Delay(500).Wait();
                progress.Report(i.ToString());
            }
        }
    }
    

Beachten Sie Folgendes:

  1. Kurzer und sauberer Code, der sequentiell ohne Rückrufe und explizite Threads geschrieben wird.
  2. Task statt Thread .
  3. async Schlüsselwort, mit dem await verwendet werden kann, um zu verhindern, dass der Ereignishandler den Beendigungsstatus erreicht, bis die Aufgabe abgeschlossen ist, und in der Zwischenzeit den UI-Thread nicht blockiert.
  4. Fortschrittsklasse (siehe IProgress Interface ), die Separation of Concerns (SoC) unterstützt und keinen expliziten Dispatcher und Aufruf erfordert. Es verwendet das aktuelle SynchronizationContext von seinem Erstellungsort (hier der UI-Thread).
  5. TaskCreationOptions.LongRunning Dies weist darauf hin, dass die Aufgabe nicht in ThreadPool eingereiht werden soll.

Ausführlichere Beispiele finden Sie unter: Die Zukunft von C #: Gute Dinge kommen für diejenigen, die auf sie warten von Joseph Albahari .

Siehe auch I Threading Model Konzept.

Ausnahmen behandeln

Das folgende Snippet ist ein Beispiel für die Behandlung von Ausnahmen und das Umschalten der Enabled -Eigenschaft der Schaltfläche, um Mehrfachklicks während der Ausführung im Hintergrund zu verhindern.

private async void Button_Click(object sender, EventArgs e)
{
    button.Enabled = false;

    try
    {
        var progress = new Progress<string>(s => button.Text = s);
        await Task.Run(() => SecondThreadConcern.FailingWork(progress));
        button.Text = "Completed";
    }
    catch(Exception exception)
    {
        button.Text = "Failed: " + exception.Message;
    }

    button.Enabled = true;
}

class SecondThreadConcern
{
    public static void FailingWork(IProgress<string> progress)
    {
        progress.Report("I will fail in...");
        Task.Delay(500).Wait();

        for (var i = 0; i < 3; i++)
        {
            progress.Report((3 - i).ToString());
            Task.Delay(500).Wait();
        }

        throw new Exception("Oops...");
    }
}
379
Ryszard Dżegan

Variante von Marc Gravells einfachste Lösung für .NET 4:

control.Invoke((MethodInvoker) (() => control.Text = "new text"));

Oder verwenden Sie stattdessen Action delegate:

control.Invoke(new Action(() => control.Text = "new text"));

Siehe hier für einen Vergleich der beiden: MethodInvoker vs Action for Control.BeginInvoke

227
Zaid Masud

Fire and Forget-Erweiterungsmethode für .NET 3.5+

using System;
using System.Windows.Forms;

public static class ControlExtensions
{
    /// <summary>
    /// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread.
    /// </summary>
    /// <param name="control"></param>
    /// <param name="code"></param>
    public static void UIThread(this Control @this, Action code)
    {
        if (@this.InvokeRequired)
        {
            @this.BeginInvoke(code);
        }
        else
        {
            code.Invoke();
        }
    }
}

Dies kann mit der folgenden Codezeile aufgerufen werden:

this.UIThread(() => this.myLabel.Text = "Text Goes Here");
133
StyxRiver

Dies ist die klassische Vorgehensweise:

using System;
using System.Windows.Forms;
using System.Threading;

namespace Test
{
    public partial class UIThread : Form
    {
        Worker worker;

        Thread workerThread;

        public UIThread()
        {
            InitializeComponent();

            worker = new Worker();
            worker.ProgressChanged += new EventHandler<ProgressChangedArgs>(OnWorkerProgressChanged);
            workerThread = new Thread(new ThreadStart(worker.StartWork));
            workerThread.Start();
        }

        private void OnWorkerProgressChanged(object sender, ProgressChangedArgs e)
        {
            // Cross thread - so you don't get the cross-threading exception
            if (this.InvokeRequired)
            {
                this.BeginInvoke((MethodInvoker)delegate
                {
                    OnWorkerProgressChanged(sender, e);
                });
                return;
            }

            // Change control
            this.label1.Text = e.Progress;
        }
    }

    public class Worker
    {
        public event EventHandler<ProgressChangedArgs> ProgressChanged;

        protected void OnProgressChanged(ProgressChangedArgs e)
        {
            if(ProgressChanged!=null)
            {
                ProgressChanged(this,e);
            }
        }

        public void StartWork()
        {
            Thread.Sleep(100);
            OnProgressChanged(new ProgressChangedArgs("Progress Changed"));
            Thread.Sleep(100);
        }
    }


    public class ProgressChangedArgs : EventArgs
    {
        public string Progress {get;private set;}
        public ProgressChangedArgs(string progress)
        {
            Progress = progress;
        }
    }
}

Ihr Arbeitsthread hat ein Ereignis. Ihr UI-Thread startet einen anderen Thread, um die Arbeit zu erledigen, und verknüpft dieses Worker-Ereignis, damit Sie den Status des Worker-Threads anzeigen können.

Dann müssen Sie in der Benutzeroberfläche Threads kreuzen, um das tatsächliche Steuerelement zu ändern ... wie z. B. eine Beschriftung oder eine Fortschrittsanzeige.

65
Hath

Die einfache Lösung ist die Verwendung von Control.Invoke.

void DoSomething()
{
    if (InvokeRequired) {
        Invoke(new MethodInvoker(updateGUI));
    } else {
        // Do Something
        updateGUI();
    }
}

void updateGUI() {
    // update gui here
}
60
OregonGhost

Threading-Code ist oft fehlerhaft und immer schwer zu testen. Sie müssen keinen Threading-Code schreiben, um die Benutzeroberfläche von einer Hintergrundaufgabe aus zu aktualisieren. Verwenden Sie einfach die Klasse BackgroundWorker , um die Aufgabe auszuführen, und die Methode ReportProgress , um die Benutzeroberfläche zu aktualisieren. Normalerweise melden Sie nur einen vollständigen Prozentsatz, aber es gibt eine weitere Überladung, die ein Statusobjekt enthält. Hier ist ein Beispiel, das nur ein String-Objekt meldet:

    private void button1_Click(object sender, EventArgs e)
    {
        backgroundWorker1.WorkerReportsProgress = true;
        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(5000);
        backgroundWorker1.ReportProgress(0, "A");
        Thread.Sleep(5000);
        backgroundWorker1.ReportProgress(0, "B");
        Thread.Sleep(5000);
        backgroundWorker1.ReportProgress(0, "C");
    }

    private void backgroundWorker1_ProgressChanged(
        object sender, 
        ProgressChangedEventArgs e)
    {
        label1.Text = e.UserState.ToString();
    }

Das ist in Ordnung, wenn Sie immer das gleiche Feld aktualisieren möchten. Wenn Sie kompliziertere Aktualisierungen vornehmen müssen, können Sie eine Klasse definieren, die den Status der Benutzeroberfläche darstellt, und diese an die ReportProgress-Methode übergeben.

Als letztes muss das Flag WorkerReportsProgress gesetzt werden, da andernfalls die Methode ReportProgress vollständig ignoriert wird.

46
Don Kirkby

Die überwiegende Mehrheit der Antworten verwendet Control.Invoke, was ein Rennbedingung, die darauf wartet, passiert zu werden ist. Betrachten Sie zum Beispiel die akzeptierte Antwort:

string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate { 
    someLabel.Text = newText; // runs on UI thread
});

Wenn der Benutzer das Formular schließt, kurz bevor this.Invoke aufgerufen wird (denken Sie daran, dass this das Form -Objekt ist), wird wahrscheinlich ein ObjectDisposedException ausgelöst.

Die Lösung besteht darin, SynchronizationContext zu verwenden, insbesondere SynchronizationContext.Current, wie hamilton.danielb vorschlägt (andere Antworten beruhen auf spezifischen SynchronizationContext -Implementierungen, die völlig unnötig sind). Ich würde seinen Code leicht modifizieren, um SynchronizationContext.Post anstelle von SynchronizationContext.Send zu verwenden (da der Worker-Thread normalerweise nicht warten muss):

public partial class MyForm : Form
{
    private readonly SynchronizationContext _context;
    public MyForm()
    {
        _context = SynchronizationContext.Current
        ...
    }

    private MethodOnOtherThread()
    {
         ...
         _context.Post(status => someLabel.Text = newText,null);
    }
}

Beachten Sie, dass Sie unter .NET 4.0 und höher eigentlich Aufgaben für asynchrone Vorgänge verwenden sollten. Siehe n-san's Antwort für den entsprechenden aufgabenbasierten Ansatz (mit TaskScheduler.FromCurrentSynchronizationContext).

Ab .NET 4.5 können Sie auch Progress<T> (das im Grunde genommen SynchronizationContext.Current bei seiner Erstellung erfasst) verwenden, wie durch Ryszard Dżegan's für Fälle demonstriert wird, in denen die Operation lange andauert muss UI-Code ausführen, während Sie noch arbeiten.

41
Ohad Schneider

Sie müssen sicherstellen, dass das Update im richtigen Thread ausgeführt wird. der UI-Thread.

Dazu müssen Sie den Event-Handler aufrufen, anstatt ihn direkt aufzurufen.

Sie können dies tun, indem Sie Ihr Ereignis folgendermaßen auslösen:

(Der Code wurde hier aus meinem Kopf heraus eingegeben, daher habe ich nicht die korrekte Syntax usw. überprüft, aber er sollte Sie zum Laufen bringen.)

if( MyEvent != null )
{
   Delegate[] eventHandlers = MyEvent.GetInvocationList();

   foreach( Delegate d in eventHandlers )
   {
      // Check whether the target of the delegate implements 
      // ISynchronizeInvoke (Winforms controls do), and see
      // if a context-switch is required.
      ISynchronizeInvoke target = d.Target as ISynchronizeInvoke;

      if( target != null && target.InvokeRequired )
      {
         target.Invoke (d, ... );
      }
      else
      {
          d.DynamicInvoke ( ... );
      }
   }
}

Beachten Sie, dass der obige Code in WPF-Projekten nicht funktioniert, da WPF-Steuerelemente die Schnittstelle ISynchronizeInvoke nicht implementieren.

Um sicherzustellen, dass der obige Code mit Windows Forms und WPF sowie allen anderen Plattformen funktioniert, können Sie sich die Klassen AsyncOperation, AsyncOperationManager und SynchronizationContext ansehen.

Um Ereignisse auf diese Weise einfach auszulösen, habe ich eine Erweiterungsmethode erstellt, mit der ich das Auslösen eines Ereignisses vereinfachen kann, indem ich einfach Folgendes aufrufe:

MyEvent.Raise(this, EventArgs.Empty);

Natürlich können Sie auch die BackGroundWorker-Klasse verwenden, die diese Angelegenheit für Sie abstrahiert.

35

Sie müssen die Methode im GUI-Thread aufrufen. Sie können dies tun, indem Sie Control.Invoke aufrufen.

Zum Beispiel:

delegate void UpdateLabelDelegate (string message);

void UpdateLabel (string message)
{
    if (InvokeRequired)
    {
         Invoke (new UpdateLabelDelegate (UpdateLabel), message);
         return;
    }

    MyLabelControl.Text = message;
}
29
Kieron

Wegen der Trivialität des Szenarios hätte ich tatsächlich die UI-Thread-Abfrage für den Status. Ich denke, Sie werden feststellen, dass es sehr elegant sein kann.

public class MyForm : Form
{
  private volatile string m_Text = "";
  private System.Timers.Timer m_Timer;

  private MyForm()
  {
    m_Timer = new System.Timers.Timer();
    m_Timer.SynchronizingObject = this;
    m_Timer.Interval = 1000;
    m_Timer.Elapsed += (s, a) => { MyProgressLabel.Text = m_Text; };
    m_Timer.Start();
    var thread = new Thread(WorkerThread);
    thread.Start();
  }

  private void WorkerThread()
  {
    while (...)
    {
      // Periodically publish progress information.
      m_Text = "Still working...";
    }
  }
}

Der Ansatz vermeidet die Marshalling-Operation, die bei Verwendung der Methoden ISynchronizeInvoke.Invoke und ISynchronizeInvoke.BeginInvoke erforderlich ist. Es ist nichts Falsches an der Verwendung der Marshalling-Technik, aber es gibt ein paar Vorsichtsmaßnahmen, die Sie beachten müssen.

  • Stellen Sie sicher, dass Sie BeginInvoke nicht zu häufig aufrufen, da sonst die Meldungspumpe überlaufen könnte.
  • Das Aufrufen von Invoke im Worker-Thread ist ein blockierender Aufruf. Die in diesem Thread ausgeführte Arbeit wird vorübergehend angehalten.

Die Strategie, die ich in dieser Antwort vorschlage, kehrt die Kommunikationsrollen der Threads um. Anstelle des Worker-Threads, der die Daten pusht, fragt der UI-Thread danach ab. Dies ist ein häufiges Muster, das in vielen Szenarien verwendet wird. Da Sie lediglich Fortschrittsinformationen aus dem Worker-Thread anzeigen möchten, werden Sie feststellen, dass diese Lösung eine großartige Alternative zur Marshalling-Lösung darstellt. Es hat die folgenden Vorteile.

  • Die Benutzeroberfläche und die Worker-Threads bleiben im Gegensatz zu dem Ansatz Control.Invoke oder Control.BeginInvoke, mit dem sie eng gekoppelt werden, lose miteinander verbunden.
  • Der Benutzeroberflächenthread wird den Fortschritt des Arbeitsthreads nicht behindern.
  • Der Arbeitsthread kann die Zeit, die der UI-Thread für die Aktualisierung benötigt, nicht dominieren.
  • Die Intervalle, in denen die Benutzeroberfläche und die Arbeitsthreads Vorgänge ausführen, können unabhängig bleiben.
  • Der Arbeitsthread kann die Nachrichtenpumpe des UI-Threads nicht überlaufen.
  • Der Benutzeroberflächen-Thread kann bestimmen, wann und wie oft die Benutzeroberfläche aktualisiert wird.
28
Brian Gideon

Keines der Invoke-Elemente in den vorherigen Antworten ist erforderlich.

Sie müssen sich WindowsFormsSynchronizationContext ansehen:

// In the main thread
WindowsFormsSynchronizationContext mUiContext = new WindowsFormsSynchronizationContext();

...

// In some non-UI Thread

// Causes an update in the GUI thread.
mUiContext.Post(UpdateGUI, userData);

...

void UpdateGUI(object userData)
{
    // Update your GUI controls here
}
27
Jon H

Diese ähnelt der obigen Lösung mit .NET Framework 3.0, hat jedoch das Problem Sicherheitsunterstützung zur Kompilierungszeit gelöst.

public  static class ControlExtension
{
    delegate void SetPropertyValueHandler<TResult>(Control souce, Expression<Func<Control, TResult>> selector, TResult value);

    public static void SetPropertyValue<TResult>(this Control source, Expression<Func<Control, TResult>> selector, TResult value)
    {
        if (source.InvokeRequired)
        {
            var del = new SetPropertyValueHandler<TResult>(SetPropertyValue);
            source.Invoke(del, new object[]{ source, selector, value});
        }
        else
        {
            var propInfo = ((MemberExpression)selector.Body).Member as PropertyInfo;
            propInfo.SetValue(source, value, null);
        }
    }
}

Benutzen:

this.lblTimeDisplay.SetPropertyValue(a => a.Text, "some string");
this.lblTimeDisplay.SetPropertyValue(a => a.Visible, false);

Der Compiler schlägt fehl, wenn der Benutzer den falschen Datentyp übergibt.

this.lblTimeDisplay.SetPropertyValue(a => a.Visible, "sometext");
23
Francis

Salvete! Nachdem ich nach dieser Frage gesucht hatte, fand ich die Antworten von FrankG und Oregon Ghost , um mir am einfachsten zu helfen. Jetzt habe ich in Visual Basic programmiert und dieses Snippet über einen Konverter ausgeführt. Ich bin mir also nicht ganz sicher, wie es ausgeht.

Ich habe ein Dialogfeld mit dem Namen form_Diagnostics,, das ein Richtungsfeld mit dem Namen updateDiagWindow, enthält, das ich als eine Art Protokollanzeige verwende. Ich musste in der Lage sein, seinen Text aus allen Threads zu aktualisieren. Die zusätzlichen Zeilen ermöglichen es dem Fenster, automatisch zu den neuesten Zeilen zu scrollen.

Und so kann ich jetzt die Anzeige mit einer Zeile von überall im gesamten Programm so aktualisieren, wie Sie denken, dass es ohne Threading funktionieren würde:

  form_Diagnostics.updateDiagWindow(whatmessage);

Hauptcode (fügen Sie diesen in den Klassencode Ihres Formulars ein):

#region "---------Update Diag Window Text------------------------------------"
// This sub allows the diag window to be updated by all threads
public void updateDiagWindow(string whatmessage)
{
    var _with1 = diagwindow;
    if (_with1.InvokeRequired) {
        _with1.Invoke(new UpdateDiagDelegate(UpdateDiag), whatmessage);
    } else {
        UpdateDiag(whatmessage);
    }
}
// This next line makes the private UpdateDiagWindow available to all threads
private delegate void UpdateDiagDelegate(string whatmessage);
private void UpdateDiag(string whatmessage)
{
    var _with2 = diagwindow;
    _with2.appendtext(whatmessage);
    _with2.SelectionStart = _with2.Text.Length;
    _with2.ScrollToCaret();
}
#endregion
23
bgmCoder
Label lblText; //initialized elsewhere

void AssignLabel(string text)
{
   if (InvokeRequired)
   {
      BeginInvoke((Action<string>)AssignLabel, text);
      return;
   }

   lblText.Text = text;           
}

Beachten Sie, dass BeginInvoke()Invoke() vorgezogen wird, da es weniger wahrscheinlich ist, dass Deadlocks auftreten (dies ist hier jedoch kein Problem, wenn nur Text einer Beschriftung zugewiesen wird):

Wenn Sie Invoke() verwenden, warten Sie auf die Rückkehr der Methode. Nun kann es sein, dass Sie im aufgerufenen Code etwas tun, das auf den Thread warten muss. Dies ist möglicherweise nicht sofort ersichtlich, wenn er in einige von Ihnen aufgerufene Funktionen eingebettet ist. Dies kann indirekt über Event-Handler geschehen. Sie würden also auf den Thread warten, der Thread würde auf Sie warten und Sie sind festgefahren.

Dies führte tatsächlich dazu, dass einige unserer veröffentlichten Programme hängen blieben. Es war leicht zu beheben, indem Invoke() durch BeginInvoke() ersetzt wurde. Verwenden Sie BeginInvoke(), es sei denn, Sie benötigen einen synchronen Betrieb, was bei einem Rückgabewert der Fall sein kann.

21
ILoveFortran

Dies in meiner C # 3.0-Variante von Ian Kemps Lösung:

public static void SetPropertyInGuiThread<C,V>(this C control, Expression<Func<C, V>> property, V value) where C : Control
{
    var memberExpression = property.Body as MemberExpression;
    if (memberExpression == null)
        throw new ArgumentException("The 'property' expression must specify a property on the control.");

    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (propertyInfo == null)
        throw new ArgumentException("The 'property' expression must specify a property on the control.");

    if (control.InvokeRequired)
        control.Invoke(
            (Action<C, Expression<Func<C, V>>, V>)SetPropertyInGuiThread,
            new object[] { control, property, value }
        );
    else
        propertyInfo.SetValue(control, value, null);
}

Du nennst es so:

myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!")
  1. Es fügt dem Ergebnis der "as MemberExpression" eine Nullprüfung hinzu.
  2. Es verbessert die statische Typensicherheit.

Ansonsten ist das Original eine sehr schöne Lösung.

21
Rotaerk

Für viele Zwecke ist es so einfach:

public delegate void serviceGUIDelegate();
private void updateGUI()
{
  this.Invoke(new serviceGUIDelegate(serviceGUI));
}

"serviceGUI ()" ist eine Methode auf GUI-Ebene innerhalb des Formulars (dies), mit der beliebig viele Steuerelemente geändert werden können. Rufe "updateGUI ()" aus dem anderen Thread auf. Parameter können hinzugefügt werden, um Werte zu übergeben, oder (wahrscheinlich schneller) Klassenbereichsvariablen mit Sperren verwenden, wenn die Möglichkeit eines Konflikts zwischen Threads, die auf sie zugreifen, besteht, der zu Instabilität führen kann. Verwenden Sie BeginInvoke anstelle von Invoke, wenn der Nicht-GUI-Thread zeitkritisch ist (unter Berücksichtigung der Warnung von Brian Gideon).

21
Frankg

Als ich auf dasselbe Problem stieß, bat ich Google um Hilfe, aber anstatt mir eine einfache Lösung zu geben, verwirrte es mich mehr, indem es Beispiele für MethodInvoker und bla bla bla gab. Also habe ich beschlossen, es selbst zu lösen. Hier ist meine Lösung:

Machen Sie einen Delegierten wie folgt aus:

Public delegate void LabelDelegate(string s);

void Updatelabel(string text)
{
   if (label.InvokeRequired)
   {
       LabelDelegate LDEL = new LabelDelegate(Updatelabel);
       label.Invoke(LDEL, text);
   }
   else
       label.Text = text
}

Sie können diese Funktion in einem neuen Thread wie diesem aufrufen

Thread th = new Thread(() => Updatelabel("Hello World"));
th.start();

Seien Sie nicht verwechselt mit Thread(() => .....). Ich verwende eine anonyme Funktion oder einen Lambda-Ausdruck, wenn ich an einem Thread arbeite. Um die Codezeilen zu reduzieren, können Sie auch die Methode ThreadStart(..) verwenden, die ich hier nicht erläutern soll.

20
ahmar

Benutze einfach so etwas:

 this.Invoke((MethodInvoker)delegate
            {
                progressBar1.Value = e.ProgressPercentage; // runs on UI thread
            });
17
Hassan Shouman

Sie können den bereits vorhandenen Delegaten Action verwenden:

private void UpdateMethod()
{
    if (InvokeRequired)
    {
        Invoke(new Action(UpdateMethod));
    }
}
15
Embedd_Khurja

Meine Version ist es, eine Zeile des rekursiven "Mantras" einzufügen:

Für keine Argumente:

    void Aaaaaaa()
    {
        if (InvokeRequired) { Invoke(new Action(Aaaaaaa)); return; } //1 line of mantra

        // Your code!
    }

Für eine Funktion mit Argumenten:

    void Bbb(int x, string text)
    {
        if (InvokeRequired) { Invoke(new Action<int, string>(Bbb), new[] { x, text }); return; }
        // Your code!
    }

DAS IST ES.


Einige Argumente: Normalerweise ist es für die Lesbarkeit des Codes schlecht, {} nach einer if () -Anweisung in eine Zeile zu setzen. Aber in diesem Fall handelt es sich um ein alltägliches "Mantra". Die Lesbarkeit des Codes wird nicht beeinträchtigt, wenn diese Methode über das Projekt hinweg konsistent ist. Und es schützt Ihren Code vor Müll (eine Codezeile anstelle von fünf).

Wie Sie if(InvokeRequired) {something long} sehen, wissen Sie nur, dass "diese Funktion sicher von einem anderen Thread aufgerufen werden kann".

14
MajesticRa

Versuchen Sie, das Etikett auf diese Weise zu aktualisieren

public static class ExtensionMethods
{
    private static Action EmptyDelegate = delegate() { };

    public static void Refresh(this UIElement uiElement)
    {
        uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
    }
}
13
user1360355

Erstellen Sie eine Klassenvariable:

SynchronizationContext _context;

Stellen Sie es in dem Konstruktor ein, der Ihre Benutzeroberfläche erstellt:

var _context = SynchronizationContext.Current;

Wenn Sie das Etikett aktualisieren möchten:

_context.Send(status =>{
    // UPDATE LABEL
}, null);
13
blackmind

Sie müssen invoke und delegate verwenden

private delegate void MyLabelDelegate();
label1.Invoke( new MyLabelDelegate(){ label1.Text += 1; });
12
A. Zalonis

Die meisten anderen Antworten auf diese Frage sind für mich etwas komplex (ich bin neu in C #), daher schreibe ich meine:

Ich habe eine WPF Anwendung und habe einen Arbeiter wie folgt definiert:

Problem:

BackgroundWorker workerAllocator;
workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1) {
    // This is my DoWork function.
    // It is given as an anonymous function, instead of a separate DoWork function

    // I need to update a message to textbox (txtLog) from this thread function

    // Want to write below line, to update UI
    txt.Text = "my message"

    // But it fails with:
    //  'System.InvalidOperationException':
    //  "The calling thread cannot access this object because a different thread owns it"
}

Lösung:

workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1)
{
    // The below single line works
    txtLog.Dispatcher.BeginInvoke((Action)(() => txtLog.Text = "my message"));
}

Ich muss noch herausfinden, was die obige Zeile bedeutet, aber es funktioniert.

Für WinForms:

Lösung:

txtLog.Invoke((MethodInvoker)delegate
{
    txtLog.Text = "my message";
});

Am einfachsten finde ich:

   void Update()
   {
       BeginInvoke((Action)delegate()
       {
           //do your update
       });
   }
8
Vasily Semenov

Greifen Sie beispielsweise auf ein anderes Steuerelement als im aktuellen Thread zu:

Speed_Threshold = 30;
textOutput.Invoke(new EventHandler(delegate
{
    lblThreshold.Text = Speed_Threshold.ToString();
}));

Dort ist lblThreshold ein Label und Speed_Threshold eine globale Variable.

8
Da Xiong

Ich habe gerade die Antworten gelesen und dies scheint ein sehr heißes Thema zu sein. Ich verwende derzeit .NET 3.5 SP1 und Windows Forms.

Die in den vorherigen Antworten beschriebene bekannte Formel, die die InvokeRequired -Eigenschaft verwendet, deckt die meisten Fälle ab, jedoch nicht den gesamten Pool.

Was ist, wenn das Handle noch nicht erstellt wurde?

Die InvokeRequired - Eigenschaft, wie beschrieben hier (Control.InvokeRequired-Eigenschaftsverweis auf MSDN) gibt true zurück, wenn der Aufruf von a erfolgte Thread, der nicht der GUI-Thread ist, false, wenn der Aufruf über den GUI-Thread erfolgte oder wenn das Handle noch nicht erstellt wurde.

Sie können auf eine Ausnahme stoßen, wenn ein modales Formular von einem anderen Thread angezeigt und aktualisiert werden soll. Da dieses Formular modal angezeigt werden soll, können Sie Folgendes tun:

private MyForm _gui;

public void StartToDoThings()
{
    _gui = new MyForm();
    Thread thread = new Thread(SomeDelegate);
    thread.Start();
    _gui.ShowDialog();
}

Und der Delegat kann ein Label auf der GUI aktualisieren:

private void SomeDelegate()
{
    // Operations that can take a variable amount of time, even no time
    //... then you update the GUI
    if(_gui.InvokeRequired)
        _gui.Invoke((Action)delegate { _gui.Label1.Text = "Done!"; });
    else
        _gui.Label1.Text = "Done!";
}

Dies kann zu einer InvalidOperationException führen, wenn die Vorgänge vor der Aktualisierung des Etiketts "weniger Zeit in Anspruch nehmen" (lesen und als Vereinfachung interpretieren) als die erforderliche Zeit für den GUI-Thread zum Erstellen des Formulars Handle . Dies geschieht innerhalb der ShowDialog () -Methode.

Sie sollten auch das Handle wie folgt überprüfen:

private void SomeDelegate()
{
    // Operations that can take a variable amount of time, even no time
    //... then you update the GUI
    if(_gui.IsHandleCreated)  //  <---- ADDED
        if(_gui.InvokeRequired)
            _gui.Invoke((Action)delegate { _gui.Label1.Text = "Done!"; });
        else
            _gui.Label1.Text = "Done!";
}

Sie können die auszuführende Operation ausführen, wenn das -Handle noch nicht erstellt wurde: Sie können das GUI-Update (wie im obigen Code gezeigt) einfach ignorieren oder Sie können warten (riskanter). Dies sollte die Frage beantworten.

Optionales Material: Persönlich habe ich Folgendes geschrieben:

public class ThreadSafeGuiCommand
{
  private const int SLEEPING_STEP = 100;
  private readonly int _totalTimeout;
  private int _timeout;

  public ThreadSafeGuiCommand(int totalTimeout)
  {
    _totalTimeout = totalTimeout;
  }

  public void Execute(Form form, Action guiCommand)
  {
    _timeout = _totalTimeout;
    while (!form.IsHandleCreated)
    {
      if (_timeout <= 0) return;

      Thread.Sleep(SLEEPING_STEP);
      _timeout -= SLEEPING_STEP;
    }

    if (form.InvokeRequired)
      form.Invoke(guiCommand);
    else
      guiCommand();
  }
}

Ich füttere meine Formulare, die von einem anderen Thread aktualisiert werden, mit einer Instanz dieses ThreadSafeGuiCommand und definiere Methoden, die die GUI (in meinem Formular) folgendermaßen aktualisieren :

public void SetLabeTextTo(string value)
{
  _threadSafeGuiCommand.Execute(this, delegate { Label1.Text = value; });
}

Auf diese Weise bin ich mir ziemlich sicher, dass ich meine GUI auf den neuesten Stand bringen werde, welcher Thread auch immer den Aufruf ausführen wird, und optional auf eine genau festgelegte Zeitspanne (das Timeout) warten kann.

8
Sume

Wenn Sie sich im UI-Thread befinden, können Sie ihn nach dem Task-Scheduler für den Synchronisationskontext fragen. Es würde Ihnen einen TaskScheduler geben, der alles auf dem UI-Thread plant.

Anschließend können Sie Ihre Aufgaben verketten, sodass eine andere Aufgabe (die im UI-Thread geplant ist) sie auswählt und einer Beschriftung zuweist, wenn das Ergebnis fertig ist.

public partial class MyForm : Form
{
  private readonly TaskScheduler _uiTaskScheduler;
  public MyForm()
  {
    InitializeComponent();
    _uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
  }

  private void buttonRunAsyncOperation_Click(object sender, EventArgs e)
  {
    RunAsyncOperation();
  }

  private void RunAsyncOperation()
  {
    var task = new Task<string>(LengthyComputation);
    task.ContinueWith(antecedent =>
                         UpdateResultLabel(antecedent.Result), _uiTaskScheduler);
    task.Start();
  }

  private string LengthyComputation()
  {
    Thread.Sleep(3000);
    return "47";
  }

  private void UpdateResultLabel(string text)
  {
    labelResult.Text = text;
  }
}

Dies funktioniert für Tasks (keine Threads), die derzeit die bevorzugte Methode zum Schreiben von gleichzeitigem Code sind .

8
nosalan

Selbst wenn der Vorgang zeitaufwändig ist (thread.sleep in meinem Beispiel) - Dieser Code sperrt NICHT Ihre Benutzeroberfläche:

 private void button1_Click(object sender, EventArgs e)
 {

      Thread t = new Thread(new ThreadStart(ThreadJob));
      t.IsBackground = true;
      t.Start();         
 }

 private void ThreadJob()
 {
     string newValue= "Hi";
     Thread.Sleep(2000); 

     this.Invoke((MethodInvoker)delegate
     {
         label1.Text = newValue; 
     });
 }
7
Yuliia Ashomok

Der einfachste Weg in WPF-Anwendungen ist:

this.Dispatcher.Invoke((Action)(() =>
{
    // This refers to a form in a WPF application 
    val1 = textBox.Text; // Access the UI 
}));
7

Ich konnte die Logik von Microsoft hinter dieser hässlichen Implementierung nicht verstehen, aber Sie müssen zwei Funktionen haben:

void setEnableLoginButton()
{
  if (InvokeRequired)
  {
    // btn_login can be any conroller, (label, button textbox ..etc.)

    btn_login.Invoke(new MethodInvoker(setEnable));

    // OR
    //Invoke(new MethodInvoker(setEnable));
  }
  else {
    setEnable();
  }
}

void setEnable()
{
  btn_login.Enabled = isLoginBtnEnabled;
}

Diese Schnipsel funktionieren für mich, sodass ich etwas in einem anderen Thread tun kann und dann die GUI aktualisiere:

Task.Factory.StartNew(()=>
{
    // THIS IS NOT GUI
    Thread.Sleep(5000);
    // HERE IS INVOKING GUI
    btn_login.Invoke(new Action(() => DoSomethingOnGUI()));
});

private void DoSomethingOnGUI()
{
   // GUI
   MessageBox.Show("message", "title", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}

Noch einfacher:

btn_login.Invoke(new Action(()=>{ /* HERE YOU ARE ON GUI */ }));
6
MBH

Ich wollte eine Warnung hinzufügen, da mir aufgefallen ist, dass einige der einfachen Lösungen das Kontrollkästchen InvokeRequired nicht enthalten.

Ich habe festgestellt, dass, wenn Ihr Code ausgeführt wird bevor das Fensterhandle des Steuerelements erstellt wurde (z. B. bevor das Formular angezeigt wird), Invoke eine Ausnahme auslöst. Daher empfehle ich, immer die Option InvokeRequired zu aktivieren, bevor Sie Invoke oder BeginInvoke anrufen.

6
Jos Bosmans

Hier ist ein neuer Blick auf eine uralte Ausgabe mit einem funktionaleren Stil. Wenn Sie die TaskXM-Klasse in all Ihren Projekten beibehalten, steht Ihnen nur eine Codezeile zur Verfügung, sodass Sie sich nie wieder Gedanken über Thread-übergreifende Aktualisierungen machen müssen.

public class Example
{
    /// <summary>
    /// No more delegates, background workers, etc. Just one line of code as shown below.
    /// Note it is dependent on the Task Extension method shown next.
    /// </summary>
    public async void Method1()
    {
        // Still on the GUI thread here if the method was called from the GUI thread
        // This code below calls the extension method which spins up a new task and calls back.
        await TaskXM.RunCodeAsync(() =>
        {
            // Running an asynchronous task here
            // Cannot update the GUI thread here, but can do lots of work
        });
        // Can update GUI on this line
    }
}


/// <summary>
/// A class containing extension methods for the Task class
/// </summary>
public static class TaskXM
{
    /// <summary>
    /// RunCodeAsyc is an extension method that encapsulates the Task.run using a callback
    /// </summary>
    /// <param name="Code">The caller is called back on the new Task (on a different thread)</param>
    /// <returns></returns>
    public async static Task RunCodeAsync(Action Code)
    {
        await Task.Run(() =>
        {
            Code();
        });
        return;
    }
}
5
John Peters

Vielleicht eine kleine Überdosis, aber so löse ich das normalerweise:

Aufrufe sind hier wegen der Synchronisation nicht erforderlich. Das BasicClassThreadExample ist für mich nur eine Art Layout, passen Sie es also an Ihre tatsächlichen Bedürfnisse an.

Es ist einfach, weil Sie nicht mit den Dingen im UI-Thread umgehen müssen!

public partial class Form1 : Form
{
    BasicClassThreadExample _example;

    public Form1()
    {
        InitializeComponent();
        _example = new BasicClassThreadExample();
        _example.MessageReceivedEvent += _example_MessageReceivedEvent;
    }

    void _example_MessageReceivedEvent(string command)
    {
        listBox1.Items.Add(command);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        listBox1.Items.Clear();
        _example.Start();
    }
}

public class BasicClassThreadExample : IDisposable
{
    public delegate void MessageReceivedHandler(string msg);

    public event MessageReceivedHandler MessageReceivedEvent;

    protected virtual void OnMessageReceivedEvent(string msg)
    {
        MessageReceivedHandler handler = MessageReceivedEvent;
        if (handler != null)
        {
            handler(msg);
        }
    }

    private System.Threading.SynchronizationContext _SynchronizationContext;
    private System.Threading.Thread _doWorkThread;
    private bool disposed = false;

    public BasicClassThreadExample()
    {
        _SynchronizationContext = System.ComponentModel.AsyncOperationManager.SynchronizationContext;
    }

    public void Start()
    {
        _doWorkThread = _doWorkThread ?? new System.Threading.Thread(dowork);

        if (!(_doWorkThread.IsAlive))
        {
            _doWorkThread = new System.Threading.Thread(dowork);
            _doWorkThread.IsBackground = true;
            _doWorkThread.Start();
        }
    }

    public void dowork()
    {
        string[] retval = System.IO.Directory.GetFiles(@"C:\Windows\System32", "*.*", System.IO.SearchOption.TopDirectoryOnly);
        foreach (var item in retval)
        {
            System.Threading.Thread.Sleep(25);
            _SynchronizationContext.Post(new System.Threading.SendOrPostCallback(delegate(object obj)
            {
                OnMessageReceivedEvent(item);
            }), null);
        }
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                _doWorkThread.Abort();
            }
            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~BasicClassThreadExample() { Dispose(false); }

}
5
Carsten R.

Ein weiteres Beispiel zu diesem Thema: Ich habe die abstrakte Klasse UiSynchronizeModel erstellt, die eine allgemeine Methodenimplementierung enthält:

public abstract class UiSynchronizeModel
{
    private readonly TaskScheduler uiSyncContext;
    private readonly SynchronizationContext winformsOrDefaultContext;

    protected UiSynchronizeModel()
    {
        this.winformsOrDefaultContext = SynchronizationContext.Current ?? new SynchronizationContext();
        this.uiSyncContext = TaskScheduler.FromCurrentSynchronizationContext();
    }

    protected void RunOnGuiThread(Action action)
    {
        this.winformsOrDefaultContext.Post(o => action(), null);
    }

    protected void CompleteTask(Task task, TaskContinuationOptions options, Action<Task> action)
    {
        task.ContinueWith(delegate
        {
            action(task);
            task.Dispose();
        }, CancellationToken.None, options, this.uiSyncContext);
    }
}

Ihre Modell- oder Controller-Klasse sollte von dieser abstrakten Klasse abgeleitet sein. Sie können ein beliebiges Muster (Aufgaben oder manuell verwaltete Hintergrundthreads) verwenden und die folgenden Methoden verwenden:

public void MethodThatCalledFromBackroundThread()
{
   this.RunOnGuiThread(() => {
       // Do something over UI controls
   });
}

Aufgaben Beispiel:

var task = Task.Factory.StartNew(delegate
{
    // Background code
    this.RunOnGuiThread(() => {
        // Do something over UI controls
    });
});

this.CompleteTask(task, TaskContinuationOptions.OnlyOnRanToCompletion, delegate
{
    // Code that can safely use UI controls
});
5

Grundsätzlich können Sie dieses Problem unabhängig von der Framework-Version oder dem der GUI zugrunde liegenden Bibliothekstyp beheben, indem Sie das Steuerelement speichern, indem Sie den Synchronisierungskontext des Threads für den Arbeitsthread erstellen, der die Interaktion des Steuerelements vom Arbeitsthread in die Thread-Nachrichtenwarteschlange der GUI marshallt.

Beispiel:

SynchronizationContext ctx = SynchronizationContext.Current; // From control
ctx.Send\Post... // From worker thread
4
Roman Ambinder

nd noch eine generische Control Erweiterung Ansatz ..

Fügen Sie zunächst eine Erweiterungsmethode für Objekte des Typs hinzu. Steuerelement

public static void InvokeIfRequired<T>(this T c, Action<T> action) where T : Control
{
    if (c.InvokeRequired)
    {
        c.Invoke(new Action(() => action(c)));
    }
    else
    {
        action(c);
    }
}

und rufen Sie so von einem anderen Thread aus auf, um auf ein Control mit dem Namen object1 im UI-Thread zuzugreifen:

object1.InvokeIfRequired(c => { c.Visible = true; });
object1.InvokeIfRequired(c => { c.Text = "ABC"; });

..oder so

object1.InvokeIfRequired(c => 
  { 
      c.Text = "ABC";
      c.Visible = true; 
  }
);
3
flodis

Holen Sie sich zuerst die Instanz Ihres Formulars (in diesem Fall mainForm) und verwenden Sie diesen Code dann einfach in einem anderen Thread.

mainForm.Invoke(new MethodInvoker(delegate () 
{
    // Update things in my mainForm here
    mainForm.UpdateView();
}));
3
Musculaa

Ich bevorzuge dieses hier:

private void UpdateNowProcessing(string nowProcessing)
{
    if (this.InvokeRequired)
    {
        Action<string> d = UpdateNowProcessing;
        Invoke(d, nowProcessing);
    }
    else
    {
        this.progressDialog.Next(nowProcessing);
    }            
}
3
user523650

Fügen Sie eine gemeinsame Variable in eine separate Klasse ein, um den Wert zu speichern.

Beispiel:

public  class data_holder_for_controls
{
    // It will hold the value for your label
    public string status = string.Empty;
}

class Demo
{
    public static  data_holder_for_controls d1 = new data_holder_for_controls();

    static void Main(string[] args)
    {
        ThreadStart ts = new ThreadStart(perform_logic);
        Thread t1 = new Thread(ts);
        t1.Start();
        t1.Join();
        //your_label.Text=d1.status; --- can access it from any thread
    }

    public static void perform_logic()
    {
        // Put some code here in this function
        for (int i = 0; i < 10; i++)
        {
            // Statements here
        }
        // Set the result in the status variable
        d1.status = "Task done";
    }
}
3
Saurabh

benutze einfach den Synchronisationskontext von ui

using System.Threading;

// ...

public partial class MyForm : Form
{
    private readonly SynchronizationContext uiContext;

    public MyForm()
    {
        InitializeComponent();
        uiContext = SynchronizationContext.Current; // get ui thread context
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Thread t = new Thread(() =>
            {// set ui thread context to new thread context                            
             // for operations with ui elements to be performed in proper thread
             SynchronizationContext
                 .SetSynchronizationContext(uiContext);
             label1.Text = "some text";
            });
        t.Start();
    }
}
2
user53373

In meinem Fall (WPF) ist die Lösung so einfach:

private void updateUI()
{
    if (!Dispatcher.CheckAccess())
    {
        Dispatcher.BeginInvoke(updateUI);
        return;
    }

    // Update any number of controls here
}
2
ipe

Der allgemeine Ansatz ist wie folgt:

using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        int clickCount = 0;

        public Form1()
        {
            InitializeComponent();
            label1.SetText("0");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            new Thread(() => label1.SetText((++clickCount).ToString())).Start();
        }
    }

    public static class ControlExtensions
    {
        public static void SetText(this Control control, string text)
        {
            if (control.InvokeRequired)
                control.Invoke(setText, control, text);
            else
                control.Text = text;
        }

        private static readonly Action<Control, string> setText =
            (control, text) => control.Text = text;
    }
}

Erklärung :

Die Antwort ist ziemlich wie diese . Verwendet aber ordentlicher (wie für mich) und neuere Syntax. Der Punkt ist InvokeRequired Eigentum von control. Es wird ein Wert abgerufen, der angibt, ob der Aufrufer eine Aufrufmethode aufrufen muss, wenn Methodenaufrufe an das Steuerelement gesendet werden, da sich der Aufrufer in einem anderen Thread befindet als dem, in dem das Steuerelement erstellt wurde. Wenn wir also control.SetText("some text") für denselben Thread aufrufen, für den control erstellt wurde, ist es in Ordnung, Text als diesen control.Text = text festzulegen. Aber in jedem anderen Thread wird System.InvalidOperationException ausgelöst, daher muss eine Methode über control.Invoke(...) aufgerufen werden, um Text für den Thread festzulegen, für den control erstellt wurde.

1
Alex

Der einfachste Weg ist wie folgt aufzurufen:

 Application.Current.Dispatcher.Invoke(new Action(() =>
             {
                    try
                    {
                        ///
                    }
                    catch (Exception)
                    {
                      //
                    }


                    }
     ));
0
Lankan