我有一个使用MVVM模式的WPF应用程序,当它忙于做一些用户必须等待的事情时,有时必须显示一个等待标记.感谢本页面上的答案组合:在应用程序繁忙时显示沙漏,我有一个几乎可以工作的解决方案(尽管它在精神上并不是真正的MVVM).每当我在视图模型中做一些耗时的事情时,我会这样做:
using (UiServices.ShowWaitCursor())
{
.. do time-consuming logic
this.SomeData = somedata;
}
Run Code Online (Sandbox Code Playgroud)
(ShowWaitCursor()返回一个显示waitcursor的IDisposable,直到它被处理掉.)我的例子中的最后一行是我设置一些属性的地方.此属性绑定在我的XAML中,例如:
<ItemsControl ItemsSource="{Binding SomeData}" />
Run Code Online (Sandbox Code Playgroud)
但是,由于这可能是一个很长的对象列表,有时候会有复杂的数据模板等,实际的绑定和渲染有时需要相当长的时间.由于此绑定发生在using语句之外,因此waitcursor将在用户实际等待结束之前消失.
所以我的问题是如何在WPF MVVM应用程序中进行等待,将数据绑定考虑在内?
T.J*_*aer 85
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以我不喜欢使用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>
Run Code Online (Sandbox Code Playgroud)
我过去所做的是在viewmodel中定义布尔属性,指示正在进行冗长的计算.例如IsBusy,在工作时设置为true,在空闲时设置为false.
然后在视图中我绑定到此并在此属性为true时显示进度条或微调器或类似内容.我个人从未使用这种方法设置光标,但我不明白为什么它不可能.
如果你想要更多控制并且一个简单的布尔值是不够的,你可以使用从viewmodel驱动的VisualStateManager.使用此方法,您可以根据视图模型的状态详细指定UI的外观.