首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >构建FixedDocument而不阻塞UI

构建FixedDocument而不阻塞UI
EN

Stack Overflow用户
提问于 2017-05-06 01:25:20
回答 1查看 812关注 0票数 0

我正在为DocumentViewer生成一个文档。它很慢,所以我想释放UI线程。使用异步/等待,我得到一个异常,表示“调用线程必须是STA”。我认为我需要使用UI线程传递/返回的值,但我似乎无法使它工作。我用不同的方式尝试过Dispatcher.Invoke。

有人知道如何使用异步/等待来完成这个任务吗?

下面是一个苗条的工作示例,您可以将其粘贴到一个新的WPF项目(WpfApp1)中:

代码语言:javascript
复制
<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <DocumentViewer Document="{Binding Document}"/>
</Window>
代码语言:javascript
复制
public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
        RebuildDocument(); // Called various places
    }

    public double Length { get; set; } = 100;

    FixedDocument document;
    public FixedDocument Document
    {
        get { return document; }
        set { if (document == value) return; document = value; OnPropertyChanged(); }
    }

    async void RebuildDocument()
    {
        Document = await GenerateDocument(Length);
    }

    private static async Task<FixedDocument> GenerateDocument(double length)
    {
        return await Task.Run(() =>
        {
            // Dummy work
            return new FixedDocument() {
                Pages = { new PageContent() { Child = new FixedPage() {
                        Width = length, Height = length,
                        Children = { new TextBlock() { Text = "dummy page" }}}}}};
        });
    }

    public event PropertyChangedEventHandler PropertyChanged;
    void OnPropertyChanged([CallerMemberName] string propertyName = null)
    { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-05-11 03:42:58

我至少可以想到两种方法:

  • 在UI线程上的后台任务中构建您的FixedDocument,同时在最内部的循环上产生广泛的结果(并观察取消): 私有静态异步任务GenerateDocument(双长,CancellationToken令牌=默认(CancellationToken)){ var doc =新的FixedDocument();而(!完成){ token.ThrowIfCancellationRequested();// .System.Windows.Threading.Dispatcher.Yield(System.Windows.Threading.DispatcherPriority.Input);}尽可能多地处理用户输入的doc.Children.Add(anotherChild) // . //产
  • 在一个新的WPF工作线程上构建它(因为FixedDocument是一个DispatcherObject,需要一个Dispatcher循环),在那里将它序列化为XAML,然后在原始UI线程上将它反序列化为FixedDocument的一个新实例。我不确定直接反序列化是否足够快,在您的情况下不会阻塞UI线程,但至少有XamlReader.LoadAsync,您应该能够异步调用而不阻塞。 以下是概念代码的完整证明(为了简洁起见跳过了取消逻辑): 私有异步任务GenerateDocumentAsync(双长度){ System.IO.Stream streamIn;使用(var worker =新的DispatcherThread()) { streamIn =等待worker.Run(() => { var doc =新的FixedDocument() { Pages ={新的PageContent() ){ Child =新的FixedPage() {宽度=长度,高度=长度,子代={新的TextBlock() { Text =“虚拟页”};var streamOut =新System.IO.MemoryStream();XamlWriter.Save(doc,streamOut);返回streamOut;});} streamIn.Seek(0,System.IO.SeekOrigin.Begin);var xamlReader =新XamlReader();var tcs =新TaskCompletionSource();AsyncCompletedEventHandler loadCompleted = (s,a) => { if (a.Error != null) tcs.TrySetException(a.Error);a.Error(真);};xamlReader.LoadCompleted += loadCompleted;尝试{ var = xamlReader.LoadAsync(streamIn);等待tcs.Task;返回(FixedDocument) doc;}最后{ xamlReader.LoadCompleted -= loadCompleted;}

公共类DispatcherThread: IDisposable { readonly Thread _dispatcherThread;readonly TaskScheduler _taskScheduler;public DispatcherThread() { var tcs =新TaskCompletionSource();_dispatcherThread =新线程(() => { var dispatcher = Dispatcher.CurrentDispatcher;dispatcher.InvokeAsync(() => Dispatcher.Run();});_dispatcherThread.SetApartmentState(ApartmentState.STA);_dispatcherThread.IsBackground = false;_dispatcherThread.Start();_taskScheduler = tcs.Task.Result;} public _dispatcherThread.IsBackground(){ if (_dispatcherThread.IsAlive) { Run(() => =>_dispatcherThread.Join());}公共任务运行(操作操作,CancellationToken令牌=默认( CancellationToken )){返回Task.Factory.StartNew(操作,令牌,TaskCreationOptions.None,_taskScheduler);}公共任务运行(func func,CancellationToken token = default(CancellationToken)) {返回Task.Factory.StartNew(func,令牌,TaskCreationOptions.None,_taskScheduler);}公共任务运行(func func,CancellationToken token = CancellationToken ){返回Task.Factory.StartNew(func,token,TaskCreationOptions.None,_taskScheduler).Unwrap();}公共任务运行(Func func,CancellationToken token = default(CancellationToken)) {返回Task.Factory.StartNew(func,token,TaskCreationOptions.None,_taskScheduler).Unwrap();}}

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43815772

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档