【问题标题】:Make wpf UI responsive when button click is performed执行按钮单击时使 wpf UI 响应
【发布时间】:2013-03-14 12:37:08
【问题描述】:

在我的 wpf (c#) 应用程序中,当用户按下按钮时会执行一个漫长的过程。当按下按钮直到执行完完整代码时,窗口冻结,用户无法在窗口中执行任何其他任务。如何使按钮单击代码作为后台进程,以便窗口响应用户。我尝试了以下方法,但没有成功。

private void btn_convert_Click(object sender, RoutedEventArgs e)
{
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, 
    new Action(() => { 

         WorkerMethod();
    }));
}

其中 WorkerMethod 是包含整个代码的函数。任何建议。

问候,
桑吉萨

【问题讨论】:

    标签: c# wpf multithreading background-process


    【解决方案1】:

    如果您的目标是 .NET 4.5,您可以使用 async/await 让您的 UI 响应:

    private async void btn_convert_Click(object sender, RoutedEventArgs e)
    {
        await WorkerMethod();
    }
    

    修改你的WorkerMethod:

    private Task WorkerMethod()
    {
        return Task.Run(() =>
            {
               // your long running task here
            });
    }
    

    【讨论】:

    • 你能解释一下这段代码吗? async 和 await 关键字有什么作用?
    【解决方案2】:

    您在这里实际上所做的是要求 UI 线程通过 Dispatcher 运行您的繁重任务。

    您应该创建一个新线程/任务并让它运行您的后台方法:

    Thread t = new Thread(() => WorkerMethod());
    t.Start();
    

    更多信息请阅读:

    WPF BackgroundWorker vs. Dispatcher

    Understanding “Dispatcher” in WPF

    【讨论】:

    • 这个解决方案工作正常,但有一个异常。在工作方法中,我有按钮启用禁用代码,它给出了异常,调用线程无法访问这个对象,因为另一个线程拥有它。
    • @Sangeetha 使用 Dispatcher 仅启用/禁用它们
    【解决方案3】:

    这里答案的替代方案是 BackgroundWorker。 它还有一个额外的好处,就是在完成后将您返回到 GUI 线程。

    private void btn_convert_Click(object sender, RoutedEventArgs e)
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += WorkerMethod;
        worker.RunWorkerCompleted += WorkerEnded;
        worker.RunWorkerAsync();
    }
    
    private void WorkerMethod()
    {
        // New thread, cannot access GUI
    }
    
    private void WorkerEnded()
    {
        // Back on the GUI Thread
        // Worker is finished
    }
    

    【讨论】:

    • 我是线程新手,觉得这个简单易懂
    【解决方案4】:

    您正在调用UI-Dispatcher 上的方法,这将使UI 冻结。使用

    在您的点击事件中创建一个新线程
    private void btn_convert_Click(object sender, RoutedEventArgs e)
        {
             Thread t = new Thread(new ThreadStart(WorkerMethod));
             //assign STA or MTA, etc....
             t.Start();
        }
    

    查看here 以获取有关“System.Threading.Thread`”的信息。

    【讨论】:

    • 在工作方法中我有按钮启用禁用代码,这给出了异常,调用线程无法访问该对象,因为不同的线程拥有它。
    • @Sangeetha 您必须在 UI-Dispatcher 的队列中排队您的请求。查看here 并如图所示继续。
    【解决方案5】:

    我设想了另一种我引以为豪的方式: 如果多线程仅允许 IHM 为进度条设置动画,如果用户不应该与进程交互的 ihm 或者只是没有时间开发多线程并且您不能让您的应用程序在没有反馈:

    您可以开发一个加载器,该加载器启动到另一个 STA 线程,该线程创建另一个窗口并在冻结窗口的中间显示此加载器。

    这是我撰写的一篇文章以及此类解决方案的实现:

    http://charly-studio.com/blog/a-loader-for-wpf-long-ui-process/

    这是我使用组件的方式:

    private void MainWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        // Long UI process context
        using (new LongUIProcessContext("Wait while loading..."))
        {
            Thread.Sleep(2000);
    
            // Sub long ui process context
            using (new LongUIProcessContext("Wait while loading 2..."))
            {
                Thread.Sleep(2000);
            } 
    
            // Another sub long ui process context
            using (new LongUIProcessContext("Wait while loading 3..."))
            {
                Thread.Sleep(2000);
            }
        }
    }
    

    这是我建议的替补席的样子:

    这是我制作的工作台的直接链接,您可以在其中找到组件:

    The bench solution direct download

    【讨论】:

      【解决方案6】:

      尝试使用 Backgroundworker 类。您必须注册到 DoWork 事件并创建一个事件处理程序,将所有处理代码放入其中。您使用 RunWorkerAsync 方法启动工作程序。还有一种方法可以报告您的工人进度“ReportProgress”。 RunWorkerCompleted 将在您的工作人员处理完您的代码时向您显示。在BackgroundWorker之后尝试在互联网上搜索,您会发现很多如何使用它的示例

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-14
        相关资源
        最近更新 更多