TaskFactory新用户界面创建 [英] TaskFactory New UI Creation

查看:147
本文介绍了TaskFactory新用户界面创建的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用TaskFactory创建新的UI元素?当我尝试时,出现以下错误:

调用线程必须是STA,因为许多UI组件都需要STA.

示例代码

Dim txtBoxList as new List(Of TextBox)

Sub StartThread()
    Dim TS As TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
    Task.Factory.StartNew(Sub() CreateControl(), TS)
End Sub

Sub CreateControl()
    Dim txtBox As New TextBox
    Dispatcher.BeginInvoke(Sub() txtBoxList.Add(txtBox))
End Sub

解决方案

如果您使用的是WPF,则确实需要抛弃您可能从古代技术中学到的所有概念,并理解和接受WPF心态 .

基本上,您几乎从不需要在WPF的过程代码中创建或操作UI元素.相反,WPF使自己大量使用 数据绑定 .

WPF线程模型 不能不允许允许您在后台线程中创建或操作UI元素的实例,并将其添加到

无论如何,这种事情几乎为零,因为在大多数情况下,创建UI元素是一项琐碎的任务,可以(并且必须)由UI线程执行.

您不必担心可视树,而应该专注于将 Data 加载到后台线程中,然后作为 DataContext 到用户界面,以便它可以显示您的数据

这是一个使用 ItemsControl 显示用户列表,这些用户在后台线程中异步加载,然后分派到UI线程进行显示:

<Window x:Class="WpfApplication7.AsyncItemsControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ItemsControl ItemsSource="{Binding}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border Background="LightGray" BorderBrush="Black" BorderThickness="1" Margin="2">
                    <StackPanel>
                        <TextBlock Text="{Binding LastName}" Margin="2"/>
                        <TextBlock Text="{Binding FirstName}" Margin="2"/>
                    </StackPanel>
                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Window>

后面的代码:

public partial class AsyncItemsControl : Window
{
    public AsyncItemsControl()
    {
        InitializeComponent();

        var dispatcher = TaskScheduler.FromCurrentSynchronizationContext();

        Task.Factory.StartNew(() => GetUsers())
                    .ContinueWith(x => DataContext = x.Result,dispatcher);

    }

    public List<User> GetUsers()
    {
        // pretend this method calls a Web Service or Database to retrieve the data, and it takes 5 seconds to get a response:
        Thread.Sleep(5000);

        return new List<User>
        {
            new User() {FirstName = "Marty", LastName = "McFly"},
            new User() {FirstName = "Emmett", LastName = "Brown"},
            new User() {FirstName = "Bufford", LastName = "Tannen"}
        };
    }
}

数据项:

public class User
{
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

结果:

  • 请注意,该示例使用DataBinding,并且不会在过程代码中创建或操作UI元素,而是使用具有简单string属性的简单User类进行操作.

  • 还请注意,在5秒钟的加载"时间内,UI响应,因为实际工作是由后台线程执行的.

  • 这种方法允许UI与数据之间更大的分离,从而无需更改基础业务/应用程序逻辑即可实现UI更大的可伸缩性和可定制性.

  • 注意ItemsControl如何处理创建和呈现显示3个数据项所需的适当UI元素的工作. 每个项目的外观"的实际定义是 DataTemplate .

  • 我强烈建议阅读未通过此答案链接的材料,以更深入地了解WPF的总体工作原理.

  • 侧面说明:如果您以C#5.0为目标,则可以利用async / await并通过删除所有基于Task的内容使此代码更整洁.我使用的是C#4.0,因此该功能对我不可用.

  • WPF Rocks..只需将我的代码复制并粘贴到File -> New Project -> WPF Application中,然后亲自查看结果.

  • 让我知道是否需要进一步的帮助.

How do I create a new UI element using TaskFactory? When I try I get the following error :

The calling thread must be STA, because many UI components require this.

Example Code

Dim txtBoxList as new List(Of TextBox)

Sub StartThread()
    Dim TS As TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
    Task.Factory.StartNew(Sub() CreateControl(), TS)
End Sub

Sub CreateControl()
    Dim txtBox As New TextBox
    Dispatcher.BeginInvoke(Sub() txtBoxList.Add(txtBox))
End Sub

解决方案

If you're working with WPF, you really need to leave behind any and all notions you might have learned from ancient technologies and understand and embrace The WPF Mentality.

Basically, you almost never need to create or manipulate UI elements in procedural code in WPF. Instead, WPF lends itself to heavily use DataBinding.

The WPF Threading Model does not allow you to create or manipulate instances of UI elements in background threads, and add them to the Visual Tree which was created by the "main" UI thread.

Anyways, there is almost zero need for such a thing, because creating UI elements is in most cases a trivial task which can (and must) be performed by the UI Thread.

rather than worrying about the Visual Tree, you should concentrate on having your Data loaded in background threads, and then passed as a DataContext to the UI so that it can display your data accordingly.

This is a small example which uses an ItemsControl to display a list of users, which are loaded asynchronously in a background thread and then dispatched to the UI thread for display:

<Window x:Class="WpfApplication7.AsyncItemsControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ItemsControl ItemsSource="{Binding}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border Background="LightGray" BorderBrush="Black" BorderThickness="1" Margin="2">
                    <StackPanel>
                        <TextBlock Text="{Binding LastName}" Margin="2"/>
                        <TextBlock Text="{Binding FirstName}" Margin="2"/>
                    </StackPanel>
                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Window>

Code Behind:

public partial class AsyncItemsControl : Window
{
    public AsyncItemsControl()
    {
        InitializeComponent();

        var dispatcher = TaskScheduler.FromCurrentSynchronizationContext();

        Task.Factory.StartNew(() => GetUsers())
                    .ContinueWith(x => DataContext = x.Result,dispatcher);

    }

    public List<User> GetUsers()
    {
        // pretend this method calls a Web Service or Database to retrieve the data, and it takes 5 seconds to get a response:
        Thread.Sleep(5000);

        return new List<User>
        {
            new User() {FirstName = "Marty", LastName = "McFly"},
            new User() {FirstName = "Emmett", LastName = "Brown"},
            new User() {FirstName = "Bufford", LastName = "Tannen"}
        };
    }
}

Data Item:

public class User
{
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

Result:

  • Notice that the example uses DataBinding and it does not create or manipulate UI elements in procedural code, but rather operates with a simple User class with simple string properties.

  • Also notice that during the 5 seconds "load" time, the UI is responsive because the actual work is being performed by a background thread.

  • This approach allows a greater separation between UI and data which allows a much greater scalability and customizability of the UI without having to change the underlying business / application logic.

  • Notice how the ItemsControl takes care of creating and rendering the appropiate UI elements needed to display the 3 Data Items. The actual definition of "how each item looks" is the DataTemplate.

  • I strongly recommend reading the material linked thoughout this answer for more in-depth understanding of how WPF works in general.

  • Side note: if you're targetting C# 5.0 you can leverage async / await and make this code cleaner by removing all the Task based stuff. I'm on C# 4.0 so that feature is not available for me.

  • WPF Rocks. Just copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.

  • Let me know if you need further help.

这篇关于TaskFactory新用户界面创建的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆