it-swarm.asia

كيف يمكنني تحديث واجهة المستخدم الرسومية من مؤشر ترابط آخر؟

ما هي أبسط طريقة لتحديث Label من سلسلة رسائل أخرى؟

لدي Form في thread1 ، ومن هنا أبدأ سلسلة رسائل أخرى (thread2). أثناء قيام thread2 بمعالجة بعض الملفات ، أود تحديث Label على Form بالحالة الحالية لعمل thread2.

كيف أقوم بذلك؟

1268
CruelIO

بالنسبة إلى .NET 2.0 ، إليك بعض الرموز البرمجية التي كتبت والتي تعمل بالضبط كما تريد ، وتعمل مع أي خاصية على Control:

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

نسميها مثل هذا:

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

إذا كنت تستخدم .NET 3.0 أو أعلى ، فيمكنك إعادة كتابة الطريقة المذكورة أعلاه كطريقة امتداد للفئة Control ، والتي من شأنها تبسيط الدعوة إلى:

myLabel.SetPropertyThreadSafe("Text", status);

تحديث 05/10/2010:

بالنسبة إلى .NET 3.0 ، يجب عليك استخدام هذا الرمز:

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

يستخدم تعبيرات LINQ و lambda للسماح ببناء أكثر نظافة وبساطة وأمانًا:

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

لا يتم التحقق من اسم الخاصية الآن فقط في وقت التحويل البرمجي ، بل يكون نوع الخاصية كذلك ، لذلك من المستحيل (على سبيل المثال) تعيين قيمة سلسلة لخاصية منطقية ، وبالتالي تسبب استثناء وقت التشغيل.

لسوء الحظ ، هذا لا يمنع أي شخص من القيام بأشياء غبية مثل تمرير خاصية وقيمة Control أخرى ، وبالتالي سيتم تجميع ما يلي بسعادة:

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

ومن ثم أضفت عمليات فحص وقت التشغيل للتأكد من أن الخاصية التي تم تمريرها تنتمي فعليًا إلى Control التي يتم استدعاء الأسلوب بها. ليست مثالية ، ولكن لا يزال أفضل بكثير من الإصدار .NET 2.0.

إذا كان لدى أي شخص أي اقتراحات أخرى حول كيفية تحسين هذا الرمز لضمان وقت الترجمة ، فيرجى التعليق!

735
Ian Kemp

أبسط طريقة هي طريقة مجهولة تم تمريرها إلى Label.Invoke :

// 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 يمنع التنفيذ حتى يكتمل - هذا رمز متزامن. لا يطرح السؤال حول التعليمات البرمجية غير المتزامنة ، ولكن يوجد الكثير من المحتوى على Stack Overflow حول كتابة التعليمات البرمجية غير المتزامنة عندما تريد التعرف عليها.

1001
Marc Gravell

التعامل مع العمل الطويل

بما أن .NET 4.5 و C # 5.0 يجب عليك استخدام النمط غير المتزامن القائم على المهام (TAP) جنبا إلى جنب مع غير متزامن - انتظر الكلمات الأساسية في جميع المناطق (بما في ذلك واجهة المستخدم الرسومية):

TAP هو نمط التصميم غير المتزامن الموصى به لتطوير جديد

بدلاً من نموذج البرمجة غير المتزامن (APM) و النمط غير المتزامن القائم على الأحداث (EAP) (يتضمن الأخير فئة BackgroundWorker ).

ثم ، الحل الموصى به للتطوير الجديد هو:

  1. التنفيذ غير المتزامن لمعالج الأحداث (نعم ، هذا كل شيء):

    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. تطبيق مؤشر الترابط الثاني بإعلام مؤشر ترابط واجهة المستخدم:

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

لاحظ ما يلي:

  1. رمز قصير ونظيف مكتوب بطريقة متسلسلة دون عمليات رد اتصال ومؤشرات ترابط واضحة.
  2. المهمة بدلا من الموضوع .
  3. async الكلمة الأساسية ، التي تسمح باستخدام انتظار والتي بدورها تمنع معالج الأحداث من الوصول إلى حالة الإكمال حتى الانتهاء من المهمة وفي الوقت نفسه لا يمنع مؤشر ترابط واجهة المستخدم.
  4. فئة التقدم (راجع IProgress Interface ) التي تدعم فصل المخاوف (SoC) مبدأ التصميم ولا تتطلب مرسلًا صريحًا واستدعاء. ويستخدم الحالي SynchronizationContext من مكان الإنشاء) (هنا موضوع واجهة المستخدم).
  5. TaskCreationOptions.LongRunning تلميحات إلى عدم انتظار المهمة في ThreadPool .

للحصول على مزيد من الأمثلة المطوّلة ، راجع: مستقبل C #: تأتي الأشياء الجيدة إلى من ينتظرون بواسطة Joseph Albahari .

انظر أيضًا حول نموذج مؤشر ترابط واجهة المستخدم المفهوم.

معالجة الاستثناءات

المقتطف التالي هو مثال على كيفية التعامل مع الاستثناءات وتبديل خاصية Enabled للزر لمنع النقرات المتعددة أثناء تنفيذ الخلفية.

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

تنوع Marc Gravell's أبسط حل لـ .NET 4:

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

أو استخدم مفوض الإجراء بدلاً من ذلك:

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

انظر هنا لمقارنة الاثنين: MethodInvoker vs Action for Control.BeginInvoke

218
Zaid Masud

إطلاق النار وطريقة التمديد ل. 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();
        }
    }
}

يمكن استدعاء هذا باستخدام سطر التعليمات البرمجية التالي:

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

هذه هي الطريقة الكلاسيكية التي يجب عليك القيام بها:

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

مؤشر ترابط العامل لديه حدث. يبدأ مؤشر ترابط واجهة المستخدم الخاص بك من بدء تشغيل مؤشر ترابط آخر للقيام بالعمل وربط حدث العامل حتى تتمكن من عرض حالة مؤشر ترابط العامل.

ثم في واجهة المستخدم ، تحتاج إلى تقاطع الخيوط لتغيير عنصر التحكم الفعلي ... مثل التسمية أو شريط التقدم.

64
Hath

الحل البسيط هو استخدام Control.Invoke.

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

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

غالبًا ما تكون شفرة الخيوط عربات التي تجرها الدواب ويصعب دائمًا اختبارها. لا تحتاج إلى كتابة رمز الترابط لتحديث واجهة المستخدم من مهمة خلفية. فقط استخدم BackgroundWorker class لتشغيل المهمة و ReportProgress الطريقة لتحديث واجهة المستخدم. عادةً ما تقوم بالإبلاغ عن نسبة مئوية كاملة ، ولكن هناك حمل زائد آخر يتضمن كائن حالة. فيما يلي مثال يوضح فقط كائن سلسلة:

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

هذا جيد إذا كنت تريد دائمًا تحديث نفس الحقل. إذا كان لديك تحديثات أكثر تعقيدًا يمكنك القيام بها ، فيمكنك تحديد فئة لتمثيل حالة واجهة المستخدم وتمريرها إلى أسلوب ReportProgress.

شيء أخير ، تأكد من تعيين العلامة WorkerReportsProgress ، أو سيتم تجاهل طريقة ReportProgress بالكامل.

45
Don Kirkby

الغالبية العظمى من الإجابات تستخدم Control.Invoke وهو شرط سباق في انتظار الحدوث . على سبيل المثال ، ضع في الاعتبار الإجابة المقبولة:

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

إذا أغلق المستخدم النموذج مباشرة قبل استدعاء this.Invoke (تذكر ، this هو الكائن Form) ، فمن المحتمل أن يتم تشغيل ObjectDisposedException.

يكمن الحل في استخدام SynchronizationContext ، وتحديداً SynchronizationContext.Current كما يوحي { hamilton.danielb _ (تعتمد الإجابات الأخرى على تطبيقات SynchronizationContext معينة والتي لا لزوم لها تمامًا). أقوم بتعديل الرمز الخاص به قليلاً لاستخدام SynchronizationContext.Post بدلاً من SynchronizationContext.Send بالرغم من ذلك (حيث لا توجد حاجة عادة إلى مؤشر ترابط العامل للانتظار):

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 وما فوق ، يجب أن تستخدم المهام لعمليات المزامنة. راجع n-san's الإجابة عن النهج القائم على المهمة المكافئ (باستخدام TaskScheduler.FromCurrentSynchronizationContext).

أخيرًا ، على .NET 4.5 وما فوق ، يمكنك أيضًا استخدام Progress<T> (والتي تلتقط أساسًا SynchronizationContext.Current عند إنشائها) كما هو موضح بواسطة Ryszard Dżegan للحالات التي تحتاج فيها العملية طويلة المدى إلى تشغيل رمز UI أثناء العمل.

39
Ohad Schneider

يجب عليك التأكد من أن التحديث يحدث في الخيط الصحيح ؛ موضوع واجهة المستخدم.

للقيام بذلك ، يجب عليك استدعاء معالج الأحداث بدلاً من الاتصال به مباشرة.

يمكنك القيام بذلك عن طريق رفع هذا الحدث الخاص بك مثل:

(تم كتابة الرمز هنا خارج رأسي ، لذلك لم أقم بالتحقق من بناء الجملة الصحيح ، وما إلى ذلك ، لكن يجب أن يتم ذلك.)

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 ، لأن عناصر تحكم WPF لا تطبق واجهة ISynchronizeInvoke.

للتأكد من أن الكود أعلاه يعمل مع Windows Forms و WPF ، وجميع الأنظمة الأساسية الأخرى ، يمكنك إلقاء نظرة على الفصول AsyncOperation و AsyncOperationManager و SynchronizationContext.

من أجل رفع الأحداث بهذه الطريقة بسهولة ، قمتُ بإنشاء طريقة ملحق ، والتي تتيح لي تبسيط رفع حدث من خلال الاتصال فقط:

MyEvent.Raise(this, EventArgs.Empty);

بالطبع ، يمكنك أيضًا الاستفادة من فئة BackGroundWorker ، والتي ستستخلص هذه المسألة من أجلك.

34
Frederik Gheysels

ستحتاج إلى استدعاء الأسلوب على مؤشر ترابط واجهة المستخدم الرسومية. يمكنك القيام بذلك عن طريق استدعاء Control.Invoke.

فمثلا:

delegate void UpdateLabelDelegate (string message);

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

    MyLabelControl.Text = message;
}
28
Kieron

لا شيء من عناصر الاستدعاء في الإجابات السابقة ضروري.

تحتاج إلى إلقاء نظرة على WindowsFormsSynchronizationContext:

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

بسبب تافه السيناريو ، سيكون لدي بالفعل استطلاع مؤشر ترابط UI للحالة. أعتقد أنك ستجد أنه يمكن أن يكون أنيقًا جدًا.

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

تتجنب الطريقة تنظيم العملية المطلوبة عند استخدام طريقتي ISynchronizeInvoke.Invoke و ISynchronizeInvoke.BeginInvoke. لا حرج في استخدام تقنية الحشد ، ولكن هناك بعض التحذيرات التي يجب أن تكون على دراية بها.

  • تأكد من عدم الاتصال بـ BeginInvoke بشكل متكرر جدًا أو قد يتجاوز مضخة الرسائل.
  • استدعاء Invoke على مؤشر ترابط العامل هو استدعاء حظر. سيوقف العمل الذي يتم في هذا الخيط مؤقتًا.

تعكس الاستراتيجية التي أقترحها في هذه الإجابة أدوار الاتصال في سلاسل الرسائل. بدلاً من مؤشر ترابط العامل دفع البيانات استطلاعات مؤشر ترابط UI لذلك. هذا نمط شائع يستخدم في العديد من السيناريوهات. نظرًا لأن كل ما تريد القيام به هو عرض معلومات التقدم من مؤشر ترابط العامل ، فأعتقد أنك ستجد أن هذا الحل هو بديل رائع لحل التنظيم. لديها المزايا التالية.

  • تظل UI وخيوط العمل المنفصلة مقترنة بشكل غير مقارن بنهج Control.Invoke أو Control.BeginInvoke الذي يربط بينهما بإحكام.
  • لن يعوق مؤشر ترابط واجهة المستخدم تقدم مؤشر ترابط العامل.
  • لا يمكن أن يسيطر مؤشر ترابط العامل على الوقت الذي يقضيه مؤشر ترابط واجهة المستخدم في التحديث.
  • يمكن أن تظل الفواصل الزمنية التي تنفذ فيها مؤشرات ترابط UI و العامل مستقلة.
  • لا يمكن تجاوز مؤشر ترابط العامل ضخ رسالة مؤشر ترابط UI.
  • يحصل مؤشر ترابط واجهة المستخدم على تحديد وقت وعدد مرات تحديث واجهة المستخدم.
26
Brian Gideon

يشبه هذا الحل أعلاه باستخدام .NET Framework 3.0 ، لكنه حل مشكلة دعم أمان وقت الترجمة .

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

ليستخدم:

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

سيفشل المحول البرمجي إذا قام المستخدم بتمرير نوع البيانات غير صحيح.

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

Salvete! بعد أن بحثت عن هذا السؤال ، وجدت الإجابات من قبل FrankG و Oregon Ghost لتكون الأسهل فائدة بالنسبة لي. الآن ، أنا رمز في Visual Basic وتشغيل هذا المقتطف من خلال محول؛ لذلك لست متأكدا تماما كيف اتضح.

لدي نموذج حوار يسمى form_Diagnostics, والذي يحتوي على مربع نص منسق ، يسمى updateDiagWindow, والذي أستخدمه كنوع من عرض التسجيل. كنت بحاجة إلى أن أكون قادرًا على تحديث نصه من جميع المواضيع. تسمح الخطوط الإضافية للنافذة بالانتقال تلقائيًا إلى أحدث الخطوط.

وهكذا ، يمكنني الآن تحديث الشاشة بسطر واحد ، من أي مكان في البرنامج بالكامل بالطريقة التي تعتقد أنها ستعمل دون أي ترابط:

  form_Diagnostics.updateDiagWindow(whatmessage);

الرمز الرئيسي (ضع هذا داخل رمز فئة النموذج الخاص بك):

#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

هذا في بلدي C # 3.0 الاختلاف من حل إيان كيمب:

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

أنت تسميها مثل هذا:

myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!")
  1. يضيف التحقق خالية إلى نتيجة "كـ MemberExpression".
  2. أنه يحسن ثابت نوع السلامة.

خلاف ذلك ، الأصلي هو حل لطيف جدا.

20
Rotaerk

بالنسبة للعديد من الأغراض ، يكون الأمر بهذه البساطة:

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

"serviceGUI ()" هي طريقة لمستوى واجهة المستخدم الرسومية داخل النموذج (هذا) والتي يمكن أن تغير أكبر عدد تريده من عناصر التحكم. استدعاء "updateGUI ()" من سلسلة رسائل أخرى. يمكن إضافة المعلمات لتمرير القيم ، أو (ربما أسرع) استخدام متغيرات نطاق الفئة مع تأمين عليها كما هو مطلوب إذا كان هناك أي احتمال للاصطدام بين مؤشرات الترابط الوصول إليها مما قد يسبب عدم الاستقرار. استخدم BeginInvoke بدلاً من Invoke إذا كان مؤشر ترابط غير واجهة المستخدم الرسومية هو وقت حرج (مع الأخذ في الاعتبار تحذير Brian Gideon).

20
Frankg

عندما واجهت نفس المشكلة ، طلبت المساعدة من Google ، لكن بدلاً من إعطائي حلاً بسيطًا ، أربكتني أكثر بإعطاء أمثلة على MethodInvoker و blah blah blah. لذلك قررت حلها بمفردي. إليكم الحل:

قم بعمل مندوب مثل هذا:

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
}

يمكنك استدعاء هذه الوظيفة في موضوع جديد مثل هذا

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

لا تخلط مع Thread(() => .....). يمكنني استخدام وظيفة مجهولة المصدر أو تعبير lambda عندما أعمل على سلسلة رسائل. لتقليل سطور التعليمات البرمجية ، يمكنك استخدام طريقة ThreadStart(..) والتي لا يُفترض أن أشرحها هنا.

19
ahmar
Label lblText; //initialized elsewhere

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

   lblText.Text = text;           
}

لاحظ أن BeginInvoke() مفضل على Invoke() لأنه من غير المحتمل أن يتسبب في توقف تام (ولكن ، هذه ليست مشكلة هنا عند تخصيص نص لتسمية فقط):

عند استخدام Invoke() ، أنت في انتظار عودة الأسلوب. الآن ، ربما تكون قد فعلت شيئًا في التعليمات البرمجية التي تم استدعاؤها والتي ستحتاج إلى انتظار سلسلة الرسائل ، والتي قد لا تكون واضحة على الفور إذا تم دفنها في بعض الوظائف التي تتصل بها ، والتي قد تحدث بحد ذاتها بشكل غير مباشر عبر معالجات الأحداث. لذلك كنت في انتظار الخيط ، سيكون الخيط في انتظارك وأنت في طريق مسدود.

هذا في الواقع تسبب في تعليق بعض البرامج التي تم إصدارها. كان من السهل إصلاح ما يكفي عن طريق استبدال Invoke() بـ BeginInvoke(). ما لم تكن بحاجة إلى تشغيل متزامن ، وهو ما قد يحدث إذا كنت بحاجة إلى قيمة إرجاع ، فاستخدم BeginInvoke().

19
ILoveFortran

ببساطة استخدام شيء مثل هذا:

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

يمكنك استخدام المفوض الموجود بالفعل Action:

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

الإصدار الخاص بي هو إدراج سطر واحد من "تعويذة" العودية:

بدون حجج:

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

        // Your code!
    }

لوظيفة لها وسائط:

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

هذا هو .


بعض الوسيطة : عادةً ما يكون من السهل على قراءة التعليمات البرمجية وضع {} بعد عبارة if () في سطر واحد. لكن في هذه الحالة ، يكون "تعويذة" روتينية. لا يكسر قراءة التعليمات البرمجية إذا كانت هذه الطريقة متسقة مع المشروع. ويحفظ التعليمات البرمجية الخاصة بك من القمامة (سطر واحد من التعليمات البرمجية بدلاً من خمسة).

كما ترى if(InvokeRequired) {something long} ، فأنت تعلم فقط "هذه الوظيفة آمنة للاتصال من سلسلة رسائل أخرى".

14
MajesticRa

حاول تحديث الملصق باستخدام هذا

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

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

إنشاء متغير فئة:

SynchronizationContext _context;

قم بتعيينه في المنشئ الذي ينشئ واجهة المستخدم الخاصة بك:

var _context = SynchronizationContext.Current;

عندما تريد تحديث التصنيف:

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

يجب عليك استخدام الاستدعاء والتفويض

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

معظم الإجابات الأخرى معقدة بعض الشيء بالنسبة لي حول هذا السؤال (أنا جديد على C #) ، لذلك أنا أكتب إجابتي:

لديWPFapplication وحددت العامل على النحو التالي:

القضية:

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

حل:

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

لم أعرف بعد ما يعنيه السطر أعلاه ، لكنه يعمل.

لـ WinForms :

حل:

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

على سبيل المثال ، قم بالوصول إلى عنصر تحكم آخر بخلاف في السلسلة الحالية:

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

هناك lblThreshold هو تسمية و Speed_Threshold هو متغير عمومي.

8
Da Xiong

لقد قرأت الإجابات ويبدو أن هذا موضوع ساخن للغاية. أنا أستخدم حاليًا .NET 3.5 SP1 و Windows Forms.

تغطي الصيغة المعروفة الموضحة إلى حد كبير في الإجابات السابقة التي تستخدم InvokeRequired الخاصية معظم الحالات ، ولكن لا تجمع بأكمله.

ماذا لو كان المؤشر لم يتم إنشاؤه بعد؟

InvokeRequired property ، كما هو موضح هنا (مرجع Control.InvokeRequired Property to MSDN) إرجاع true إذا تم إجراء المكالمة من مؤشر ترابط ليس مؤشر ترابط واجهة المستخدم الرسومية ، false إما إذا تم إجراء المكالمة من مؤشر ترابط واجهة المستخدم الرسومية ، أو إذا لم يتم إنشاء المؤشر حتى الآن.

يمكنك العثور على استثناء إذا كنت تريد عرض نموذج مشروط وتحديثه بواسطة سلسلة رسائل أخرى. لأنك تريد أن يظهر النموذج بشكل مشروط ، يمكنك القيام بما يلي:

private MyForm _gui;

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

ويمكن للمفوض تحديث ملصق على واجهة المستخدم الرسومية:

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

يمكن أن يؤدي هذا إلى InvalidOperationException إذا كانت العمليات قبل تحديث الملصق "تستغرق وقتًا أقل" (اقرأها وتفسيرها على أنها تبسيط) من الوقت الذي يستغرقه مؤشر ترابط واجهة المستخدم الرسومية لإنشاء نموذج 's المؤشر . يحدث هذا داخل ShowDialog () الطريقة.

يجب عليك أيضًا التحقق من المؤشر مثل هذا:

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 حتى الآن: يمكنك فقط تجاهل تحديث واجهة المستخدم الرسومية (كما هو موضح في الرمز أعلاه) أو يمكنك الانتظار (أكثر خطورة). هذا يجب أن يجيب على السؤال.

مواد اختيارية: شخصيا خطرت لترميز ما يلي:

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

أقوم بإدخال النماذج التي يتم تحديثها بواسطة مؤشر ترابط آخر بمثيل هذا ThreadSafeGuiCommand ، وأنا أقوم بتعريف الطرق التي تقوم بتحديث واجهة المستخدم الرسومية (في نموذجي) مثل هذا:

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

وبهذه الطريقة ، أنا متأكد تمامًا من أنني سوف أقوم بتحديث واجهة المستخدم الرسومية الخاصة بي بغض النظر عن مؤشر الترابط الذي سيجري المكالمة ، وانتظر اختيارياً فترة زمنية محددة جيدًا (المهلة).

8
Sume

عندما تكون في سلسلة رسائل واجهة المستخدم ، يمكنك أن تسألها عن جدولة مهمة سياق التزامن. سوف يوفر لك TaskScheduler الذي يقوم بجدولة كل شيء على مؤشر ترابط واجهة المستخدم.

بعد ذلك ، يمكنك سلسلة المهام الخاصة بك بحيث تكون النتيجة جاهزة ، ثم تقوم مهمة أخرى (والتي تم جدولتها على مؤشر ترابط واجهة المستخدم) باختيارها وتعيينها إلى تسمية.

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

هذا يعمل للمهام (وليس مؤشرات الترابط) التي هي الطريقة المفضلة لكتابة التعليمات البرمجية المتزامنة الآن .

8
nosalan

أسهل طريقة أعتقد:

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

إن أبسط طريقة في تطبيقات WPF هي:

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

حتى إذا كانت العملية تستغرق وقتًا طويلاً (thread.sleep in my example) - هذا الرمز لن يقفل واجهة المستخدم الخاصة بك:

 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

لم أستطع الحصول على منطق Microsoft وراء هذا التطبيق القبيح ، لكن يجب أن يكون لديك وظيفتان:

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

تعمل هذه المقتطفات بالنسبة لي ، لذا يمكنني القيام بشيء ما في سلسلة رسائل أخرى ، ثم أقوم بتحديث واجهة المستخدم الرسومية:

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

أبسط من ذلك:

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

كنت أرغب في إضافة تحذير لأنني لاحظت أن بعض الحلول البسيطة تغفل التحقق InvokeRequired.

لقد لاحظت أنه إذا تم تنفيذ الرمز الخاص بك قبل إنشاء مقبض النافذة لعنصر التحكم (على سبيل المثال قبل عرض النموذج) ، Invoke يلقي استثناء. لذلك أوصي دائمًا بالتحقق من InvokeRequired قبل الاتصال بـ Invoke أو BeginInvoke.

6
Jos Bosmans

إليك نظرة جديدة على مشكلة قديمة باستخدام أسلوب أكثر فاعلية. إذا احتفظت بفئة TaskXM في جميع مشاريعك ، فلديك فقط سطر واحد من التعليمات البرمجية لا تقلق أبدًا بشأن تحديثات السلسلة المشتركة مرة أخرى.

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

ربما جرعة زائدة قليلاً ، ولكن هذا هو نوع من طريقة حل هذا بشكل طبيعي:

الاستدعاءات غير مطلوبة هنا بسبب التزامن. BasicClassThreadExample هو مجرد نوع من التخطيط بالنسبة لي ، لذلك قم بتغييره ليناسب احتياجاتك الفعلية.

إنها بسيطة لأنك لا تحتاج إلى التعامل مع الأشياء في موضوع واجهة المستخدم!

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.

مثال آخر حول الموضوع: قمت بعمل فصل تجريدي ، UiSynchronizeModel ، يحتوي على طريقة تنفيذ شائعة:

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

يجب اشتقاق الفئة النموذجية أو وحدة التحكم الخاصة بك من هذه الفئة المجردة. يمكنك استخدام أي نمط (المهام أو مؤشرات ترابط الخلفية المُدارة يدويًا) واستخدام هذه الطرق مثل هذا:

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

مثال المهام:

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
Alexander Egorov

تتمثل طريقة حل هذه المشكلة بشكل أساسي بغض النظر عن إصدار Framework أو نوع مكتبة واجهة المستخدم الرسومية الأساسية في حفظ عنصر التحكم في إنشاء سياق تزامن مؤشر الترابط لمؤشر الترابط العامل الذي سينظم التفاعل ذي الصلة لعنصر التحكم من مؤشر ترابط العامل إلى قائمة انتظار رسائل سلاسل رسائل واجهة المستخدم الرسومية.

مثال:

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

وبعد عام آخر التحكم التمديد aproach ..

أولاً أضف طريقة امتداد للكائنات من النوع التحكم

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

وندعو مثل هذا من مؤشر ترابط آخر للوصول إلى عنصر تحكم باسم object1 في مؤشر ترابط واجهة المستخدم:

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

..او مثل هذا

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

أولاً الحصول على مثيل النموذج الخاص بك (في هذه الحالة mainForm) ، ثم استخدم هذا الرمز في مؤشر ترابط آخر.

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

انا افضل هذا واحد:

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

ضع بعض المتغيرات العامة في فصل منفصل للاحتفاظ بالقيمة.

مثال:

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

مجرد استخدام سياق التزامن من واجهة المستخدم

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

في حالتي (WPF) الحل بسيط مثل هذا:

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

    // Update any number of controls here
}
2
ipe

النهج العام هو مثل:

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

شرح :

الجواب جميل مثل هذا واحد . ولكن يستخدم أكثر إتقانًا (بالنسبة لي) وأحدث بناء جملة. النقطة هي InvokeRequired خاصية control. تحصل على قيمة تشير إلى ما إذا كان يجب على المتصل استدعاء طريقة استدعاء عند إجراء مكالمات طريقة لعنصر التحكم لأن المتصل موجود في سلسلة رسائل مختلفة عن تلك التي تم إنشاء عنصر التحكم عليها. لذلك إذا اتصلنا بـ control.SetText("some text") على نفس سلسلة الرسائل control تم إنشاؤه في ، فلا بأس في ضبط Text مثل هذا control.Text = text. ولكن في أي موضوع آخر ، فإنه يتسبب في System.InvalidOperationException لذلك يجب على الشخص استدعاء طريقة عبر control.Invoke(...) لتعيين Text على سلسلة الرسائل control تم إنشاؤه على.

1
Alex

أبسط طريقة هو الاحتجاج على النحو التالي:

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


                    }
     ));
0
Lankan