WPF元素事件处理程序中的UI更新

9

在WPF中存在UI更新问题。

我有这样的代码:

    private void ButtonClick_EventHandler(object sender, RoutedEventArgs e)
    {
        Label.Visibility = Visibility.Visible;
        TextBox.Text = "Processing...";

        LongTimeMethod(); //some long operation
    }

问题在于直到 LongTimeMethod 结束(即事件处理程序结束)之前,Label.Visibility 和 TextBox.Text 不会被更改。
目前我解决这个问题的方法是:
    private void ButtonClick_EventHandler(object sender, RoutedEventArgs e)
    {
        Label.Visibility = Visibility.Visible;
        TextBox.Text = "Processing...";

        Dispatcher.BeginInvoke(new Action(LongTimeMethod), 
            DispatcherPriority.Background);
    }

有没有其他解决方案不需要使用调度程序调用?调用 this.UpdateLayout() 也无法解决问题。

2个回答

6

使用Dispatcher.BeginInvoke仍然会在UI线程上运行LongTimeMethod()。如果不需要在UI线程上运行(例如进行一些后台处理),建议使用TPL在后台线程上运行:

private void ButtonClick_EventHandler(object sender, RoutedEventArgs e)
{
    Label.Visibility = Visibility.Visible;
    TextBox.Text = "Processing...";

    Task.Factory.StartNew(() => LongTimeMethod())
        .ContinueWith(t =>
        {
            Dispatcher.BeginInvoke((Action)delegate()
            {
                TextBox.Text = "Done!";
            });
        });

}

通过这种方法,长时间运行的方法会在后台线程上处理(因此UI线程将保持空闲以保持渲染,应用程序不会冻结),当后台任务完成时,您可以在UI Dispatcher 上执行任何更改UI的操作(例如更新文本框文本)。


在这种情况下,该方法在UI线程中运行是可以的。我只是想避免使用调度程序、线程等。 - brain_pusher
2
如果UI线程正在执行长时间运行的操作,则不会可用于更新您设置的UI属性。通过使用 Dispatcher.BeginInvoke,您在说“在UI线程上执行此操作,但现在不要执行它;等你完成当前任务时再执行它”。这就是为什么您可以看到更新的UI的原因。 - Steve Greatrex

1

Visibility和Text是依赖属性,由调度程序更新。您的解决方案绝对正确,但我的建议是异步执行。

另一方面,您可以在WPF中模拟Application.DoEvents(请参阅文章)。


有一个原因,为什么Application.DoEvents没有被引入WPF!如果你想释放UI线程,你应该释放UI线程,而不是通过处理任何未决消息来伪造它。 - Steve Greatrex
我们坚持并行性,我刚刚提出了一种替代方案。 - Optillect Team

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