Как создать элементы управления 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);

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