在WPF中创建重型用户控件

4
我知道不能在UI线程中生成不同的线程并将其放入Visual Tree,因为这会引发异常,即不能访问该对象,因为不同的线程拥有它。
我的当前情况是我正在大量地生成UI控件运行时,比如说200个 (FrameworkContentElement) 控件,并将其添加到 DockWindow 中。是否可能在创建这些控件的同时不冻结UI,尝试将它们加载到UI线程中?我甚至不能显示进度对话框,因为那将使用UI线程,同时在另一个线程上进行工作是可以的,如果我需要处理数据并将其放入UI中,但这次我需要创建这些UI控件。
我考虑的一种方法是创建UI控件并将它们序列化到MemoryStream中,然后将它们加载到UI线程中,这里的一个问题是我必须重新附加DataContext到控件上,但是那时我可以将其委托给另一个线程。问题仍然是这样做是否可行?
我尝试混合Task和Thread对象以使ApartmentState变为STA,但还是没有成功。
public static Task<T> StartSTATask<T>(Func<T> func)
    {
        var tcs = new TaskCompletionSource<T>();
        Thread thread = new Thread(() =>
        {
            try
            {
                tcs.SetResult(func());
            }
            catch (Exception e)
            {
                tcs.SetException(e);
            }
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        return tcs.Task;
    }

编辑:这些控件再次是FrameworkContentElement,虚拟化控件在这种情况下不起作用。 这是使用FlowDocument控件在运行时创建控件。 比如Runs,Tables,Paragraphs等。 因此,在这种情况下ListBox,TreeViews等不适用。


你看过虚拟化控件了吗? - jamesSampica
是的,我知道那些控件,不幸的是它们是FrameworkContentElement而不是FrameworkElements,我正在将它们加载到RichTextBox/FlowDocumentScrollViewer控件中。 - 123 456 789 0
1个回答

2

对于渲染 200 个控件,WPF 在一台性能良好的计算机上可以处理几千个“基元”。

您可以在加载数据和解析数据时显示进度条。然后,如果需要,可以通过在非 UI 线程中循环处理数据并调用 UI 线程来实例化控件来限制创建 UI 元素的速度。您甚至可以通过小睡眠将实例化分开,以让屏幕渲染,但仅在 UI 非常繁重时使用...

...话虽如此——如果您的 UI 如此繁重,那么您可能设计得不正确。问题不应该是“在我的 UI 减速到拖动之前我可以放置多少 UI 元素?”而是“最少需要多少个活动 UI 元素才能完成工作?”。

“活动”一词指的是 listview 所采取的方法,其中实际项目是虚拟化的 - 它们只在需要时创建并在不可见时处置。因此,如果您的 UI 允许,则可以考虑使用虚拟化容器(例如 ListView)而不是 DockPanel;

如果您能提供具体的 UI 元素示例,我可以进一步阐述。


我刚刚更新了我的问题,我也是这么想的,但不幸的是创建所有运行时控件需要一些时间。有一个(Xaml)模板被呈现为FlowDocument,然后将所有占位符控件替换为适当的控件。这有点像我们在运行时进行映射替换的过程。 - 123 456 789 0
我写了一个docx->FlowDocument解析器,遇到了同样的问题 - FlowDocument的性能太差了!尽量使用扁平结构(不要在FlowDocument中放置画布来容纳表格:))。隐藏FlowDocument,加载内容然后将其显示出来,在外观上假装有内容进入,给用户一种有东西要来的感觉。 - Sten Petrov
我在FlowDocument/任何地方都没有画布。我只是在创建文档时使用适当的控件,只是我们必须在运行时替换这些控件,这需要时间。 - 123 456 789 0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接