C# 如何在另一个线程中初始化重载控件?

C# 如何在另一个线程中初始化重载控件?,c#,multithreading,xaml,windows-runtime,C#,Multithreading,Xaml,Windows Runtime,我举起两个用户控件。 第一个包含一个ItemsControl,我必须用第二个用户控件动态填充它。第二个用户控件可能递归地包含另一个ÌtemsControl本身,因此初始化它是一个相当繁重的控件 下面是我为子控制所做的: public sealed partial class SubControl : UserControl { public SubControl(ModelA item) { this.InitializeComponent();

我举起两个用户控件。 第一个包含一个
ItemsControl
,我必须用第二个用户控件动态填充它。第二个用户控件可能递归地包含另一个
ÌtemsControl
本身,因此初始化它是一个相当繁重的控件

下面是我为
子控制所做的:

public sealed partial class SubControl : UserControl
{
    public SubControl(ModelA item)
    {
        this.InitializeComponent();
        this.DataContext = item;

        //if the current item has child items, add them to the ItemsControl:
        if(item.SubItems != null)
        {
            BuildUI(item.SubItems);
        }
    }

    private void BuildUI(List<AbstractModel> data)
    {
        foreach(var item in data)
        {
            var dataItem = item as ModelA;
            //we only want item of type ModelA in our ItemsControl:
            if(dataItem != null)
            {
                SubElements.Items.Add(new SubControl(dataItem));
            }
        }
    }
}
public sealed partial class MainControl : UserControl
{
    public MainControl(List<AbstractModel> data)
    {
        this.InitializeComponent();
        BuildUI(data);
    }

    private void BuildUI(List<AbstractModel> data)
    {
        foreach(var item in data)
        {
            var dataItem = item as ModelA;
            if(dataItem != null)
            {
                Elements.Items.Add(item);
            }
        }
    }
}
在构建这棵“树”的过程中,我注意到UI出现了一些小的冻结,但我目前使用的是一台功能非常强大的计算机。我希望该应用程序即使在Windows Surface RT等较小的设备上也能平稳运行。因此,我更改了
MainControl
code:

public sealed partial class MainControl : UserControl
{
    public MainControl(List<AbstractModel> data)
    {
        this.InitializeComponent();
        BuildUI(data);
    }

    private async void BuildUI(List<AbstractModel> data)
    {
        var list = new List<SubControl>();
        await Task.Run(() =>
        {
            foreach(var item in data)
            {
                var dataItem = item as ModelA;
                if(dataItem != null)
                {
                    list.Add(new SubControl(dataItem));
                }
            }
        });
        foreach(var item in list)
        {
            Elements.Items.Add(item);
        }
    }
}
公共密封部分类MainControl:UserControl
{
公共主控(列表数据)
{
this.InitializeComponent();
BuildUI(数据);
}
专用异步void BuildUI(列表数据)
{
var list=新列表();
等待任务。运行(()=>
{
foreach(数据中的var项)
{
var dataItem=作为ModelA的项目;
if(dataItem!=null)
{
清单.增加(新分包合同(数据项));
}
}
});
foreach(列表中的变量项)
{
元素。项。添加(项);
}
}
}
我们的想法是在不同的线程中构建所有的
子控件
,这样UI就不会被阻塞,当所有的用户控件都初始化后,我们会将它们添加到
MainControl
中的
ItemsControl

但是,由于数据的编组,这不起作用,即使UI上实际上没有一个
子控制
!它在构建
子控件时崩溃,这真的很奇怪,因为它对实际的UI没有任何影响;它们只是添加到一个临时的
列表中


在后台任务中构建这些用户控件以使UI不冻结有什么诀窍?

有很多方法可以做到这一点

您可以使用以下工作流:

  • 实现在初始化完成时引发的EventHandler
  • 显示一个空的主控件,并说“正在加载”
  • 在另一个线程中构建所有UI
  • 生成所有UI时,引发finish事件
  • 此事件的侦听器将向MainControl添加子控件
[…]因为它对实际用户界面没有任何影响;它们只是添加到临时列表中


顺便说一句:您的代码确实会崩溃,因为您是在一个异步方法中添加控件的,该方法实际上是一个非GUI线程。所以你的观点是错误的。

Windows UI是非常单线程的。每个UI控件都必须创建,并且只能从单个线程使用。这是没有办法的

所以,是时候换一种方式来考虑解决方案了。创建几十个控件是没有问题的;UI可以很好地处理这个问题。您正在谈论向列表控件添加数百或数千项,而这只是一个不可用的UI。因此,正确的解决方案是重新思考您的UI设计。也许你可以把结果分成几类


如果您已经考虑过UI设计,并且仍然确定要向用户显示数百或数千项,那么答案就是使用。这比简单的
foreach
循环更难编码,但这是有效显示大量数据的唯一方法。

您建议在“另一个线程”上构建UI,同时注意不能在非UI线程上添加控件,这与您自己相矛盾。