it-swarm.dev

GUI'yi başka bir diziden nasıl güncellerim?

Label öğesini başka bir diziden güncellemenin en basit yolu nedir?

thread1'da Form var ve bundan başka bir konuya başlıyorum (thread2). thread2 bazı dosyaları işlerken, Label'daki Form __ ile güncel thread2 'in çalışma durumunu güncellemek istiyorum.

Bunu nasıl yapabilirim?

1268
CruelIO

.NET 2.0 için, tam olarak istediğiniz şeyi yapan ve Control üzerinde herhangi bir özellik için çalışan, yazdığım güzel bir kod:

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 });
  }
}

Buna şöyle diyoruz:

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

.NET 3.0 veya daha üstünü kullanıyorsanız, yukarıdaki yöntemi Control sınıfının bir uzantı yöntemi olarak yeniden yazabilirsiniz; bu, çağrıyı basitleştirir:

myLabel.SetPropertyThreadSafe("Text", status);

GÜNCELLEME 05/10/2010:

.NET 3.0 için bu kodu kullanmanız gerekir:

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 ||
      !@this.GetType().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 });
  }
}

daha temiz, daha basit ve daha güvenli sözdizimi sağlamak için LINQ ve lambda ifadeleri kullananlar:

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

Yalnızca derleme zamanında kontrol edilen özellik adı değil, özelliğin türü de aynıdır, bu nedenle (örneğin) bir boolean özelliğine bir dize değeri atamak imkansızdır ve bu nedenle çalışma zamanı istisnasına neden olur.

Ne yazık ki bu, başka bir Control 'ın özellik ve değerine geçmek gibi aptalca şeyler yapmalarını engellemez; bu nedenle, aşağıdakiler mutlu bir şekilde derlenir:

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

Bu yüzden, passed in özelliğinin aslında yöntemin çağrıldığı Control dosyasına ait olduğundan emin olmak için çalışma zamanı kontrollerini ekledim. Mükemmel değil, ama yine de .NET 2.0 sürümünden çok daha iyi.

Derleme zamanı güvenliği için bu kodun nasıl geliştirileceğine dair başka bir öneriniz varsa, lütfen yorum yapın!

735
Ian Kemp

En basit yolu Label.Invoke 'a geçirilen isimsiz bir yöntemdir:

_// 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
_

Invoke işleminin tamamlanana kadar yürütmeyi engellediğine dikkat edin - bu senkron koddur. Soru, eşzamansız kod hakkında soru sormuyor, ancak hakkında bilgi edinmek istediğinizde eşzamanlı olmayan kod yazma hakkında --- Yığın Taşması içeriği var.

1033
Marc Gravell

Uzun süre çalışmak

.NET 4.5 ve C # 5.0 öğelerini kullanıp Görev Tabanlı Eşzamansız Desen (TAP) , async - bekliyor ile birlikte kullanmalısınız. ] anahtar kelimeler tüm alanlarda (GUI dahil):

TAP, yeni geliştirme için önerilen asenkron tasarım deseni

yerine Asenkron Programlama Modeli (APM) ve Olay Tabanlı Asenkron Desen (EAP) (ikincisi BackgroundWorker Sınıfını içerir).

Ardından, yeni gelişme için önerilen çözüm:

  1. Bir olay işleyicisinin zaman uyumsuz uygulaması (Evet, hepsi bu):

    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. UI iş parçacığını bildiren ikinci iş parçacığının uygulanması:

    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());
            }
        }
    }
    

Aşağıdakilere dikkat edin:

  1. Geri aramalar ve açık iş parçacıkları olmadan sırayla yazılmış kısa ve temiz kod.
  2. Görev yerine Konu .
  3. async anahtar sözcüğü, await işlevinin kullanılmasına izin verir; bu da, olay işleyicisinin, görev tamamlanana kadar tamamlanma durumuna ulaşmasını engeller ve bu arada UI iş parçacığını engellemez.
  4. Endişelerin Ayrılması (SoC) tasarım ilkesini destekleyen ve açık gönderici ve çağrı gerektirmeyen bir ilerleme sınıfı (bkz. IProgress Arabirimi ). Geçerli SynchronizationContext öğesini oluşturulduğu yerden kullanır (burada UI iş parçacığı).
  5. TaskCreationOptions.LongRunning , görevin sıraya alınmamasını ima eden _ = ThreadPool .

Daha ayrıntılı örnekler için bakınız: C # Geleceği: 'Bekleyenler' için iyi şeyler geliyor tarafından Joseph Albahari .

Ayrıca bakınız: UI Threading Model konsepti.

İstisnaları işleme

Aşağıdaki kod parçası, arka plan yürütme sırasında birden fazla tıklamayı önlemek için istisnalar ve toggle button'un Enabled özelliğinin nasıl ele alınacağına bir örnektir.

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...");
    }
}
371
Ryszard Dżegan

.NET 4 için Marc Gravell'in en basit çözüm varyasyonu:

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

Veya yerine Eylem temsilcisini kullanın:

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

İkisinin karşılaştırması için buraya bakınız: MethodInvoker - Action for Control.BeginInvoke

218
Zaid Masud

.NET 3.5+ için Fire ve unut uzantısı

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();
        }
    }
}

Bu, aşağıdaki kod satırını kullanarak çağrılabilir:

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

Bunu yapmanız gereken klasik yol budur:

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;
        }
    }
}

Çalışan iş parçanızın bir etkinliği var. UI iş parçacığınız, iş yapmak için başka bir iş parçacığı başlatır ve bu işçinin olayı kancaya böylelikle iş parçacığının durumunu görüntüleyebilirsiniz.

Daha sonra, kullanıcı arayüzünde, gerçek kontrolü değiştirmek için, bir etiket veya bir ilerleme çubuğu gibi ... konuları geçirmeniz gerekir.

64
Hath

Basit çözüm Control.Invoke kullanmaktır.

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

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

Threading kodu genellikle buggy ve test etmek her zaman zordur. Kullanıcı arayüzünü bir arka plan görevinden güncellemek için iş parçacığı kodu yazmanıza gerek yoktur. Görevi çalıştırmak için BackgroundWorker sınıfını ve kullanıcı arayüzünü güncellemek için ReportProgress yöntemini kullanın. Genellikle, yalnızca tamamlanmış bir yüzde bildirirsiniz, ancak durum nesnesi içeren başka bir aşırı yük var. İşte sadece bir string nesnesini rapor eden bir örnek:

    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();
    }

Her zaman aynı alanı güncellemek istiyorsanız sorun değil. Yapılacak daha karmaşık güncellemeler varsa, UI durumunu temsil edecek bir sınıf tanımlayabilir ve bunu ReportProgress yöntemine geçirebilirsiniz.

Son bir şey, WorkerReportsProgress bayrağını ayarladığınızdan emin olun, aksi takdirde ReportProgress yöntemi tamamen yoksayılır.

45
Don Kirkby

Cevapların büyük çoğunluğu, gerçekleşmesini bekleyen yarış durumu olan Control.Invoke öğesini kullanır. Örneğin, kabul edilen cevabı düşünün:

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

Eğer kullanıcı this.Invoke çağrılmadan hemen önce formu kapatırsa (unutmayın, thisForm nesnesidir), bir ObjectDisposedException muhtemelen ateşlenir.

Çözüm, SynchronizationContext, özellikle SynchronizationContext.Currenthamilton.danielb önerdiği şekilde kullanmaktır (diğer cevaplar tamamen gereksiz olan belirli SynchronizationContext uygulamalarına dayanmaktadır). Kodunu, SynchronizationContext.Post yerine SynchronizationContext.Send kullanmak için biraz değiştirirdim (genellikle çalışan iş parçacığının beklemesine gerek olmadığından):

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

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

.NET 4.0 ve üzeri sürümlerde, zaman uyumsuzluk işlemleri için görevleri kullanmanız gerektiğini unutmayın. Eşdeğer göreve dayalı yaklaşım için n-san'ın cevabına bakınız (TaskScheduler.FromCurrentSynchronizationContext kullanarak).

Son olarak, .NET 4.5 ve üzeri sürümlerde, uzun süredir devam eden işlemin hala çalışırken UI kodunu çalıştırması gereken durumlar için Ryszard Dżegan's tarafından gösterildiği gibi Progress<T> (temelde SynchronizationContext.Current öğesini yakalayan) kullanabilirsiniz.

39
Ohad Schneider

Güncellemenin doğru iş parçacığında gerçekleşdiğinden emin olmanız gerekir; UI iş parçacığı.

Bunu yapabilmek için, olay işleyiciyi doğrudan çağırmak yerine çağırmanız gerekir.

Etkinliğinizi şu şekilde yükselterek bunu yapabilirsiniz:

(Kod buraya kafamdan yazıldığı için doğru sözdizimini kontrol etmedim, vb.

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 ( ... );
      }
   }
}

WPF kontrolleri ISynchronizeInvoke arayüzünü uygulamadığı için yukarıdaki kodun WPF projeleri üzerinde çalışmayacağını unutmayın.

Yukarıdaki kodun Windows Forms ve WPF ve diğer tüm platformlarda çalıştığından emin olmak için AsyncOperation, AsyncOperationManager ve SynchronizationContext sınıflarına göz atabilirsiniz.

Olayları bu şekilde kolayca yükseltmek için, sadece arayarak bir etkinliği yükseltmeyi basitleştirmemi sağlayan bir uzatma yöntemi geliştirdim:

MyEvent.Raise(this, EventArgs.Empty);

Elbette, bu konuyu sizin için soyutlayacak olan BackGroundWorker sınıfını da kullanabilirsiniz.

34

GUI iş parçacığındaki yöntemi çağırmanız gerekir. Control.Invoke'i arayarak yapabilirsiniz.

Örneğin:

delegate void UpdateLabelDelegate (string message);

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

    MyLabelControl.Text = message;
}
28
Kieron

Önceki cevaplardaki Invoke materyallerinden hiçbiri gerekli değildir.

WindowsFormsSynchronizationContext'e bakmanız gerekir:

// 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
}
26
Jon H

Senaryonun önemsizliği nedeniyle, aslında durum için UI thread anketine sahip olurdum. Bence oldukça şık olabileceğini göreceksin.

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...";
    }
  }
}

Yaklaşım, ISynchronizeInvoke.Invoke ve ISynchronizeInvoke.BeginInvoke yöntemlerini kullanırken gereken marşalizasyon işleminden kaçınır. Marshaling tekniğini kullanmakta yanlış bir şey yoktur, ancak bilmeniz gereken birkaç uyarı vardır.

  • BeginInvoke işlevini çok sık aramadığınızdan emin olun, aksi takdirde mesaj pompasını aşabilir.
  • İşçi iş parçacığı üzerinde Invoke çağrısı engelleyici bir çağrıdır. Bu iş parçacığında yapılan işi geçici olarak durduracaktır.

Bu cevapta önerdiğim strateji, ipliklerin iletişim rollerini tersine çevirir. Verileri iterek çalışan iş parçacığı yerine, kullanıcı arabirimi iş parçacığı için yoklama. Bu, birçok senaryoda kullanılan ortak bir kalıptır. Yapmak istediğiniz tek şey, çalışan iş parçacığından ilerleme bilgilerini görüntülemek olduğundan, o zaman bu çözümün marşaj çözümüne mükemmel bir alternatif olduğunu göreceksiniz. Aşağıdaki avantajlara sahiptir.

  • UI ve çalışan iş parçacıkları, onları sıkıca birleştiren Control.Invoke veya Control.BeginInvoke yaklaşımının aksine gevşek şekilde bağlı kalır.
  • UI iş parçacığı çalışan iş parçacığının ilerlemesini engellemez.
  • Çalışan iş parçacığı, UI iş parçacığının güncelleştirmeyi harcadığı zaman baskın olamaz.
  • Kullanıcı Arabiriminin ve alt iş parçacığının işlem yaptığı aralıklar bağımsız kalabilir.
  • Çalışan iş parçacığı, kullanıcı arabirimi iş parçacığının ileti pompasını aşamaz.
  • UI dizisi, UI'nin ne zaman ve ne sıklıkta güncelleneceğini belirler.
26
Brian Gideon

Bu, .NET Framework 3.0 kullanan yukarıdaki çözüme benzer, ancak derleme zamanı güvenlik desteği sorununu çözdü.

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);
        }
    }
}

Kullanmak:

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

Kullanıcı yanlış veri türünü geçerse derleyici başarısız olur.

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

Salvete! Bu soruyu aradıktan sonra, cevapları FrankG ve Oregon Ghost 'un benim için en kolay olanı buldum. Şimdi, Visual Basic'te kodladım ve bu pasajı bir çeviriciden geçirdim; Bu yüzden nasıl sonuçlandığından emin değilim.

Bir günlüğe kaydetme ekranı olarak kullanıyorum, form_Diagnostics, adında bir zengin kutu içeren updateDiagWindow, adında bir iletişim formum var. Metnini tüm iş parçacıklarından güncelleyebilmem gerekiyordu. Ekstra satırlar, pencerenin otomatik olarak en yeni satırlara kaydırılmasını sağlar.

Ve şimdi, ekranı tüm programın herhangi bir yerinden herhangi bir iş parçacığı olmadan çalışacağını düşündüğünüz şekilde tek bir satırla güncelleyebilirim:

  form_Diagnostics.updateDiagWindow(whatmessage);

Ana Kod (bunu formunuzun sınıf kodunun içine koyun):

#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
22
bgmCoder

Ian Kemp'in çözümünün C # 3.0 varyasyonunda bu:

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);
}

Buna şöyle diyorsun:

myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!")
  1. "MemberExpression olarak" sonucuna null kontrolü ekler.
  2. Statik tip güvenliğini arttırır.

Aksi takdirde, orijinal çok güzel bir çözümdür.

20
Rotaerk

Birçok amaç için bu kadar basit:

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

"serviceGUI ()", (bu) biçiminde istediğiniz kadar denetimi değiştirebilen GUI düzeyindeki bir yöntemdir. Diğer konudan "updateGUI ()" yi arayın. Değerleri geçmek için parametreler eklenebilir veya (muhtemelen daha hızlı), kararsızlığa neden olabilecek konulara erişen dişliler arasında bir çakışma olasılığı varsa, gerektiğinde kilitleri olan sınıf kapsam değişkenlerini kullanabilirsiniz. GUI dışı iş parçacığı zaman kritik ise (Brian Gideon'ın uyarısını göz önünde bulundurarak) Invoke yerine BeginInvoke kullanın.

20
Frankg

Aynı sorunla karşılaştığımda Google’dan yardım istedim, ancak bana basit bir çözüm vermek yerine MethodInvoker ve blah blah blah örnekleri vererek beni daha çok şaşırttı. Bu yüzden kendi başıma çözmeye karar verdim. İşte benim çözümüm:

Böyle bir temsilci oluşturun:

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
}

Bu işlevi, buna benzer yeni bir dizgede arayabilirsiniz.

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

Thread(() => .....) ile karıştırmayın. Bir iş parçacığı üzerinde çalışırken adsız bir işlev veya lambda ifadesi kullanıyorum. Kod satırlarını azaltmak için, burada açıklamamam gereken ThreadStart(..) yöntemini de kullanabilirsiniz.

19
ahmar
Label lblText; //initialized elsewhere

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

   lblText.Text = text;           
}

BeginInvoke() işlevinin Invoke() yerine tercih edildiğini unutmayın; çünkü kilitlenmelere neden olma olasılığı daha düşüktür (ancak bu, yalnızca bir etikete metin atandığında bu bir sorun değildir):

Invoke() işlevini kullanırken, yöntemin geri dönmesini beklersiniz. Şimdi, çağrılan kodda iş parçacığı için beklemeniz gerekebilecek bir şey yapmanız olabilir; bu, çağırdığınız bazı işlevlere gömülürse hemen anlaşılamayacak, kendisi de olay işleyicileri aracılığıyla dolaylı olarak gerçekleşebilir. Böylece ipliği bekliyor olacaksınız, iplik sizi bekliyor olacak ve kilitleneceksiniz.

Bu aslında yayımlanan bazı yazılımların askıda kalmasına neden oldu. Invoke() işlevini BeginInvoke() ile değiştirerek düzeltmek yeterince kolaydı. Senkronize çalışmaya ihtiyaç duymuyorsanız, ki eğer bir dönüş değerine ihtiyacınız varsa, BeginInvoke() kullanın.

19
ILoveFortran

Basitçe böyle bir şey kullanın:

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

Zaten var olan temsilciyi Action kullanabilirsiniz:

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

Versiyonum bir satır özyinelemeli "mantra" eklemektir:

Argüman yok için:

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

        // Your code!
    }

Argümanları olan bir işlev için:

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

BU BT .


Bazı argümantasyon : Genellikle, kod okunabilirliğinin if () ifadesinden sonra bir satırda {} yazması kötü olur. Ancak bu durumda aynı "mantra" rutindir. Bu yöntem proje üzerinde tutarlıysa kod okunabilirliğini bozmaz. Ve kodunuzu çöpten kurtarır (beş yerine bir kod satırı).

if(InvokeRequired) {something long} işlevini gördüğünüzde, "bu işlev başka bir iş parçacığından çağrı yapmak güvenlidir" diye bilirsiniz.

14
MajesticRa

Bunu kullanarak etiketi yenilemeyi deneyin

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

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

Bir sınıf değişkeni yaratın:

SynchronizationContext _context;

Kullanıcı arayüzünüzü oluşturan kurucuda ayarlayın:

var _context = SynchronizationContext.Current;

Etiketi güncellemek istediğinizde:

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

İnvoke kullanmalı ve temsilci seçmelisiniz

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

Diğer cevapların çoğu bu soru için benim için biraz karmaşık (C # için yeniyim), bu yüzden benim yazıyorum:

BirWPFuygulamam var ve bir çalışanı aşağıdaki gibi tanımladım:

Konu:

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"
}

Çözüm:

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

Henüz yukarıdaki çizginin ne anlama geldiğini bulamadım, ama işe yarıyor.

WinForms için :

Çözüm:

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

Örneğin, mevcut iş parçacığı dışındaki bir denetime erişin:

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

Burada lblThreshold bir Label ve Speed_Threshold global bir değişkendir.

8
Da Xiong

Sadece cevapları okudum ve bu çok sıcak bir konu olarak görünüyor. Şu anda .NET 3.5 SP1 ve Windows Forms kullanıyorum.

InvokeRequired özelliğini kullanan önceki yanıtlarda büyük ölçüde açıklanan iyi bilinen formül, çoğu durumu kapsar, ancak tüm havuzu kapsar.

Ya Handle henüz yaratılmadıysa?

InvokeRequired property, açıklandığı gibi here (Control.InvokeRequired MSDN’nin Özellik referansı) , çağrı GUI iş parçacığı olmayan bir iş parçacığından yapılmışsa true, çağrı yapılmışsa false GUI iş parçacığından ya da Handle henüz oluşturulmadıysa.

Başka bir iş parçacığı tarafından gösterilen ve güncellenen kalıcı bir form olmasını istiyorsanız bir istisna ile karşılaşabilirsiniz. O formun kalıcı olarak gösterilmesini istediğiniz için aşağıdakileri yapabilirsiniz:

private MyForm _gui;

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

Temsilci GUI'deki bir Etiketi güncelleyebilir:

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!";
}

Bu, bir InvalidOperationException etiketinin güncellemesinden önceki işlemler "daha az zaman alır" (okuma ve basitleştirme olarak yorumlanırsa) GUI iş parçacığının oluşturulması için geçen süreden daha fazla olabilirse Form 's İşleç . Bu, ShowDialog () yönteminde gerçekleşir.

Ayrıca Sapla şunu da kontrol etmelisiniz:

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!";
}

Handle henüz oluşturulmadıysa, işlemi gerçekleştirmek için işlem yapabilir: GUI güncellemesini (yukarıdaki kodda gösterildiği gibi) görmezden gelebilir veya bekleyebilirsiniz (daha riskli). Bu soruya cevap vermelidir.

İsteğe bağlı şeyler: Şahsen ben aşağıdakini kodlama geldi:

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();
  }
}

Bunun bir örneğiyle başka bir iş parçacığı tarafından güncellenen formlarımı besledim ThreadSafeGuiCommand ve GUI'yi (benim formumda) bu şekilde güncelleyen yöntemleri şöyle tanımlarım:

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

Bu şekilde, GUI'imin çağrıyı ne yapacak olursa olsun, isteğe bağlı olarak iyi tanımlanmış bir süreyi (zaman aşımı) bekleyecek şekilde güncelleyeceğinden eminim.

8
Sume

UI iş parçacığındayken, senkronizasyon bağlamında görev zamanlayıcısı isteyebilir. Size UI iş parçacığındaki her şeyi zamanlayan bir TaskScheduler verecektir.

Ardından görevlerinizi, sonuç hazır olduğunda daha sonra başka bir görev (UI iş parçacığında programlanmış olan) seçip bir etikete ataması için zincirleyebilirsiniz.

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;
  }
}

Bu, şu anda eşzamanlı kod yazmanın tercih edilen şekli olan görevler (iş parçacığı değil) için işe yarar .

8
nosalan

Sanırım en kolay yol:

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

WPF uygulamalarında en basit yol şudur:

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

İşlem zaman alıcı olsa bile (benim örneğimde thread.sleep) - Bu kod kullanıcı arayüzünüzü KİLİTLEMEZ:

 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; 
     });
 }
6
Yuliia Ashomok

Bu çirkin uygulamanın arkasındaki Microsoft mantığını alamadım, ancak iki işleve sahip olmalısınız:

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;
}

Bu pasajlar benim için çalışıyor, bu yüzden başka bir iş parçacığında bir şeyler yapabilirim ve sonra GUI'yi güncellerim:

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);
}

Daha da basit:

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

Bir uyarı eklemek istedim çünkü bazı basit çözümlerin InvokeRequired kontrolünü atladığını fark ettim.

Eğer kodunuz kontrolün pencere tutamaçları oluşturulmadan önce (ör. Form gösterilmeden önce) çalıştırılırsa, Invoke bir istisna atar. Bu yüzden, InvokeRequired veya Invoke işlevini çağırmadan önce daima BeginInvoke öğesini denetlemenizi öneririm.

6
Jos Bosmans

İşte daha işlevsel bir tarz kullanan asırlık bir konuya yeni bir bakış. TaskXM sınıfını tüm projelerinizde tutarsanız, çapraz güncellemeler hakkında bir daha asla endişelenmeyeceğiniz tek bir kod satırı vardır.

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

Belki biraz doz aşımı, ama bu normal olarak çözme yöntemim:

Eşitleme nedeniyle burada çağrılar gerekli değildir. BasicClassThreadExample benim için sadece bir çeşit düzen, bu nedenle gerçek gereksinimlerinize uyacak şekilde değiştirin.

Çok basit çünkü UI iş parçacığındaki şeyleri kullanmanıza gerek yok!

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.

Konuyla ilgili bir başka örnek: Genel bir yöntem uygulaması içeren soyut bir sınıf, UiSynchronizeModel yaptım:

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);
    }
}

Model veya kontrolör sınıfınız bu soyut sınıftan türetilmelidir. Herhangi bir deseni (görevler veya el ile yönetilen arka plan iş parçacıkları) kullanabilir ve aşağıdaki gibi yöntemleri kullanabilirsiniz:

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

Görevler örneği:

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

Temel olarak, çerçeve versiyonundan veya GUI'nin temelindeki kütüphane tipinden bağımsız olarak bu sorunu çözmenin yolu, kontrolün, çalışan iş parçacığından GUI'nin iş parçacığı mesajları kuyruğuna kadar olan etkileşimi ile ilgili olarak çalışan iş parçacığının Senkronizasyon bağlamını yaratan kontrolü kaydetmektir.

Örnek:

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

Ve bir başka jenerik Kontrol uzatma yaklaşımı ..

İlk önce Control türündeki nesneler için bir uzatma yöntemi ekleyin

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);
    }
}

ve UI-thread'deki object1 isimli bir Control komutuna erişmek için buna benzer bir diziden çağrı yapın:

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

..veya böyle

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

İlk önce formunuzun örneğini alın (bu durumda mainForm) ve sonra bu kodu başka bir dizide kullanın.

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

Bunu tercih ederim:

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

Değeri tutmak için bazı ortak değişkenleri ayrı bir sınıfa yerleştirin.

Örnek:

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

sadece ui senkronizasyon bağlamı kullanın

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

Benim durumumda (WPF) çözüm bu kadar basittir:

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

    // Update any number of controls here
}
2
ipe

Genel yaklaşım şöyledir:

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;
    }
}

Açıklama :

Cevap oldukça güzel bu . Ancak daha temiz (benim için) ve daha yeni sözdizimi kullanır. Önemli olan InvokeRequired __control özelliğidir. Kontrole yapılan metot çağrıları yaparken arayanın bir invoke metodu çağırması gerekip gerekmediğini belirten bir değer alır, çünkü arayan, kontrolün oluşturulduğundan farklı bir konu üzerindedir. Dolayısıyla, aynı iş parçasında control.SetText("some text") çağırırsak control oluşturuldu, Text işlevini bu control.Text = text olarak ayarlamak yeterlidir. Ancak diğer herhangi bir iş parçacığı üzerinde System.InvalidOperationException 'ya neden olur, bu yüzden control.Invoke(...) yöntemiyle Text dizisi üzerinde control değerini oluşturmak için bir yöntem çağırması gerekir.

1
Alex

En basit yol şu şekilde çağırıyor:

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


                    }
     ));
0
Lankan