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

问题描述 投票:0回答:4

我有一个使用 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 语句之外,因此等待光标将在用户的实际等待结束之前消失。

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

wpf mvvm
4个回答
89
投票

Isak的答案对我来说不起作用,因为它没有解决用户实际等待结束时如何操作的问题。 我最终这样做了:每次我开始做一些耗时的事情时,我都会调用一个辅助方法。此辅助方法会更改光标,然后创建一个 DispatcherTimer,当应用程序空闲时将调用该 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();
          }
     }
}

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>

7
投票

我过去所做的是在视图模型中定义布尔属性,指示正在进行的长时间计算。例如

IsBusy
工作时设置为 true,空闲时设置为 false。

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

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


1
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.