2010-05-04 2 views
10

У меня есть метод, который создает фоновый поток, чтобы сделать какое-то действие. В этом фоновом потоке я создаю объект. Но этот объект при создании во время выполнения дает мне исключение:Как создать элементы управления WPF в фоновом потоке?

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

Я знаю, что я должен использовать диспетчер, чтобы что-то отразить UI. Но в этом случае я просто создаю объект и не выполняю его с пользовательским интерфейсом. Это мой код:

public void SomeMethod() 
     { 
     BackgroundWorker worker = new BackgroundWorker(); 
     worker.DoWork += new DoWorkEventHandler(Background_Method); 
     worker.RunWorkerAsync(); 
     } 

    void Background_Method(object sender, DoWorkEventArgs e) 
     { 
     TreeView tv = new TreeView(); 
     } 

Как создать объект в фоновом режиме?

Я использую приложение WPF

+0

Еще один вопрос: возможен ли метод Background Worker вернуть некоторое значение определенного типа? – Polaris

+2

Проверьте свойство e.Result в методе RunWorkerCompleted. http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx. – Amsakanna

ответ

6

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

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

void Background_Method(object sender, DoWorkEventArgs e) 
{ 
    // ... time consuming stuff... 

    // call back to the window to do the UI-manipulation 
    this.BeginInvoke(new MethodInvoker(delegate { 
     TreeView tv = new TreeView(); 
     // etc, manipulate 
    })); 
} 

я, возможно, получил синтаксис неправильный для BeginInvoke (это с верхней части моей головы), но там вы идете в любом случае ...

+0

Я получаю некоторые данные из веб-службы, и время выполнения тратит на это много времени. Вот почему я хочу получить данные в фоновом режиме и генерировать свой treeView, когда данные будут готовы. – Polaris

+0

Я обновил свой ответ еще несколькими комментариями о том, как вы можете делать материал в потоке пользовательского интерфейса из рабочего потока. –

0

Чтобы сделать код просто работать , вы должны присоединиться к квартире STA COM по телефону Thread.SetApartmentState(ApartmentState.STA). Поскольку BackgroundWorker, вероятно, использует пул общих потоков, присоединение к конкретной квартире может повлиять на других пользователей этого пула потоков или может даже потерпеть неудачу, если оно уже установлено, например. MTA раньше. Даже если все это сработает, ваш новый созданный TreeView будет заблокирован для этого рабочего потока. Вы не сможете использовать его в своем основном потоке пользовательского интерфейса.

Если вы более подробно объяснили свои истинные намерения, вы, несомненно, получите лучшую помощь.

0

Try следующий код:

public void SomeMethod() 
{ 

System.ComponentModel.BackgroundWorker myWorker = new System.ComponentModel.BackgroundWorker(); 

myWorker.DoWork += myWorker_DoWork; 

myWorker.RunWorkerAsync(); 

} 

private void myWorker_DoWork(object sender, 
    System.ComponentModel.DoWorkEventArgs e) 
{ 
    // Do time-consuming work here 
} 
3

НТН:

void Background_Method(object sender, DoWorkEventArgs e) 
    { 
     // Time Consuming operations without using UI elements 
     // Result of timeconsuming operations 
     var result = new object(); 
     App.Current.Dispatcher.Invoke(new Action<object>((res) => 
      { 
       // Working with UI 
       TreeView tv = new TreeView(); 
      }), result); 
    } 
0
void Background_Method(object sender, DoWorkEventArgs e) 
{ 
    TreeView tv = new TreeView(); 
    // Generate your TreeView here 
    UIDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => 
    { 
     someContainer.Children.Add(tv); 
    }; 
} 
0

я решил мою проблему. Я просто использовал свойство e.Result метода RunWorkerCompleted. Я получаю данные в фоновом потоке, а затем использую эти данные, когда поток завершен. Спасибо всем органам за полезные методы. Особая благодарность Veer дать рекомендацию о недвижимости e.Result.

+0

Если вы хотите обновить свой пользовательский интерфейс через определенные промежутки времени, вы можете использовать параметр UserState для параметра ReportProgress для отправки ваших данных и использования их в методе ProgressChanged путем перевода e.UserState в требуемый тип. – Amsakanna

0

Смотрите ответ на этот вопрос: How to run something in the STA thread?

Когда вы определяете нить, установите ApartmentState в ГНА:

thread.SetApartmentState(ApartmentState.STA); 

Это должно сделать трюк!

0

Никто не обсуждает случай отдельной ветки STA в деталях (даже если концепция абсолютно такая же).

Итак, давайте представим себе простое управление вкладки добавлены на кнопку мыши

private void button_Click(object sender, RoutedEventArgs e) 
    { 
     TabItem newTab = new TabItem() { Header = "New Tab" }; 
     tabMain.Items.Add(newTab); 
    } 

Если переместить его в другую STA нить

private void button_Click(object sender, RoutedEventArgs e) 
    { 
     Thread newThread = new Thread(new ThreadStart(ThreadStartingPoint)); 
     newThread.SetApartmentState(ApartmentState.STA); 
     newThread.IsBackground = true; 
     newThread.Start(); 
    } 
    private void ThreadStartingPoint() 
    { 
     TabItem newTab = new TabItem() { Header = "New Tab" }; 
     tabMain.Items.Add(newTab); 
    } 

конечно мы получаем System.InvalidOperationException

Теперь, что произойдет, если мы добавим контроль

private void AddToParent(string header) 
    { 
     TabItem newTab = new TabItem() { Header = header }; 
     tabMain.Items.Add(newTab); 
    } 

с использованием метода делегата?

public void DelegateMethod(string header) 
    { 
     tabMain.Dispatcher.BeginInvoke(
       new Action(() => { 
        this.AddToParent(header); 
       }), null); 
    } 

это работает, если вы называете это

private void button_Click(object sender, RoutedEventArgs e) 
    { 
     Thread newThread = new Thread(new ThreadStart(ThreadStartingPoint)); 
     newThread.SetApartmentState(ApartmentState.STA); 
     newThread.IsBackground = true; 
     newThread.Start(); 
    } 
    private void ThreadStartingPoint() 
    { 
     DelegateMethod("new tab"); 
    } 

потому что, конечно, в настоящее время мы продолжаем визуальное дерево в той же исходной нити.

Смежные вопросы