2015-12-08 10 views
0

Я запускаю BackgroundWorker, которые предположительно обновят мой UserControl. Я попытался Призывая после проверки InvokeRequired свойства:Вызов элемента управления из фонового рабочего стола без ошибок

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 
} 

Я также попытался вызвать RichTextBox внутри моего UserControl непосредственно:

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

Но когда я отладки кода, он просто прекращает работу остальной части кода без ошибок.

Оба _ucResultRich.InvokeRequired и _ucResultRich.rchResult.InvokeRequired возвращение true.

Я делаю что-то неправильно здесь?

Update

Я поставил Invoke участие в try catch, и теперь я могу получить следующее сообщение об ошибке с сообщением исключения:

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

Это потому, что не могу определить контроль? Потому что он показывает это как Control ''.

+1

Весь смысл BackgroundWorker легко создать 2 потоки: один для графического интерфейса и еще один для чего-то другого. Он включает в себя все, что требуется для идеальной связи между обоими потоками, но вы должны использовать его правильно. Событие 'DoWork' запускает поток backgroundworker и, следовательно, вы не можете изменить то, что принадлежит нишу GUI (например, любого элемента управления).Чтобы выполнить какие-либо изменения в элементах управления, вам нужно либо дождаться завершения потока backgroundworker (через событие «RunWorkerCompleted»), либо вызвать «WorkerReportsProgress» (+ знать, как его использовать) ... – varocarbas

+1

... Вкратце: вы должны либо научитесь правильно использовать фонового рабочего или вообще не использовать его (существуют другие подходы многопоточности). То, что вы пытаетесь ('Invoke'), не имеет никакого смысла: вы смешиваете разные подходы (а не максимизируете основную причину использования фонового рабочего, простоту встроенных функций), что обычно приводит к ошибкам, которые должны никогда не бывает (= те, которые получены из-за неправильного использования данной переменной). – varocarbas

+0

Вы получаете хорошую диагностику, контроль с проблемой на самом деле не имеет имени. Это 'tmpRich'. Независимо от того, что вы делаете, вы получите это исключение, _ucResultRich принадлежит вашему потоку пользовательского интерфейса, а tmpRich принадлежит вашему рабочему потоку. Это произошло, когда вы назначили его свойство Rtf. Вам придется переосмыслить это, очень высокие шансы, конечно, что вы обнаружите, что больше нет смысла использовать рабочий поток, потому что вся реальная работа * должна быть выполнена потоком пользовательского интерфейса. Вы можете переделать и присвоить _ucResultRich.Rtf в обработчике событий RunWorkerCompleted с помощью e.Result. –

ответ

1

Чтобы обновить элементы управления в потоке пользовательского интерфейса, вам необходимо вызвать делегата, а не действие при использовании других потоков.

Вы можете использовать мой метод общего назначения для достижения этого:

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); 

Update

Вы можете использовать этот метод для вызова бесцельных методов управления:

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()' также используя это? –