2011-05-23 68 views
3

在我的多線程應用程序中,我需要跨UI線程訪問UI元素,我使用線程安全方法來做到這一點。我在我的許多項目中反覆使用了這一點,並將它們保存在表單文件中,這使得文件看起來很難看。所以我想創建一個分離類,在那裏我可以把所有這些,並在需要時打電話給他們,但我有麻煩。對於instace改變控件的文本元素,我使用以下保持線程安全的UI訪問器在一個單獨的類在c#

delegate void SetTextCallback(string text, Control ctrl); 

public void SetText(string text, Control ctrl) 
    { 
     if (ctrl.InvokeRequired) 
     { 
      SetTextCallback d = new SetTextCallback(SetText); 
      this.Invoke(d, new object[] { text, ctrl }); 
     } 
     else 
     { 
      if (ctrl.GetType() == typeof(Label)) 
      { 
       ctrl.Text = text; 
      } 
      else 
      { 
       ctrl.Text += Environment.NewLine + text; 
      } 
     } 
    } 

,並調用該函數作爲

SetText("some text",label1); 

這工作得很好,如果它是在窗體類,如果我把它改成另一班我得到了一個錯誤在線

this.Invoke(d, new object[] { text, ctrl }); 

有人可以告訴我,我該如何做到這一點。

也有可能有一個UI訪問器方法做所有的東西,那就是現在我有像這樣的多種方法來改變文本之一來改變啓用的屬性之一來改變背景顏色和一個改變前面的顏色。是否有可能有類似

public void ChangePropert(Control ctrl,Property prop,Value val) 
+0

您能否確認您在調用時遇到的異常?我想它的通用線程異常解釋控制只能由它的所有者訪問。此外,請參閱有關屬性的反思,其中的問題將是類型安全。 – Smudge202 2011-05-23 08:34:38

回答

2

這一切的問題是你開始泄漏,其中控制實際居住形式之外的UI代碼。線程不應該知道控件,它應該工作並更新主線程,並讓主線程擔心需要在UI中執行什麼操作。

完成此操作的方法有第二個線程可以調用的回調,但強制該回調實際在主線程上執行而不是在第二個線程上執行。您可以使用「同步」上下文來完成此操作。

您需要將您的輔助線程包裝在可以保留對主線程同步上下文的引用的類中。然後輔助線程可以使用它來回叫。

例子:

public partial class Form1 : Form 
{ 
    private SynchronizationContext _synchronizationContext; 

    public Form1() 
    { 
     InitializeComponent(); 
     //Client must be careful to create sync context somehwere they are sure to be on main thread 
     _synchronizationContext = AsyncOperationManager.SynchronizationContext; 
    } 

    //Callback method implementation - must be of this form 
    public void ReceiveThreadData(object threadData) 
    { 
     // This callback now exeutes on the main thread. 
     // Can use directly in UI without error 
     this.listBoxMain.Items.Add((string)threadData); 
    } 

    private void DoSomeThreadWork() 
    { 
     // Thread needs callback and sync context so it must be wrapped in a class. 
     SendOrPostCallback callback = new SendOrPostCallback(ReceiveThreadData); 
     SomeThreadTask task = new SomeThreadTask(_synchronizationContext, callback); 
     Thread thread = new Thread(task.ExecuteThreadTask); 
     thread.Start(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     DoSomeThreadWork(); 
    } 

} 

而且你的線程類將是這個樣子:

/// SomeThreadTask defines the work a thread needs to do and also provides any data ///required along with callback pointers etc. 
/// Populate a new SomeThreadTask instance with a synch context and callnbackl along with ///any data the thread needs 
/// then start the thread to execute the task. 
/// </summary> 
public class SomeThreadTask 
{ 

    private string _taskId; 
    private SendOrPostCallback _completedCallback; 
    private SynchronizationContext _synchronizationContext; 

    /// <summary> 
    /// Get instance of a delegate used to notify the main thread when done. 
    /// </summary> 
    internal SendOrPostCallback CompletedCallback 
    { 
     get { return _completedCallback; } 
    } 

    /// <summary> 
    /// Get SynchronizationContext for main thread. 
    /// </summary> 
    internal SynchronizationContext SynchronizationContext 
    { 
     get { return _synchronizationContext; } 
    } 

    /// <summary> 
    /// Thread entry point function. 
    /// </summary> 
    public void ExecuteThreadTask() 
    { 

     //Just sleep instead of doing any real work 
     Thread.Sleep(5000); 

     string message = "This is some spoof data from thread work."; 

     // Execute callback on synch context to tell main thread this task is done. 
     SynchronizationContext.Post(CompletedCallback, (object)message); 


    } 

    public SomeThreadTask(SynchronizationContext synchronizationContext, SendOrPostCallback callback) 
    { 
     _synchronizationContext = synchronizationContext; 
     _completedCallback = callback; 
    } 

} 

現在你可以擺脫對每個控件的調用所有廢話。

-1

做這僅僅是當你調試你的項目,對不對?無論如何,如果你有另一個選項不創建一個單獨的類來操縱這個,你可以設置這個CheckForIllegalCrossThreadCalls屬性爲false每個form調用線程以外的其自己的線程。

CheckForIllegalCrossThreadCalls - MSDN

+0

我很好奇。你能否擴展這個答案,請給出一個用法的概念? (Pretty please)=) – Smudge202 2011-05-23 09:01:13

+0

添加v2.0時,您應該避免使用此屬性,因爲它是爲了保持與v1.1應用程序的向後兼容性而添加的。 – 2011-06-21 15:33:30

0

你可以將這些東西作爲擴展方法分開。這將允許你在對象本身中調用方法,而不是像現在這樣將它作爲參數傳遞。

所以,你可以這樣做:label1.SetText("some text"); instad的SetText("some text", label1);

額外的增益是,你可以爲每個控件類型不同的實現,所以你可以有一個標籤和一個文本框。這將使代碼更清潔。

最後,關於您使用反射設置屬性的問題。您可以使用Type.GetProperty()方法獲取對該屬性的引用。這會返回一個PropertyInfo對象,您可以使用它來設置屬性值,如下所示:

var textProperty = label1.GetType().GetProperty("Text"); 
textProperty.SetValue(label1, "some text", null); 
相關問題