【问题标题】:Is it possible to change views, once during the start of an event handler and once during end?是否可以在事件处理程序启动期间和结束期间更改视图?
【发布时间】:2014-09-29 16:33:11
【问题描述】:

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

但文本只更新一次,第二次 Ready 更新。一般是怎么做的?

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

即使代码执行了第 2 行,视图也只会在函数结束时更新,即只有第二个真正起作用。

【问题讨论】:

  • C# 还是 VB?如果您有一些代码要显示,请包含它,因为这有助于我们为您提供更好的答案,并使该问题在未来对其他人更有用。

标签: c# wpf


【解决方案1】:

您应该将数据密集型工作放在后台线程上,以便 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 而不使用 async/await:

    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. 您需要检查ContinueWithTask 对象的异常情况。

虽然这个例子非常简陋。如果您要使用点击处理程序启动一堆后台操作,您会希望给自己一些帮助类/服务以使生活更轻松。 (并调查 MVVM,我不知道你是否正在使用它。)

我的一位同事做了一个关于在 C# 和 .NET 中使用各种异步模式的演示。你可以在这里查看:https://github.com/mtusk/TplLunchAndLearn

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

      您需要异步运行这些行。您可以使用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");
      }
      

      【讨论】:

      • 代码抛出错误,调用线程无法访问此对象,因为不同的线程拥有它。我尝试了以下代码Dispatcher.Invoke(new Action(() => { Task.Factory.StartNew(() => footerText = "Please wait for data retreival"); })); 再次抛出相同的错误... Dispacther 应该从任务工厂调用?
      • 这个解决方案非常无效。任何做过异步编程的人都应该立即明白这一点。
      【解决方案4】:

      有一种方法可以使用 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()。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-08
        • 1970-01-01
        • 1970-01-01
        • 2010-11-08
        • 1970-01-01
        • 2021-06-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多