Как создать элементы управления WPF в фоновом потоке?
У меня есть метод, который создает фоновый поток, чтобы сделать некоторые действия. В этом фоновом потоке я создаю объект. Но этот объект при создании во время выполнения дает мне исключение:
вызывающий поток должен быть STA, потому что многие компоненты пользовательского интерфейса требуют этого.
Я знаю, что я должен использовать Dispatcher, чтобы отразить что-то в UI. Но в этом случае я просто создаю объект и не итерацию с 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
8 ответов
TreeView
является элементом управления UI. Вы можете создавать элементы управления пользовательского интерфейса и управлять ими только в потоке пользовательского интерфейса, поэтому то, что вы пытаетесь сделать, невозможно.
то, что вы хотите сделать, это сделать всю трудоемкую работу в фоновом потоке, а затем "перезвонить" в поток пользовательского интерфейса для управления пользовательским интерфейсом. Это на самом деле довольно легко:
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
(это с моей головы), но вы все равно идете...
HTH:
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);
}
никто не обсуждает случай отдельного потока 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");
}
потому что сейчас мы держим визуальное дерево в том же исходном потоке.
чтобы ваш код просто работал, вы должны присоединиться к STA COM
квартиры по телефону Thread.SetApartmentState(ApartmentState.STA)
. С BackgroundWorker
вероятно, использует некоторый общий пул потоков, присоединение к определенной квартире может повлиять на других пользователей этого пула потоков или даже может завершиться неудачей, если он уже установлен, например MTA
раньше. Даже если все получилось, ваш вновь созданный TreeView
будет заблокирован для этого рабочего потока. Вы не сможете использовать его в своем основном потоке пользовательского интерфейса.
если вы объяснили немного больше подробно о ваших истинных намерениях, вы наверняка получите лучшую помощь.
попробовать следующий код:
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
}
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);
};
}
Я решил свою проблему. Я просто использовал e.Свойство Result метода RunWorkerCompleted. Я получаю данные в фоновом потоке, а затем использую эти данные, когда поток завершен. Спасибо каждое тело для полезных методов. Отдельное спасибо Veer, чтобы дать рекомендацию о e.Result
собственность.
Посмотреть ответ на этот вопрос: Как запустить что-то в потоке STA?
когда вы определяете свой поток, установите ApartmentState в STA:
thread.SetApartmentState(ApartmentState.STA);
это должно сделать трюк!