2015-12-08 24 views
0

我正在運行一個BackgroundWorker,它將更新我的UserControl。我試圖檢查InvokeRequired屬性後,調用:從backgroundworker調用控件會停止且沒有錯誤

private void bgwHighlightText_DoWork(object sender, DoWorkEventArgs e) 
{ 
    tmpRich.SelectedRtf = myRtf; 
    if (_ucResultRich.InvokeRequired && _ucResultRich.rchResult.InvokeRequired) 
    _ucResultRich.Invoke(new Action(() => _ucResultRich.rchResult.Rtf = tmpRich.Rtf)); // Debug pointer stops here 

    //Rest of the code 
} 

我也試圖調用內部的RichTextBoxUserControl直接:

_ucResultRich.rchResult.Invoke(new Action(() => _ucResultRich.rchResult.Rtf = tmpRich.Rtf)); 

但是,當我調試的代碼,它只是停止運行代碼的其餘部分沒有錯誤。

兩個_ucResultRich.InvokeRequired_ucResultRich.rchResult.InvokeRequired回報true

我在這裏做錯了什麼?

更新

我把Invoke部分try catch,現在我可以從異常消息以下錯誤:

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

是因爲它不能確定的控制?因爲它顯示它像Control ''

+1

BackgroundWorker的整點是很容易創造200線程:一個用於GUI,另一個用於其他。它包含了兩個線程之間完美通信所需的所有內容,但您必須正確使用它。 'DoWork'事件啓動了backgroundworker線程,因此你不能從那裏修改屬於GUI線程的東西(例如任何控件)。要在控件中執行任何修改,您應該等待backgroundworker線程完成(通過'RunWorkerCompleted'事件)或調用'WorkerReportsProgress'(+知道如何使用這個)... – varocarbas

+1

...總之:您應該要麼學會正確使用backgroundworker,要麼根本不使用backgroundworker(還有其他多線程方法)。你在嘗試什麼('Invoke')沒有任何意義:你在混合使用不同的方法(而不是最大化使用背景工作的主要原因,其內置功能的簡單性),通常會導致錯誤不會發生(=派生的不適當地使用給定的變量)。 – varocarbas

+0

你會得到一個很好的診斷,對問題的控制確實沒有名字。它是'tmpRich'。不管你做什麼,你都會得到這個異常,_ucResultRich由你的UI線程擁有,tmpRich由你的工作線程擁有。當您分配其Rtf屬性時發生這種情況。你將不得不重新考慮這一點,當然你會發現使用工作線程沒有任何意義,因爲所有的真實工作都必須由UI線程來完成。您可以用e.Result修改並在RunWorkerCompleted事件處理程序中分配_ucResultRich.Rtf。 –

回答

1

當使用其他線程更新UI線程上的控件時,您需要調用委託而不是Action。

你可以用我的通用方法來實現這一目標:

public delegate void SetControlPropertyDelegateHandler(Control control, string propertyName, object value); 

public static void SetControlProperty(Control control, string propertyName, object value) 
{ 
if (control == null) 
    return; 
if (control.InvokeRequired) 
{ 
    SetControlPropertyDelegateHandler dlg = new SetControlPropertyDelegateHandler(SetControlProperty); 
    control.Invoke(dlg, control, propertyName, value); 
} 
else 
{ 
    PropertyInfo property = control.GetType().GetProperty(propertyName); 
    if (property != null) 
     property.SetValue(control, value, null); 
} 
} 

而且你可以使用這個方法是這樣的:

SetControlProperty(_ucResultRich.rchResult, "Rtf", tmpRich.Rtf); 

更新

您可以使用此方法調用控件上的無參數方法:

public static void CallMethodUsingInvoke(Control control, Action methodOnControl) 
    { 
     if (control == null) 
      return; 
     var dlg = new MethodInvoker(methodOnControl); 
     control.Invoke(dlg, control, methodOnControl); 
    } 

例子:

CallMethodUsingInvoke(richTextBox1,() => _ucResultRich.rchResult.SelectAll()); 

要調用更復雜的方法,你必須爲你需要調用的方法創建適當的委託。

更新2

要想從屬性的值從其他線程,你可以使用這個方法:

public delegate object GetControlPropertyValueDelegate(Control controlToModify, string propertyName); 

public static T GetControlPropertyValue<T>(Control controlToModify, string propertyName) 
{ 
    if (controlToModify.InvokeRequired) 
    { 
     var dlg = new GetControlPropertyValueDelegate(GetControlPropertyValue); 
     return (T)controlToModify.Invoke(dlg, controlToModify, propertyName); 
    } 
    else 
    { 
     var prop = controlToModify.GetType().GetProperty(propertyName); 
     if (prop != null) 
     { 
      return (T)Convert.ChangeType(prop.GetValue(controlToModify, null), typeof(T)); 
     } 
    } 
    return default (T); 
} 

例子:

var textLength = GetControlPropertyValue<int>(_ucResultRich.rchResult, "Length"); 
+0

只是一個側面說明:我會建議你另外寫一個PropertyNameResolver(表達式到字符串)來擺脫硬編碼的屬性字符串參數 – Jannik

+0

不是一個壞主意@Jannik,謝謝! –

+0

這是一個很酷的解決方案。但是你可以使用這個來調用類似'_ucResultRich.rchResult.SelectAll()'的東西嗎? –