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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7346663/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 23:04:34  来源:igfitidea点击:

How to show a waitcursor when the WPF application is busy databinding

wpfmvvm

提问by T.J.Kjaer

I have a WPF application using the MVVM pattern that sometimes have to show a waitcursor when it is busy doing something the user has to wait for. Thanks to a combination of answers on this page: display Hourglass when application is busy, I have a solution that almost works (although it is not really MVVM in spirit). Whenever I do something time-consuming in my viewmodels I do this:

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

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

(ShowWaitCursor() returns a IDisposable that shows the waitcursor until it is being disposed of) The last line in my example is where I set some property. This property is bound in my XAML, e.g. like this:

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

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

However, since this could be a long list of objects and sometimes with complex datatemplates, etc. the actual binding and rendering sometime takes a considerable amount of time. Since this binding takes places outside of my using statement the waitcursor will go away before the actual wait is over for the user.

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

So my question is how to do a waitcursor in a WPF MVVM application that takes databinding into account?

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

回答by T.J.Kjaer

Isak's answer did not work for me, because it did not solve the problem of how to act when the actual wait is over for the user. I ended up doing this: Everytime I start doing something timeconsuming, I call a helper-method. This helper method changes the cursor and then creates a DispatcherTimer that will be called when the application is idle. When it is called it sets the mousecursor back:

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

回答by Jas Laferriere

So I didn't like using OverrideCursor because I had multiple windows and I wanted the ones that were not currently executing something to have the normal arrow cursor.

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

Here is my solution:

这是我的解决方案:

<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>

回答by Isak Savo

What I've done in the past is to define boolean properties in the viewmodel that indicates that a lengthy calculation is in progress. For instance IsBusywhich is set to true when working and false when idle.

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

Then in the view I bind to this and display a progress bar or spinner or similar while this property is true. I've personally never set the cursor using this approach but I don't see why it wouldn't be possible.

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

If you want even more control and a simple boolean isn't enough, you can use the VisualStateManagerwhich you drive from your viewmodel. With this approach you can in detail specify how the UI should look depending on the state of the viewmodel.

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

回答by Izmoto

In addition to Isak Savo's contribution, you might want to have a look at Brian Keating's blogfor a working sample.

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