【问题标题】:How to show a waitcursor when the WPF application is busy databinding当 WPF 应用程序忙于数据绑定时如何显示等待光标
【发布时间】:2011-11-12 21:13:09
【问题描述】:

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

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

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

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

但是,由于这可能是一长串对象,有时还包含复杂的数据模板等。实际的绑定和渲染有时需要相当长的时间。由于此绑定发生在我的 using 语句之外,因此等待光标将在用户实际等待结束之前消失。

所以我的问题是如何在考虑数据绑定的 WPF MVVM 应用程序中执行等待光标?

【问题讨论】:

    标签: wpf mvvm


    【解决方案1】:

    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();
              }
         }
    }
    

    【讨论】:

    • 伟大的实施!当第三方 UI 绑定操作中发生时间消耗时,这对我来说非常适合;当我真的不知道什么时候完成时。谢谢!
    • 杰夫 - 这里没有什么可以放弃的。当应用程序空闲时,光标会重置。如果十行代码表明应该在应用程序空闲之前显示等待光标,没关系;它们都将在重置时完成(显然此解决方案假定同步 UI 线程操作)。
    • @Steve:当然!我什至没有看到调度程序的优先级。说得通。好的。我在我的代码中使用了它,但我将类更改为 WaitCursorSetBusyStateShow,这样我的代码就更加自我记录:WaitCursor.Show()
    • 你不能真正将它直接用于 MVVM,因为 UiServices 不是静态类,所以它不能实现接口。这在尝试对 ViewModel 进行单元测试时会导致问题。但是,我设法通过重命名类 UiServicesStatic 并将其嵌套在一个名为 UiServices 的类中来解决它,该类实现了 IUiServices 类。这个接口有一个方法——void SetBusyState();
    • 新的 DispatcherTimer 实例未分配给字段或变量。它是如何收集垃圾的?这是潜在的内存泄漏吗?
    【解决方案2】:

    所以我不喜欢使用 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>
    

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

        除了 Isak Savo 的贡献之外,您可能还想查看 Brian Keating's blog 以获取工作示例。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-10-15
          • 2012-06-16
          • 2011-01-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-18
          相关资源
          最近更新 更多