在进行 WPF 开发过程中,当我们需要将一些请求较为耗时的数据通过数据绑定的方法显示到 View 层时,这个时候为了提升用户体验,需要保证 View 层不能出现卡顿,并最好能显示一些数据请求信息。因此,我们可以通过创建一个继承 INotifyPropertyChanged 泛型类来实现该效果。
首先,我们创建一个能够在 Task 运行的不同状态下进行属性通知的类,示例代码如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 
 | public class BindableTask<T> : INotifyPropertyChanged{
 private readonly Task<T> _task;
 public BindableTask(Task<T> task)
 {
 _task = task;
 var _ = WatchTaskAsync();
 }
 
 private async Task WatchTaskAsync()
 {
 try
 {
 await _task;
 }
 catch (Exception)
 {
 
 throw;
 }
 OnPropertyChanged(nameof(IsNotCompleted));
 OnPropertyChanged(nameof(IsSuccessfullyCompleted));
 OnPropertyChanged(nameof(IsFaulted));
 OnPropertyChanged(nameof(Result));
 }
 public bool IsNotCompleted => !_task.IsCompleted;
 public bool IsSuccessfullyCompleted => _task.Status == TaskStatus.RanToCompletion;
 public bool IsFaulted => _task.IsFaulted;
 public T Result => IsSuccessfullyCompleted ? _task.Result : default(T);
 
 public event PropertyChangedEventHandler PropertyChanged;
 public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 }
 
 | 
该类支持在任务 未完成 完成 错误 时进行属性通知
创建对应的 ViewModel,示例代码如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | public class MainViewModel{
 public BindableTask<string> MyValue { get;private set; }
 
 public MainViewModel()
 {
 MyValue = new BindableTask<string>(CalculateMyValueAsync());
 }
 
 private async Task<string> CalculateMyValueAsync()
 {
 
 await Task.Delay(TimeSpan.FromSeconds(5));
 return "hippieZhou";
 }
 }
 
 | 
创建相应的值转换器,用于将执行状态直观的显示到界面上,示例代码如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | public class BooleanToVisibilityConverter : IValueConverter{
 public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
 {
 if (bool.TryParse(value.ToString(), out bool b))
 return b ? Visibility.Visible : Visibility.Collapsed;
 else
 return Visibility.Collapsed;
 }
 
 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
 {
 throw new NotImplementedException();
 }
 }
 
 | 
创建 View 层,进行数据展示,示例代码如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | <Window.Resources><ResourceDictionary>
 <local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
 </ResourceDictionary>
 </Window.Resources>
 <Window.DataContext>
 <local:MainViewModel />
 </Window.DataContext>
 <Grid>
 <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
 <Label Content="Loading" Visibility="{Binding MyValue.IsNotCompleted, Converter={StaticResource BooleanToVisibilityConverter}}" />
 <Label Content="{Binding MyValue.Result}" />
 <Label
 Content="An Error Occurred"
 Foreground="Red"
 Visibility="{Binding MyValue.IsFaulted, Converter={StaticResource BooleanToVisibilityConverter}}" />
 </Grid>
 </Grid>
 
 | 
执行上述操作后,运行该程序,会发现程序在运行 5 秒之后显示由显示 loading 变为 hippieZhou。
参考:
- 《C#并发编程经典实例》