本文实现 MVVM 结构下,根据 ViewModel 层不同的状态,显示一个等待 UI,当数据处理完毕或服务接口返回后等待 UI 消失。
需求描述
- 在 ViewModel 中处理 Model 中的数据需要一定时间的等待;
- ViewModel 或 Model 在获取数据或访问同步服务时有一定延迟需要等待;
- ViewModel 操作 View 加载数据需要一段时间;
解决办法
- 显示一个等待 UI,当数据处理完毕或服务接口返回后等待UI消失
转动齿轮控件
等待控件
1 <Grid>
2 <local:SprocketControl Grid.Row="0"
3 Grid.Column="0"
4 Width="100"
5 Height="100"
6 Margin="0,0,0,0"
7 HorizontalAlignment="Center"
8 VerticalAlignment="Center"
9 Background="Transparent"
10 Interval="60"
11 IsIndeterminate="True"
12 StartAngle="-90"
13 TickColor="{DynamicResource MaskForegroundColor}"
14 TickCount="16"
15 TickWidth="5" />
16 </Grid>
等待效果
定义 MVVM 中的 ViewModel 的状态
/// <summary>
/// 在MVVM模式中ViewModel的状态
/// </summary>
[Flags]
public enum ViewModelStatus
{
/// <summary>
/// ViewModel无状态
/// </summary>
None = 0x0,
/// <summary>
/// ViewModel正在初始化
/// </summary>
Initializing = 0x1,
/// <summary>
/// ViewModel初始化完毕
/// </summary>
Initialized = 0x2,
/// <summary>
/// ViewModel正在加载
/// </summary>
Loading = 0x4,
/// <summary>
/// ViewModel加载完毕
/// </summary>
Loaded = 0x8,
/// <summary>
/// ViewModel正在保存
/// </summary>
Saving = 0x16,
/// <summary>
/// ViewModel保存完毕
/// </summary>
Saved = 0x32
}
ViewModel 状态转变为控件状态
1 public class StatusToAnimationVisibilityConverter : IValueConverter
2 {
3 #region IValueConverter Members
4
5 public object Convert(
6 object value, Type targetType, object parameter, CultureInfo culture)
7 {
8 try
9 {
10 string status = value.ToString();
11
12 switch (status)
13 {
14 case "Initializing":
15 case "Loading":
16 case "Saving":
17 return Visibility.Visible;
18 case "Loaded":
19 case "Saved":
20 default:
21 return Visibility.Collapsed;
22 }
23 }
24 catch (Exception)
25 {
26 return Visibility.Collapsed;
27 }
28 }
29
30 public object ConvertBack(
31 object value, Type targetType, object parameter, CultureInfo culture)
32 {
33 return DependencyProperty.UnsetValue;
34 }
35
36 #endregion
37 }
使 UserControl 支持异步显示
1 <coverters:StatusToAnimationVisibilityConverter x:Key="StatusToAnimationVisibilityConverter" />
2
3 <Style x:Key="AsyncWorkUserControlStyle" TargetType="{x:Type UserControl}">
4 <Setter Property="Template">
5 <Setter.Value>
6 <ControlTemplate TargetType="{x:Type UserControl}">
7 <Grid>
8 <ContentPresenter Panel.ZIndex="0" />
9 <Grid x:Name="animationGrid"
10 Width="Auto"
11 Height="Auto"
12 HorizontalAlignment="Stretch"
13 VerticalAlignment="Stretch"
14 Panel.ZIndex="2000"
15 Visibility="{Binding Path=Status,
16 Converter={StaticResource StatusToAnimationVisibilityConverter}}">
17 <Grid Width="Auto"
18 Height="Auto"
19 HorizontalAlignment="Stretch"
20 VerticalAlignment="Stretch"
21 Panel.ZIndex="0"
22 Background="{DynamicResource MaskGridBackgroundBrush}"
23 Opacity="0.2" />
24 <ctrl:WaitingControl x:Name="animation" Panel.ZIndex="1" />
25 </Grid>
26 </Grid>
27 </ControlTemplate>
28 </Setter.Value>
29 </Setter>
30 </Style>
应用 Style 至 UserControl
1 <UserControl x:Class="DeviceConfiguration.Views.CameraManagementView"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6 d:DesignHeight="318"
7 d:DesignWidth="632"
8 Style="{DynamicResource AsyncWorkUserControlStyle}"
9 mc:Ignorable="d">
10 </UserControl>
定义基础 ViewModel
1 /// <summary>
2 /// 响应式的ViewModel模型
3 /// </summary>
4 public abstract class ViewModelResponsive : ViewModelBase, IViewModelResponsive
5 {
6 #region Fields
7
8 private ViewModelStatus _status = ViewModelStatus.None;
9
10 #endregion
11
12 #region ViewModel Status
13
14 /// <summary>
15 /// 刷新UI数据
16 /// </summary>
17 public virtual void Refresh()
18 {
19
20 }
21
22 /// <summary>
23 /// ViewModel状态
24 /// </summary>
25 public ViewModelStatus Status
26 {
27 get
28 {
29 return _status;
30 }
31 protected set
32 {
33 if (_status != value)
34 {
35 _status = value;
36 RaisePropertyChanged(@"Status");
37 }
38 }
39 }
40
41 #endregion
42 }
ViewModel 应用
1 public class CameraManagementViewModel : ViewModelResponsive
2 {
3 protected override void BindCommands()
4 {
5 RefreshCommand = new RelayCommand(() =>
6 {
7 Refresh();
8 });
9 }
10
11 public override void Refresh()
12 {
13 base.Refresh();
14
15 Status = ViewModelStatus.Initializing;
16 CameraCollection.Clear();
17 Model.GetCameras(GetCamerasCallback);
18 }
19
20 private void GetCamerasCallback(object sender, AsyncWorkerCallbackEventArgs<IList<Camera>> args)
21 {
22 CameraCollection.Clear();
23 Status = ViewModelStatus.Loaded;
24
25 if (result)
26 {
27 foreach (var item in (args.Data as IList<Camera>))
28 {
29 CameraCollection.Add(item);
30 }
31 }
32 }
33 }
实现效果