问题
I'm running a BackgroundWorker
which suppose to update my UserControl
. I tried Invoking after checking InvokeRequired
property:
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
}
I also tried to invoke the RichTextBox
inside my UserControl
directly:
_ucResultRich.rchResult.Invoke(new Action(() => _ucResultRich.rchResult.Rtf = tmpRich.Rtf));
But when I debug the code, it just stops running the rest of the code with no error.
Both _ucResultRich.InvokeRequired
and _ucResultRich.rchResult.InvokeRequired
return true
.
Am I doing something wrong here?
Update
I put the Invoke
part in try catch
and now I can get the following error from the exception message:
Cross-thread operation not valid: Control '' accessed from a thread
other than the thread it was created on.
Is it because it cant determine the control? Cause it shows it like Control ''
.
回答1:
You need to invoke a delegate not an Action when using other threads to update controls on the UI thread.
You can use my general purpose method to achieve this:
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);
}
}
And you can use this method like this:
SetControlProperty(_ucResultRich.rchResult, "Rtf", tmpRich.Rtf);
Update
You can use this method to call parameterless methods on the control:
public static void CallMethodUsingInvoke(Control control, Action methodOnControl)
{
if (control == null)
return;
var dlg = new MethodInvoker(methodOnControl);
control.Invoke(dlg, control, methodOnControl);
}
Example:
CallMethodUsingInvoke(richTextBox1, () => _ucResultRich.rchResult.SelectAll());
To call more complex methods you must create an appropriate delegate for the method you need to call.
Update 2
To get values from properties from other threads you can use this method:
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);
}
Example:
var textLength = GetControlPropertyValue<int>(_ucResultRich.rchResult, "Length");
来源:https://stackoverflow.com/questions/34151045/invoking-a-control-from-backgroundworker-stops-with-no-error