2015-10-28 17 views
1

我試圖從另一個線程設置TextBox的Text屬性。我在下面得到了這個例外。如何修復我的後臺解決方案以進行線程安全調用?

"Cross-thread operation not valid: Control 'recTpcTxt' accessed from a thread other than the thread it was created on." 

然後,我用BackgroundWorker來解決這個問題。但是,我面臨着同樣的異常信息。

編輯[1]: 其實我自己引導一下這個鏈接; https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx。我可以通過使用invokeproperty來解決我的問題。但是,我無法解決背景工作者的問題。

我的解決方案有什麼問題嗎?如何修復我的解決方案以設置UI變量的某些屬性?

編輯[2]:更多的代碼來澄清問題;

MqttManager.cs;

public partial class MqttManager : Form 
    { 
     MqttHandler mqttHandler = new MqttHandler(); 
     public static MqttManager managerInst; 

     public MqttManager() 
     { 
      InitializeComponent(); 
      managerInst = this; 
      ... 
     } 

     ... 

     private BackgroundWorker backgroundWorker; 

     public void NotifyUIForRecMsg(string topic, string message) 
     { 
      object[] objArr = { topic, message }; 
      this.backgroundWorker.RunWorkerAsync(objArr); 
     } 

     private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
     { 
      System.Threading.Thread.Sleep(5000); 
      e.Result = e.Argument; 
     } 

     private void backgroundWorker_RunWorkerCompleted(
      object sender, 
      RunWorkerCompletedEventArgs e) 
     { 
      object[] res = (object[])e.Result; 
      this.recTpcTxt.Text = (String)res[0]; 
     } 
    } 

MqttManager.Design.cs;

partial class MqttManager 
    { 
     /// <summary> 
     /// Required designer variable. 
     /// </summary> 
     private System.ComponentModel.IContainer components = null; 

     /// <summary> 
     /// Clean up any resources being used. 
     /// </summary> 
     /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> 
     protected override void Dispose(bool disposing) 
     { 
      if (disposing && (components != null)) 
      { 
       components.Dispose(); 
      } 
      base.Dispose(disposing); 
     } 

     #region Windows Form Designer generated code 

     /// <summary> 
     /// Required method for Designer support - do not modify 
     /// the contents of this method with the code editor. 
     /// </summary> 
     private void InitializeComponent() 
     { 
      ... 

      this.backgroundWorker = new System.ComponentModel.BackgroundWorker(); 
      this.backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork); 
      this.backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker_RunWorkerCompleted); 
     } 
     #endregion 

     ... 
    } 

MqttHandler.cs;

class MqttHandler 
    { 
     MqttClient client; 

     ... 

     /// <summary> 
     /// Publish received event handler. 
     /// </summary> 
     private void client_MqttMsgPublishReceived(Object sender, MqttMsgPublishEventArgs e) 
     { 
      MqttManager.managerInst.NotifyUIForRecMsg(e.Topic, Encoding.UTF8.GetString(e.Message)); 
     } 
    } 
+0

閱讀你是濫用'BackgroundWorker'。它打算做後臺工作而不更新UI。爲什麼不從'NotifyUIForRecMsg'更新UI? –

+0

@HamletHakobyan,我無法使用NotifyUIForRecMsg更新UI bu。我得到了上述的例外。 –

+0

爲什麼'NotifyUIForRecMsg'在非UI線程中?您應該提供更多的代碼並澄清問題。 –

回答

4

檢查: https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx

基本上,設定你必須要在同一個UI線程控制propertiy。

這種簡單的解決方案將呼叫轉移到textbox1.Text = someText在UI線程

private void SetText(string text) 
{ 
    // InvokeRequired required compares the thread ID of the 
    // calling thread to the thread ID of the creating thread. 
    // If these threads are different, it returns true. 
    if (this.textBox1.InvokeRequired) 
    { 
     SetTextCallback d = new SetTextCallback(SetText); 
     this.Invoke(d, new object[] { text }); 
    } 
    else 
    { 
     this.textBox1.Text = text; 
    } 
} 

還,您可以使用textBox1.BeginInvoke代替Invoke:它會在UI線程中運行,不鎖定程序線程等待SetText委託完成

[編輯]做你的BackgroundWorker:

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    object[] arg = (object[])e.Argument; 
    SetTextToTextBox(recTpcTxt, (string)arg[0]); 
    SetTextToTextBox(recMsgTxt, (string)arg[1]); 
} 

private void SetTextToTextBox(TextBox toSet, string text) 
{ 
    // InvokeRequired required compares the thread ID of the 
    // calling thread to the thread ID of the creating thread. 
    // If these threads are different, it returns true. 
    if (toSet.InvokeRequired) 
    { 
     SetTextCallback d = new SetTextCallback(SetText); 
     toSet.Invoke(d, new object[] { text }); 
    } 
    else 
    { 
     toSet.Text = text; 
    } 
} 

[編輯2] 要正確使用BackgroundWorker的

註冊事件DoWorkRunWorkerCompleted

this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork); 
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted); 

在退出之前backgroundWorker1_DoWork EventArgs的,集result屬性,並在backgroundWorker1_RunWorkerCompleted

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    System.Threading.Thread.Sleep(5000); 
    e.Result = new string[] { "one", "two" }; 
} 
private void backgroundWorker1_RunWorkerCompleted(
    object sender, 
    RunWorkerCompletedEventArgs e) 
{ 
    string[] res = (string[])e.Result; 
    this.textBox1.Text = res[0]; 
} 
+0

我剛纔編輯我的問題上面。我已經使用你的建議。它解決了我的問題。但我想在問題中應用backgroundworker解決方案。 –

+0

爲什麼我不能通過使用backgroundworker來解決我的問題?在我提到的問題中,可以使用backgroundworker進行線程安全調用。除了使用「invoke」之外,Backgroundworker在這個鏈接中被提到作爲解決方案的替代方式。 –

+0

請參閱編輯anwser –

相關問題