2012-01-05 2 views
9

У меня есть приложение winform (одна форма), на этой форме есть RichTextBox. В конструкторе этой формы я создаю экземпляр класса MyClass. В «Form_Load» я вызываю метод Initialisation от MyClass экземпляра.BackgroundWorker - Работа с перекрестными потоками не действительна

в виде конструктора

myClass = new MyClass(RichTextBox richTextBox); 

В Form_Load

myClass.Initialisation(); 

В методе Initialisation, в цикле, я прочитал некоторые parmeters делают другие материалы. Чтобы не замораживать приложение (потому что некоторая операция может занять некоторое время, несколько секунд), я использую BackgroundWorker. Я использую его так (см. Код ниже).

Когда я исполняю, я получаю эту ошибку: операции Cross-нить не действует: Control «RichTextBox» доступ из потока, отличного от резьбы она была создана на.

Не могли бы вы рассказать мне, как решить это? Работа идеально подходит, когда я не имеют доступа к richTextBox

public Class MyClass 
{ 
    static BackgroundWorker _bw; 
    public MyClass() 
    { 
     _bw = new BackgroundWorker 
     { 
      WorkerReportsProgress = true, 
      WorkerSupportsCancellation = true 
     }; 
     _bw.DoWork += bw_DoWork; 
     _bw.ProgressChanged += bw_ProgressChanged; 
     _bw.RunWorkerCompleted += bw_RunWorkerCompleted; 
    } 
    static void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     foreach (....) 
     { 
      if (....) 
      { 
       richtextBox.Text.AppendText("MyText"); 
      } 
     } 
     e.Result = true; 
    } 
    static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){} 
    static void bw_ProgressChanged(object sender, ProgressChangedEventArgs e){} 
} 

ответ

29

Использование BackgroundWorker не освобождает вас от обычных правил пронизывающих - как, например, только интерфейс поток может получить доступ компонентов пользовательского интерфейса.

Если вы хотите обновить пользовательский интерфейс из BackgroundWorkerкроме с использованием прогресса/завершением события (которые подняты в потоке пользовательского интерфейса), необходимо использовать Control.Invoke/Control.BeginInvoke так же, как и в других ситуациях. Например:

if (....) 
{ 
    Action action =() => richtextBox.Text.Add("MyText"); 
    richtextBox.Invoke(action); // Or use BeginInvoke 
} 
11

попробовать этот код,

BeginInvoke((MethodInvoker)delegate 
{ 
    richtextBox.Text.Add("MyText"); 
}); 
+0

Это работает, спасибо – mili

3

Используя компонент BackgroundWorker, события ProgressChanged и RunWorkerCompleted позволяют вам вызывать методы/свойства элементов управления пользовательского интерфейса (которые всегда должны выполняться в потоке пользовательского интерфейса). При обновлении пользовательского интерфейса в событии DoWork, которое выполняется в потоке, отличном от UI, вы получаете эту ошибку, возможно, вам следует обновить элементы управления пользовательским интерфейсом с помощью методов Invoke или BeginInvoke в DoWork, если хотите.

0

Добавить:

e.Result = "MyText"; 

В вашей bw_DoWork И:

richTextBox1.AppendText((string)e.Result); 

В вашей bw_RunWorkerCompleted

(Alter его под свой код)

EDIT:

Если это много раз сделано во время работы BackgroundWorker «s, вы можете добавить:

_bw.ReportProgress(0, "MyText"); 

bw_DoWork к и:

richTextBox1.AppendText((string)e.UserState); 

к bw_ProgressChanged.

1

Чтобы сделать его чище и на основе предложения Джона Скита, я сделал метод расширения, который делает то же самое, вы можете изменить «this Label control» в this TextBox control или просто использовать «this Control control» (и в принципе позволяют каждый элемент управления, чтобы быть легко модернизирована):

internal static class ExtensionMethods 
{ 
    /// <summary> 
    /// Updates the label text while being used in a multithread app. 
    /// </summary> 
    /// <param name="control">The control.</param> 
    /// <param name="text">The text.</param> 
    internal static void UpdateThreadedText(this Label control, string text) 
    { 
     Action action =() => control.Text = text; 
     control.Invoke(action); 
    } 

    /// <summary> 
    /// Refreshes the threaded. 
    /// </summary> 
    /// <param name="control">The control.</param> 
    internal static void RefreshThreaded(this Label control) 
    { 
     Action action = control.Refresh; 
     control.Invoke(action); 
    } 
} 

И тогда использование довольно прост:

this.yourLabelName.UpdateThreadedText("This is the text"); 
this.yourTextBoxName.UpdateThreadedText("This is the text"); 
this.yourControlName.UpdateThreadedText("This is the text"); 

или

this.yourLabelName.RefreshThreaded(); 

Работы для меня красиво :)

0

Вы также можете попробовать это.

this.Invoke(new MethodInvoker(delegate() 
{ 
    richtextBox.Text.Add("MyText"); 
})); 
Смежные вопросы