如何在WPF应用程序忙于数据绑定时显示等待标记

时间:2011-09-08 10:36:11

标签: wpf mvvm

我有一个使用MVVM模式的WPF应用程序,当它忙于做一些用户必须等待的事情时,有时必须显示一个等待标记。感谢本页面上的答案组合:display Hourglass when application is busy,我有一个几乎可行的解决方案(虽然它在精神上并不是真正的MVVM)。 每当我在视图模型中做一些耗时的事情时,我就会这样做:

using (UiServices.ShowWaitCursor())
{
.. do time-consuming logic
this.SomeData = somedata;
}

(ShowWaitCursor()返回一个IDisposable,显示waitcursor直到它被处理掉) 我的例子中的最后一行是我设置一些属性的地方。此属性绑定在我的XAML中,例如像这样:

<ItemsControl ItemsSource="{Binding SomeData}" /> 

但是,由于这可能是一个很长的对象列表,有时候会有复杂的数据模板等,实际的绑定和渲染有时需要相当长的时间。由于此绑定发生在using语句之外,因此waitcursor将在用户实际等待结束之前消失。

所以我的问题是如何在WPF MVVM应用程序中进行等待,将数据绑定考虑在内?

4 个答案:

答案 0 :(得分:84)

Isak的回答对我不起作用,因为它没有解决当用户实际等待结束时如何采取行动的问题。 我最终做到了这一点:每当我开始做一些耗费时间的事情时,我都会调用辅助方法。此帮助器方法更改游标,然后创建将在应用程序空闲时调用的DispatcherTimer。调用它时,它会将鼠标光标设置为:

/// <summary>
///   Contains helper methods for UI, so far just one for showing a waitcursor
/// </summary>
public static class UiServices
{

     /// <summary>
     ///   A value indicating whether the UI is currently busy
     /// </summary>
     private static bool IsBusy;

     /// <summary>
     /// Sets the busystate as busy.
     /// </summary>
     public static void SetBusyState()
     {
          SetBusyState(true);
     }

     /// <summary>
     /// Sets the busystate to busy or not busy.
     /// </summary>
     /// <param name="busy">if set to <c>true</c> the application is now busy.</param>
     private static void SetBusyState(bool busy)
     {
          if (busy != IsBusy)
          {
               IsBusy = busy;
               Mouse.OverrideCursor = busy ? Cursors.Wait : null;

               if (IsBusy)
               {
                   new DispatcherTimer(TimeSpan.FromSeconds(0), DispatcherPriority.ApplicationIdle, dispatcherTimer_Tick, Application.Current.Dispatcher);
               }
          }
     }

     /// <summary>
     /// Handles the Tick event of the dispatcherTimer control.
     /// </summary>
     /// <param name="sender">The source of the event.</param>
     /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
     private static void dispatcherTimer_Tick(object sender, EventArgs e)
     {
          var dispatcherTimer = sender as DispatcherTimer;
          if (dispatcherTimer != null)
          {
              SetBusyState(false);
              dispatcherTimer.Stop();
          }
     }
}

答案 1 :(得分:9)

所以我不喜欢使用OverrideCursor,因为我有多个窗口,我希望那些当前没有执行某些操作的窗口有正常的箭头光标。

这是我的解决方案:

<Window.Style>
    <Style TargetType="Window">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsBusy}" Value="True">
                <Setter Property="Cursor" Value="Wait" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Style>
<Grid>
    <Grid.Style>
        <Style TargetType="Grid">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsBusy}" Value="True">
                    <Setter Property="IsHitTestVisible" Value="False" /> <!-- Ensures wait cursor is active everywhere in the window -->
                    <Setter Property="IsEnabled" Value="False" /> <!-- Makes everything appear disabled -->
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Style>
    <!-- Window controls go here -->
</Grid>

答案 2 :(得分:7)

我过去所做的是在viewmodel中定义布尔属性,指示正在进行冗长的计算。例如IsBusy在工作时设置为true,在空闲时设置为false。

然后在视图中我绑定到此并在此属性为true时显示进度条或微调器或类似内容。我个人从未使用这种方法设置光标,但我不明白为什么它不可能。

如果您想要更多控制并且仅使用简单的布尔值,则可以使用从视图模型中驱动的VisualStateManager。使用此方法,您可以根据视图模型的状态详细指定UI的外观。

答案 3 :(得分:1)

除了Isak Savo的贡献之外,您可能还需要查看Brian Keating's blog的工作样本。