使用ReactiveUI、Observables、SubscribeOn和ObserveOn在长时间运行的过程中显示输出日志到UI界面

3
我有一个WPF应用程序,将启动一个长时间运行的任务(60秒以上),该任务使用System.Reactive.Subject<string>周期性地推送状态消息。我的想法是,我可以从ViewModel订阅可观察对象,并通过数据绑定使ReactiveUI自动更新我的UI。这一切都很好,但是TextBox只有在长时间运行的任务完成后才会实时更新。我认为这是因为我的UI线程被阻塞了,无法更新。
在此假设下,我的研究表明,我可以使用SubscribeOn将订阅放在后台线程上,然后使用ObserveOnDispatcher将通知推回UI线程。然而,这仍然没有产生我想要的结果--UI只在长时间运行的任务返回后更新。
有人能给我一些见解,告诉我需要做什么才能使输出日志实时更新吗?以下是相关的代码片段。
XAML:
<TextBox Grid.Row="1" Text="{Binding Output}" IsReadOnly="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Margin="10,0,10,10" x:Name="OutputTextBox" />

代码后置:

protected override void OnContentRendered(EventArgs e)
        {
            if (Converter == null) return;
            _viewModel = new ConversionOutputWindowViewModel(Converter);
            DataContext = _viewModel;
            _viewModel.StartConversion(); // Long-running Task
            //_viewModel.StartSave();
            FinishButton.IsEnabled = true;
        }

视图模型:

private string _output;

public string Output // Data bound in XAML
        {
            get { return _output; }
            set { this.RaiseAndSetIfChanged(ref _output, value); }
        }

public void StartConversion()
        {
            _edmxConverter.Convert(); // Long-running Task
        }

public ConversionOutputWindowViewModel(Utilities.Converters.EdmxConverter converter)
        {
            _edmxConverter = converter;
            _compositeDisposable.Add(_edmxConverter.Output
                                                   .SubscribeOn(NewThreadScheduler.Default)
                                                   .ObserveOnDispatcher()
                                                   .Subscribe(s => Output = Output += s));
            //_compositeDisposable.Add(_edmxConverter.Output.Subscribe(s => Output = Output += s));
        }

长时间运行的任务函数:

public Subject<string> Output { get; }

Output = new Subject<string>(); //In ctor

private void PrintReplacement(XAttribute attribute, string oldValue, string newValue, int level, Verbosity minVerbosity = Verbosity.Informational)
        {
            if (Verbosity < minVerbosity) return;
            Output.OnNext($"{new string('\t', level)}{attribute.Name}: {oldValue} -> {newValue}{Environment.NewLine}");
        }

如果我把长时间运行的任务函数调用放在await Task.Run里面,会不会有所帮助?我现在是试图寻找解决方法。我对.NET线程的工作原理了解不太深入。

1个回答

0

你正在使用主题,因此.SubscribeOn(NewThreadScheduler.Default)没有任何作用(老生常谈)。

你的长时间运行的过程应该是一个方法调用,返回一个 IObservable<T> (而不是公开属性的类)。T 应该是您想要接收的状态更新。当任务完成时,然后调用 OnComplete。如果任务失败,则调用 OnError。

理想情况下,返回 IObservable 的方法应使用 Observable.Create 来定义需要完成的工作。

public IObservable<string> Convert()
{
    return Observable.Create<string>(observer=>
    {
        //do stuff here.
        //Call observer.OnNext with status messages
        //When done call observer.OnCompleted()
    });
}

由于您没有展示您的长时间运行任务,我无法在实现方面提供更多帮助。


您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Nick Alexander

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