2010-05-04 69 views
10

我有創建後臺線程做一些行動的方法。在這個後臺線程中,我創建了對象。但是這個對象在運行時創建時給我一個例外:如何在後臺線程中創建WPF控件?

調用線程必須是STA,因爲很多UI組件都需要這個。

我知道我必須使用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應用

+0

還有一個問題:Background Worker方法有可能返回某些特定類型的值嗎? – Polaris 2010-05-04 07:08:37

+2

檢查RunWorkerCompleted方法中的e.Result屬性。 http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx。 – Amsakanna 2010-05-04 07:18:55

回答

6

TreeView是UI控制。你只能在UI線程上創建和操作UI控件,所以你試圖做的事情是不可能的。

你想要做的是在後臺線程上完成所有耗時的工作,然後「回調」到UI線程來操作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(這是把我的頭頂部),但你去那裏反正...

+0

我從Web服務獲取一些數據,運行時花費大量時間。這就是爲什麼我想在後臺獲取數據並在數據準備就緒時生成我的treeView。 – Polaris 2010-05-04 07:01:17

+0

我已經更新了我的答案,並提供了一些關於如何從工作線程在UI線程中執行某些操作的註釋。 – 2010-05-04 07:14:59

0

爲了使您的代碼只是工作,您必須致電Thread.SetApartmentState(ApartmentState.STA)加入STA COM公寓。由於BackgroundWorker可能使用某個共享線程池,因此加入特定公寓可能會影響此線程池的其他用戶,或者如果它已被設置爲例如「 MTA之前。即使全部解決,新創建的TreeView也會被鎖定到該工作線程。您將無法在主UI線程中使用它。

如果您更詳細地解釋了您的真實意圖,您一定會得到更好的幫助。

0

嘗試下面的代碼:

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

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); 
    } 
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

我解決我的問題。我只是使用RunWorkerCompleted方法的e.Result屬性。我在後臺線程中獲取數據,然後在線程完成時使用這些數據。感謝所有有用的方法。特別感謝Veer提供關於e.Result財產的建議。

+0

如果要以某些間隔更新UI,可以使用ReportProgress方法的UserState參數發送數據,並通過將e.UserState轉換爲所需的類型在ProgressChanged方法中使用它們。 – Amsakanna 2010-05-04 08:56:15

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"); 
    } 

原因當然現在我們保持視覺樹在同原來的線程。

相關問題