2017-04-23 39 views
-1

我想在另一個線程中創建一個UserControl。我嘗試了1001種方法,但我無法做到。我有幾個錯誤,最常見的是:「調用線程必須是STA,因爲許多UI組件都需要它。」在其他線程中創建UserControl

STA錯誤,我已經解決了其他類型的東西,例如從另一個線程創建和顯示錶單,但這是不一樣的,我需要一個方法來返回創建的UserControl。我有類似的東西

private async void SetView<T>() where T : UserControl, new() 
{ 
    // SHOW LOADING 

    CurrentView = await Task.Run(() => new T()); 

    // HIDE LOADING 
} 
+0

看到這個:http://stackoverflow.com/questions/12387687/is-it-possible-to-initialize-wpf-usercontrols-in-different-threads/ 12389044#12389044 ...當你使用任務...如果它計劃在後臺運行....它使用線程池線程...這將已經初始化與MTA(多線程公寓已經) 。一旦初始化到特定的公寓,它就不能改變......並且即使可以,也不想改變它的公寓,因爲線程池中的線程被重用。 –

+0

創建控件時花了那麼長時間?一個控件通常不需要很長的時間來加載...控件是否可能加載了某種您可能能夠單獨加載的數據? – bassfader

回答

-1

將值從一個線程傳遞到另一個線程的最簡單的方法是利用文件。您將該值序列化爲一個文件,然後將其反序列化。因此,您必須使用System.Windows.Markup.XamlWriter/XamlReader類將UserControl保存到文件並將其加載回去。

下面的示例演示了這一點。您可以使用System.IO.Path.GetTempFileName()方法保存到臨時文件。

<Grid> 
    <Button Content="Create on new thread" HorizontalAlignment="Left" Margin="25,25,0,0" VerticalAlignment="Top" Width="126" Click="Button_Click_1"/> 
    <Button Content="Deserialize" HorizontalAlignment="Left" Margin="25,63,0,0" VerticalAlignment="Top" Width="126" Click="Button_Click_2"/> 
    <Label x:Name="Lbl" Margin="25,127,0,0" VerticalAlignment="Top" Height="105" Width="220"/>   
</Grid> 

代碼:

private void Button_Click_1(object sender, RoutedEventArgs e) 
{ 
    Task.Factory.StartNew(() => { StartSTATask(create); }); 
} 

bool create() 
{ 
    Button btn = new Button(); 
    btn.Content = "Press me"; 

    using (var stream = System.IO.File.Create(@"g:\\button.xaml")) 
     System.Windows.Markup.XamlWriter.Save(btn, stream); 

    return true; 
} 

public static Task<bool> StartSTATask(Func<bool> func) 
{ 
    var tcs = new TaskCompletionSource<bool>(); 
    var thread = new Thread(() => 
    { 
     try 
     { 
      var result = func(); 
      tcs.SetResult(result); 
     } 
     catch (Exception e) 
     { 
      tcs.SetException(e); 
     } 
    }); 
    thread.SetApartmentState(ApartmentState.STA); 
    thread.Start(); 
    return tcs.Task; 
} 

private void Button_Click_2(object sender, RoutedEventArgs e) 
{ 
    using (var stream = System.IO.File.OpenRead(@"g:\\button.xaml")) 
    { 
     Button btn = System.Windows.Markup.XamlReader.Load(stream) as Button; 
     Lbl.Content = btn; 
    } 

}