0

I am trying to create a UserControl in another thread. I tried 1001 ways but I can not do it. I have several errors, the most common is: "The call thread must be STA, since many UI components require it."

The STA error, I have solved it for other types of things, for example create and SHOW a form from another thread, but this is not the same, I need a method to return the created UserControl. I have something like that

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

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

    // HIDE LOADING
}
avechuche
  • 1,470
  • 6
  • 28
  • 45
  • See this: http://stackoverflow.com/questions/12387687/is-it-possible-to-initialize-wpf-usercontrols-in-different-threads/12389044#12389044 ... also when you use Task...if it's scheduled to run in the background....it makes use a Thread Pool thread...which would have been initialized with MTA (multi-threaded apartment already). Once initialized to a particular apartment, it can't be changed...and you wouldn't want to change its apartment even if you could, as Threads in the Thread Pool get reused. – Colin Smith Apr 23 '17 at 13:56
  • What is taking so long when creating the control? A control should usually not take very long to load... Is the control maybe loading some kind of data that you might be able to load separately? – bassfader Apr 24 '17 at 08:18

1 Answers1

-1

Easiest way to pass a value from one thread to another is to make use of files. You serialize the value to a file, and then you deserialize it. So, you have to use System.Windows.Markup.XamlWriter/XamlReader classes to save the UserControl to a file and load it back.

Below sample demonstrates this. You can use System.IO.Path.GetTempFileName() method for saving to a temp file.

<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>

Code :

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

}
AnjumSKhan
  • 9,647
  • 1
  • 26
  • 38