2009-02-24 54 views
1

爲了能夠做我使用了一段代碼像這樣適當的跨線程訪問:跨線程Winforms的最簡單的方法是什麼?

Private Delegate Sub DelSetButton(ByVal button As Button, ByVal label As String, ByVal enabled As Boolean) 

Private Sub SetButton(ByVal button As Button, ByVal label As String, ByVal enabled As Boolean) 
    If InvokeRequired Then 
     Invoke(New DelSetButton(AddressOf SetButton), button, label, enabled) 

    Else 
     button.Enabled = enabled 
     button.Text = label 

    End If 
End Sub 

這是不是可愛。

  • 我要創建一個委託具有相同簽名
  • 我需要寫一個類似的呼籲,每個控制類型或每個動作我想

我使用VB.NET 9/.NET Framework 3.5。

有沒有更好的方法來做到這一點?或者我堅持這種模式?

UPDATE:

我喜歡這一個最好的答案後:

Private Sub ContSetButton(ByVal button As Button, ByVal label As String, ByVal enabled As Boolean) 
    Invoke(New Action(Of Button, String, Boolean)(AddressOf SetButton), button, label, enabled) 
End Sub 

Private Sub SetButton(ByVal button As Button, ByVal label As String, ByVal Enabled As Boolean) 
    button.Text = label 
    button.Enabled = Enabled 
End Sub 

還不夠完美,但比我原來做的更好。

隨意回答如果你能做得比這更好。

回答

3

(這是假設你需要更詳細的控制比BackgroundWorker提供了 - 如果這足以讓你,這是正確的答案,邁赫達德Afshari的建議)

你並不需要創建一個委託類型。使用通用的FuncAction類型。

,而不是測試爲InvokeRequired,你可能想只是有兩種方法 - 其中一個總是所調用,然後它只會被調用在UI線程並沒有一個「默認地將Impl」(或其他)方法做任何調用。這是對模式的改變,而不是一種新的模式,但它可能會更清潔。

應該可以寫一個更一般的方式來處理這個問題,但是我需要一點時間來思考它......而且我會用C#而不是VB來呈現它,太...

+0

當使用工作者線程時,所有工作人員都很忙,運行時間實際上需要花費更多時間搜索/創建工作人員而不是解僱代理人員? – Sesh 2009-02-24 11:40:06

3

您可以使用BackgroundWorker並使用ReportProgress方法與UI線程進行通信。在UI線程中,您處理ProgressChanged事件並適當地更新UI。

+0

BackgroundWorker的實現對我來說看起來更加複雜或者我錯過了什麼?您需要創建一個新的工作人員,將事件添加到事件中,然後處理事件。 – 2009-02-24 12:05:25

+0

我認爲如果你大多需要單向通信(從工作者線程到UI線程),這並不是很複雜。不過這是個人觀點。 – 2009-02-24 12:12:57

3

有很多選擇。其中之一是使用SynchronizationContext(對不起我的C#)

SynchronizationContext context = SynchronizationContext.Current; 

// Later on 
private void SetButton(Button button, string label) 
{ 
    context.Send(delegate 
     { 
      button.Text = label; 
     }, null); 
} 

,這就是這麼多了。將匿名代理轉換爲VB.NET,你會沒事的。

另一種選擇將是使用面向方面編程(AOP),並有這將封某些呼叫到UI線程的方面:

[ExecuteOnUIThread()] 
protected virtual void SetButton(Button button, string label) 
{ 
    button.Text = label; 
} 
0

我創建的事件處理程序和事件處理程序的擴展方法,它允許你以安全的方式舉辦活動。 就是這樣;如果需要,調用事件處理程序。因此,如果您想在另一個線程上進行一些處理的同時更新Winforms控件,則可以安全地提升事件。事件處理程序將在必要時調用。

它看起來像這樣(對不起,C#代碼)

public static class EventExtensions 
{ 

    public static void Raise(this EventHandler target, object sender, EventArgs e) 
    { 
     EventHandler handler = target; 

     if(handler != null) 
     { 
      Delegate[] delegates = handler.GetInvocationList(); 

      EventExtensions.Raise (delegates, sender, e); 
     } 
    } 

    public static void Raise<TEventArgs>(this EventHandler<TEventArgs> target, object sender, TEventArgs e) 
         where TEventArgs : EventArgs 
    { 
     EventHandler<TEventArgs> handler = target; 

     if(handler != null) 
     { 
      Delegate[] delegates = handler.GetInvocationList(); 

      EventExtensions.Raise (delegates, sender, e); 
     } 
    } 

    private static void Raise(Delegate[] delegates, object sender, EventArgs e) 
    { 
     foreach(Delegate d in delegates) 
     { 
      ISynchronizeInvoke target = d.Target as ISynchronizeInvoke; 

      if(target != null && target.InvokeRequired) 
      { 
       target.Invoke (d, new object[] { sender, e }); 
      } 
      else 
      { 
       d.DynamicInvoke (sender, e); 
      } 
     } 
    } 
} 

這個擴展方法使您,您無需擔心是否需要與否線程同步引發一個事件。 只會增加你的事件是這樣的:

MyEvent.Raise (this, new MyEventArgs (...)); 

(我也有過載,這需要的SynchronizationContext)

0

我使用委託的代碼匿名方法,因爲這並不需要創建的代碼當前塊之外的方法(大家都知道)。但有時需要使用參數化委託來消除加倍的代碼,然後匿名方法是無用的。我試過這樣的解決方案:

// somewhere in class body 
delegate EventHandler d(ComboBox c1, ComboBox c2); 

// in a method (eg. in contructor) 
d d1 = delegate(arg1, arg2) // eg. 2 args 
{ 
return new EventHandler(
    delegate 
    { 
    // some doing ... 
    } 
); 
}; 

// And then installation of event handler 
combo1.SelectionChangeCommitted += d1(combo1, combo2); 

問題是引發這個事件。當使用命名方法時(例如,在鍵入+ =後會在VS中使用TAB鍵生成命名方法,例如combo1_SelectionChangeCommited),則總有可能運行:combo1_SelectionChangeCommited(c1, c2)

要運行在d1上方,我找到的一個解決方案是Frederik的類EventExtensions。 在我看來,這是一件好事。這意味着,作者在覈心C#編程方面比我高。因此,我想問他:弗雷德裏克,我可以在我剛剛學習的碩士論文中正式使用這些代碼嗎?宣佈代碼片段課程的作者。

1

作爲這裏所有優秀答案的附註,或許PostSharp可能會讓你發現一個可以幫助你的方面。

通過這種方式,你可以寫你這樣的代碼:

<InvokeMightBeRequired> 
Private Sub SetButton(ByVal button As Button, ByVal label As String, ByVal enabled As Boolean) 
    button.Enabled = enabled 
    button.Text = label 
End Sub 

這將注入必要的代碼來處理調用的部分。

請注意,我不知道這是否可能,但我懷疑是這樣。

0

如上回答,但想到會給拉姆達相當於

//module parameter - initialise on load 
private SynchronizationContext = SynchronizationContext.Current; 
//in the method where you want to modify UI control parameters 
ctx.Send(state => 
{ 
    Left = Screen.PrimaryScreen.WorkingArea.Width - Width - 10; 
    Top = Screen.PrimaryScreen.WorkingArea.Height - Height; 
    SetBounds(Left, Top, Width, Height); 
    Opacity = 5; 
}, null); 

我知道你已經得到這個回答了,但想到將分享我知道的很少。

相關問題