2010-04-13 4 views
1

Я установил свойство через потоки раньше, и я нашел этот пост Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on о получении собственности.Назначение свойства через потоки

Я думаю, что мой вопрос с ниже код устанавливает переменную в коллекции является объектом, следовательно, на куче и поэтому просто создает указатель на тот же объект

Так что мой вопрос, кроме создания глубокой копии , или копирование коллекции в другой объект List, есть лучший способ сделать следующее для aviod ошибки во время цикла for.

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

Код:

private void btnRunProcess_Click(object sender, EventArgs e) 
    { 
     richTextBox1.Clear(); 

     BackgroundWorker bg = new BackgroundWorker(); 
     bg.DoWork += new DoWorkEventHandler(bg_DoWork); 
     bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted); 
     bg.RunWorkerAsync(); 
    } 


    void bg_DoWork(object sender, DoWorkEventArgs e) 
    { 
     WorkflowEngine engine = new WorkflowEngine(); 
     ListBox.SelectedObjectCollection selectedCollection=null; 

     if (lstProcessFiles.InvokeRequired) 
     { 
      // Try #1 
      selectedCollection = (ListBox.SelectedObjectCollection) 
       this.Invoke(new GetSelectedItemsDelegate(GetSelectedItems), 
       new object[] { lstProcessFiles }); 

      // Try #2 
      //lstProcessFiles.Invoke(
      // new MethodInvoker(delegate { 
      //  selectedCollection = lstProcessFiles.SelectedItems; })); 
     } 
     else 
     { 
      selectedCollection = lstProcessFiles.SelectedItems; 
     }   

     // *********Same Error on this line******************** 
     // Cross-thread operation not valid: Control 'lstProcessFiles' accessed 
     // from a thread other than the thread it was created on. 
     foreach (string l in selectedCollection) 
     { 
      if (engine.LoadProcessDocument(String.Format(@"C:\TestDirectory\{0}", l))) 
      { 
       try 
       { 
        engine.Run();      
        WriteStep(String.Format("Ran {0} Succussfully", l)); 
       } 
       catch 
       { 
        WriteStep(String.Format("{0} Failed", l)); 
       } 

       engine.PrintProcess(); 
       WriteStep(String.Format("Rrinted {0} to debug", l)); 
      } 
     } 
    } 

    private delegate void WriteDelegate(string p); 
    private delegate ListBox.SelectedObjectCollection GetSelectedItemsDelegate(ListBox list); 

    private ListBox.SelectedObjectCollection GetSelectedItems(ListBox list) 
    { 
     return list.SelectedItems; 
    } 

ответ

1

Посмотрите на this SO question - он обращается к той же теме.

Во многих технологиях пользовательского интерфейса (Winforms, WPF, Silverlight) - элементы пользовательского интерфейса могут быть безопасно взаимодействовать с потоком пользовательского интерфейса. Это означает, что при написании многопоточного кода вам необходимо использовать механизмы в вашей библиотеке пользовательского интерфейса, чтобы правильно взаимодействовать с элементами управления пользовательского интерфейса. В WPF/Silverlight, который был бы Dispatcher, in WinForms, он должен использовать метод InvokeRequired и BeginInvoke() для отправки работ в поток пользовательского интерфейса.

В вашем случае, похоже, вы уже пытаетесь использовать BeginInvoke для отправки в соответствующую нить. Я подозреваю, что проблема заключается в том, что итерация по коллекции ListBox является самой кросс-потоковой операцией. Вы не знаете, как коллекция SelectedItems реализует GetEnumerator() - большинство likley, хотя оно не копирует коллекцию. Поэтому я подозреваю, что ваш код должен будет сделать копию перед ее повторением - или выполнить всю итерацию в потоке пользовательского интерфейса.

Выполнение копии коллекции не должно быть плохим, но опять же, копия должна быть сделана в потоке пользовательского интерфейса - и, возможно, она должна быть глубокой копией, поскольку она содержит другие объекты, принадлежащие UI (ListBoxItem). Если вы используете .NET 3.5, вы можете использовать LINQ для проецирования анонимного объекта в свой код, а не для создания глубоких копий элементов пользовательского интерфейса.

+0

Когда вы говорите, что LINQ используется для анонимного объекта в коде, вы имеете в виду нечто похожее на мой ответ? – Mike

+0

@Mike: Да. Вот именно то, что я имею в виду. – LBushkin

1

Вы должны использовать свойство InvokeRequired элемента управления, чтобы избежать вызова одного элемента управления из другого потока.

Проверьте, например this page

+0

Не совсем понимаю ваш ответ, поскольку я использую invoke, требуемый для lstProcessfiles – Mike

0

Вы alrady передавая выбранные элементы в этой строке:

bg.RunWorkerAsync(lstProcessFiles.SelectedItems);

почему вы пытаетесь получить их снова в методе DoWork?

Доступ их из DoworkEventArgs с:

var collection = (ListBox.SelectedObjectCollection)e.Argument

(Вы можете все еще нужно, чтобы скопировать выбранные объекты в обычный список, прежде чем позвонить в справочную работника, я не уверен, что объекты живут в этот специализированный тип коллекции)

+0

Да, извините, что остался по коду от попытки работать ... это не сработало. Спасибо что подметил это. – Mike

0

Я просто сделал копию строкового объекта, как это.Даже если это был более сложный объект, что-то вроде этого должно все еще работать

private List<string> GetSelectedItems(ListBox list) 
{ 
    return lstProcessFiles.SelectedItems.Cast<string>().ToList(); 
} 
Смежные вопросы