【问题标题】:C# - WPF - Updating the UI from another class on another ThreadC# - WPF - 从另一个线程上的另一个类更新 UI
【发布时间】:2020-10-15 04:17:33
【问题描述】:

我浏览了互联网,发现了一个不错的解决方案,我将其合并到下面的代码中,但是它并不能完全满足我的要求,它仅在调用更新时有效,但我想在另一个中运行一个方法类然后让该方法调用将向 UI 报告的方法并传递一些信息,因此这个模型只是在运行操作之前更改按钮内容。

使用调度程序,我可以获得要更新的 UI 控件,但是我不只是希望这样做,我想执行一些功能然后进行 UI 更新。 所以可能有一些我没有得到的理论,我知道 Invoke 是一个同步操作并且突破了它运行但 UI 没有更新的代码。

主窗口 有一个按钮,内容为“CLICK ME”

代码背后

public partial class MainWindow : Window
    {
        public static Button windowButton;
        
        public MainWindow()
        {
            InitializeComponent();
            windowButton = btnStart;
        }

        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            // Run a Process in another class on a different Thread
            ProcessClass processClass = new ProcessClass();
            Task processTask = new Task(() =>
            {
                processClass.DoSomething();
            });
            processTask.Start();
            
        }
    }
}

进程类

class ProcessClass:MainWindow
    {
        
        public static void UpdateUI()
        {
            App.Current.Dispatcher.Invoke(delegate
            {
                windowButton.Content = "CHANGED CONTENT";
            });
        }
        
        public void DoSomething()
        {
            UpdateUI();
            int counter = 0;
            for(int i = 1; i < 100; i++)
            {
                counter += i;
                Thread.Sleep(100);
            }

            MessageBox.Show($"Task Completed, answer is {counter}");
        }
    }

【问题讨论】:

    标签: c# wpf multithreading user-interface dispatcher


    【解决方案1】:

    假设ProcessClass是你自己可以更新的代码,把DoDomething()的签名改成

    public async Task DoSomething(IProgress<string> progress)
    {
        progress.Report("Begin DoSomething()");
    
        var counter = 0;
        for(var i = 1; i < 100; i++)
        {
            counter += i;
            await Task.Delay(100).ConfigureAwait(false);
    
            progress.Report($"DoSomething() - i = {i}");
        }
    
        progress.Report($"DoSomething() Completed, answer is {counter}");
    }
    

    现在可以编写您的按钮单击处理程序

    private async void btnStart_Click(object sender, RoutedEventArgs e)
    {
         // usually you would update some other control such as a TextBlock
         // for the feedback, rather than the button content
         var progress = new Progress<string>(s => btnStart.Content = s);
         ProcessClass processClass = new ProcessClass();
         await processClass.DoSomething(progress).ConfigureAwait(false);
    }
    

    【讨论】:

    • 这太完美了,非常感谢我这几天一直在努力解决这个问题:) 我现在只需要理解它。再次感谢。
    • @C-Sharpie 有关 async / await 的更多信息,请查看 Stephen Cleary 的博客 blog.stephencleary.com
    • 再次感谢。明天我会看看这个。我对上述解决方案有 1 个问题,尽管它可以解决问题,但它似乎在处理 DoSomething 时锁定了 UI。这是正确的还是我可以做些什么来解决它?
    • @C-Sharpie 我已经更新了代码 - 我忘记在每个 await 调用中添加 .ConfigureAwait(false)
    • 更改会导致运行时出错。它似乎挂了几秒钟然后它说: System.InvalidOperationException: '调用线程必须是 STA,因为许多 UI 组件都需要这个。'
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多