是否可以在事件处理程序启动期间和结束期间更改一次视图?

时间:2014-09-26 13:19:58

标签: c# wpf

我通过点击从数据库中获取数据。 我有一个事件处理程序,在触发时应在状态栏中显示“数据检索...”,并应在事件处理程序结束前再次更改为“就绪”。

但文本只更新一次,第二次就绪。一般情况如何?

private void Next_Click(object sender, RoutedEventArgs e){
this.footerText = "Waiting for dataRetreival";
someRandomTimeTakingMethod(); //Gets Data from DB.
this.footerText = "Ready";
}

即使代码执行第2行,视图仅在函数结束时更新,即只有第二个实际工作。

4 个答案:

答案 0 :(得分:2)

您应该将数据密集型工作放在后台线程上,以便UI可以正确更新。这提供了最佳的用户体验。

用一些代码详细说明FZysset的答案......

    private async void Next_Click(object sender, RoutedEventArgs e)
    {
        footerText.Text = "Waiting for dataRetreival";
        IsEnabled = false;
        await SomeRandomTimeTakingMethodAsync();
        IsEnabled = true;
        footerText.Text = "Ready";
    }

    private async Task SomeRandomTimeTakingMethodAsync()
    {
        await Task.Delay(TimeSpan.FromSeconds(new Random().Next(2, 5)));
        // or await Task.Run(() => { ... });
    }

上面的示例允许您利用.NET 4.5中引入的await / async。注意它流动得有多好?没有废话!

我们将内容放到后台线程中,以便UI可以保持解除阻塞状态(因此它会显示您对状态栏的更新并允许用户交互。)当然,您必须小心不要在UI上更新任何内容来自你的后台主题。

如果您使用的是旧版本的.NET,则可以在没有异步/等待的情况下使用TPL:

    private void Next_Click(object sender, RoutedEventArgs e)
    {
        footerText.Text = "Waiting for dataRetreival";
        IsEnabled = false;

        Task.Factory.StartNew(() =>
        {
            SomeRandomTimeTakingMethod();
        }).ContinueWith(t =>
        {
            IsEnabled = true;
            footerText.Text = "Ready";
        }, TaskScheduler.FromCurrentSynchronizationContext());
    }

    private void SomeRandomTimeTakingMethod()
    {
        Thread.Sleep(TimeSpan.FromSeconds(new Random().Next(2, 5)));
    }

关于后一个例子需要注意两个重要事项:

  1. 您必须向TaskScheduler.FromCurrentSynchronizationContext()调用提供ContinueWith,否则您将遇到异常,因为延续不在UI线程上。您必须在未在后台线程上运行的方法中获取上下文。
  2. 您需要检查TaskContinueWith对象的例外情况。
  3. 这个例子非常简陋。如果您要通过点击处理程序启动一系列后台操作,那么您需要给自己一些帮助类/服务以使生活更轻松。 (并调查MVVM,如果你正在使用,我无法判断。)

    我的一位同事介绍了如何在C#和.NET中使用各种异步模式。您可以在此处查看:https://github.com/mtusk/TplLunchAndLearn

答案 1 :(得分:1)

那是因为你在UI线程上启动了“someRandomTimeTakingMethod”。因此,在完成之前,它不会更新视图。

要解决这个问题,您有以下几种可能:

对于某些示例,我强烈建议您使用第一个选项:http://msdn.microsoft.com/en-us/library/hh873191.aspx

答案 2 :(得分:0)

您需要异步运行这些行。您可以使用Task class

执行此操作
private void Next_Click(object sender, RoutedEventArgs e){
    Task.Factory.StartNew(() => footerText = "Waiting for dataRetreival");
    someRandomTimeTakingMethod(); //Gets Data from DB.
    Task.Factory.StartNew(() => footerText = "Ready");
}

答案 3 :(得分:0)

使用Dispatcher有一种方法可以做到这一点。原帖是here

代码是: -

private void Next_Click(object sender, RoutedEventArgs e){
  UpdateUI("Please wait for data retrieval", delegate() { someRandomTimeTakingMethod(); });
  this.footerText = "Ready";
}

public delegate void NoArgsDelegate();

public void UpdateUI(string description, NoArgsDelegate operation)
    {
        this.FooterText= description;

        DispatcherFrame frame = new DispatcherFrame();
        DispatcherOperation dispatcherOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, operation);

        dispatcherOperation.Completed += delegate(object sender, EventArgs e)
        {                                                                                   
            frame.Continue = false; 
        };
        Dispatcher.PushFrame(frame);

    }

如果我的理解是对的,那就使用异步编程,而不是不同的线程。该线程将首先更新UI,然后调用someRandomTimeTakingMethod()。