微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

来自进程的 C# ReactiveUI MessageBus 在进程结束之前不会更新 ListBox

如何解决来自进程的 C# ReactiveUI MessageBus 在进程结束之前不会更新 ListBox

我有一个启动进程的 .NET 5 ReactiveUI 应用程序。我正在尝试从进程获取实时日志到列表框。我在获取日志方面取得了一些成功,但只有在过程结束之后;然后我一次获得所有日志。我观察到我创建的 MessageBus 侦听器被实时调用,但 UI 直到进程结束后才反映更新。下面是一些代码片段。

任何帮助将不胜感激。提前致谢!

流程代码

public void Execute(Payload payload)
{
    using Process process = new()
    {
        StartInfo = CreateStartInfo(payload)
    };

    process.ErrorDataReceived += delegate(object sender,DataReceivedEventArgs e)
    {
        MessageBus.Current.SendMessage(new LogEventArgs(e.Data));
    };
    process.OutputDataReceived += delegate(object sender,DataReceivedEventArgs e)
    {
        MessageBus.Current.SendMessage(new LogEventArgs(e.Data));
    };

    MessageBus.Current.SendMessage(new LogEventArgs("Starting Process."));
    process.Start();
    process.BeginoutputReadLine();
    process.BeginErrorReadLine();

    StreamWriter myStreamWriter = process.StandardInput;
    CancellationTokenSource tokenSource = new();
    CancellationToken token = tokenSource.Token;


    Task.Run(() =>
                {
                    while (true)
                    {
                        myStreamWriter.WriteLine();
                        Thread.Sleep(250);
                        if (token.IsCancellationRequested)
                        {
                            break;
                        }
                    }
                },tokenSource.Token);

    process.WaitForExit();
    MessageBus.Current.SendMessage(new LogEventArgs("Process Finished."));
    tokenSource.CancelAfter(500);
}

private static processstartinfo CreateStartInfo(Payload payload)
{
    string applicationPath = Path.Combine(payload.Directory,"Application");
    return new processstartinfo
    {
        FileName = $"{applicationPath}.exe",WindowStyle = ProcessWindowStyle.Hidden,UseShellExecute = false,RedirectStandardError = true,RedirectStandardOutput = true,RedirectStandardInput = true
    };
}

视图模型代码

public class LogPaneviewmodel : ReactiveObject
    {
        public ListBox LogListBox { get; set; }


        [Reactive]
        public ObservableCollection<string> LogsList { get; set; } = new();

        [Reactive]
        public ReactiveCommand<Unit,Unit> ClearLogs { get; set; }

        public LogPaneviewmodel()
        {
            MessageBus.Current.Listen<LogEventArgs>()
                      .Subscribe(OnLogMessageReceived);

            ClearLogs = ReactiveCommand.Create(OnClearLogClicked);
        }

        private void OnLogMessageReceived(LogEventArgs args)
        {
            // *** This is called in real time by the process.
            // But the ListBox is not updating until the process ends.
            LogsList.Add(args.Text);
            LogListBox.ScrollIntoView(args.Text);
        }


        private void OnClearLogClicked()
        {
            LogsList.Clear();
        }
    }

代码背后的代码

    public partial class LogPane : ReactiveUserControl<LogPaneviewmodel>
    {
        public LogPane()
        {
            InitializeComponent();

            viewmodel = App.ServiceProvider.GetrequiredService<LogPaneviewmodel>();
            viewmodel.LogListBox = LogsList;

            this.WhenActivated(disposableRegistration =>
            {
                this.OneWayBind(viewmodel,vm => vm.LogsList,view => view.LogsList.ItemsSource,s => s)
                    .disposeWith(disposableRegistration);

                this.BindCommand(viewmodel,vm => vm.ClearLogs,view => view.ClearButton)
                    .disposeWith(disposableRegistration);
            });
        }
    }

解决方法

我的进程代码一直等到进程完成。这导致线程阻塞 UI 线程。正如 GlennWatson 建议和 mm8 所暗示的,这是通过在 Task.Run() 中包装该类的启动来解决的。感谢您的帮助!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。