• WPF MVVM UI分离之《交互与数据分离》


    在我们使用WPF过程中,不可避免并且超级喜欢使用MVVM框架。

    那么,使用MVVM的出发点是视觉与业务逻辑分离,即UI与数据分离

    诸如下面的问题:

    删除操作,假如需要先执行一部分数据的处理,然后删除界面列表中的子项,之后再执行其它数据的处理。请问此业务该放置于Xaml.cs文件,还是ViewModel中呢?

    再如弹窗,提示框,设置列表的滚动等等。

    此上一些操作,我们不应该把业务代码直接挪到cs文件中,因为删除操作绝大部分的代码都是数据的处理。所以,数据的部分放置在ViewModel中,一些交互放在cs文件中,就是很合理及有必要了。

    单元测试,UI与交互的那部分mock模拟有点难度,也没必要去模拟。那么,我们是应该把数据与交互拆开,减少之间的耦合性。这样添加单元测试则更容易。

    交互与数据分离 - 描述

    首先MVVM,通过View与ViewModel的绑定,我们实现了UI与业务逻辑的分离。通俗一点,我们熟悉的Xaml与ViewModel文件中,代码之间的隔离。在此不详述~

    而MVVM,不只是界面与逻辑,其实逻辑还可以拆分成交互与数据

    即:Xaml 》Xaml.cs 》ViewModel

    是的,按照上面的结构图,我们分成三部分:

    • 界面 用于界面呈现 ---- 如页面/控件/样式/模板等其它资源的初始化,动画的触发等。
    • 交互 用于与用户确认的交互或者界面复杂逻辑的处理 ---- 如弹窗/提示框/复杂动画的处理/设置列表的滚动等其它界面元素的视觉处理。
    • 数据 只是数据的处理 ---- 增删改查导入导出保存等只针对数据的操作,界面状态属性的保存与触发更改(绑定)。

    交互与数据分离是怎样的?比如删除:

    1. 界面删除按钮,绑定ViewModel中的DeleteCommand,当我们点击删除时,触发DeleteCommand.Execute

    2. ViewModel中,先执行数据状态的判断,然后执行交互通知ShowDeleteWaringAction,调用xaml.cs文件中的确认提示框

    3. 在Xaml.cs中添加依赖属性ShowDeleteWaring,绑定ViewModel中的ShowDeleteWaringAction.Progress。在属性更改中,处理提示框确认逻辑。

    4. ViewModel中,等待ShowDeleteWaring弹框完成后,继续执行下面的业务。

    5. 还有类似上面步骤的删除动画。。。

    交互与数据分离 - 实现

    使用场景:在WPF框架下开发时,一种基于MVVM的UI分离方案

    解决方案:在业务逻辑处理过程中,新建一个交互处理线程,通知界面完成交互处理,同时后台逻辑保持同步等待。界面完成交互处理后,回调并执行后续的业务逻辑。

    实现方案:

    • View中的依赖属性DependencyProperty,绑定ViewModel中属性“UIDelegateOperation”中的交互处理进度“UIDelegateProress”
    • 每次在ViewModel执行业务逻辑需要调用交互处理时,由UIDelegateOperation创建一个新的交互进度“UIDelegateProress”,触发属性变更,并设置“UIDelegateOperation”同步等待。
    • 当View中的属性变更事件执行完成后,回调并唤醒”UIDelegateOperation“,继续完成后面的业务逻辑。

    1. 界面

    在Xaml中添加附加属性,删除动画DeleteCoursewaresAnimation,删除确认框ShowDeleteWaring。并绑定ViewModel中对应的属性

    1 <UserControl.Style>
    2  <Style TargetType="editing:CloudListView">
    3      <Setter Property="DeleteCoursewaresAnimation" Value="{Binding DeleteCoursewaresAnimation.DelegateProgress}" />
    4      <Setter Property="ShowDeleteWaringShow" Value="{Binding ShowDeleteWaring.DelegateProgress}" />
    5  </Style>
    6 </UserControl.Style>

    界面ListBox,列表子项ListBoxItemr的DataTemplate模板中,删除按钮绑定ViewModel中的DeleteCommand

    1 <Button x:Name="DeleteButton" 
    2         Command="{Binding ElementName=TheCloudDocsList,Path=DataContext.DeleteCommand}"
    3         CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent},Path=DataContext }"
    4         Content="删除" Style="{StaticResource Style.Button}" />

     2. ViewModel

    ViewModel调用UIDelegateOperation交互处理时,根据是否需要同步等待,调用不同的函数 Start(),StartAsync(),StartWithResult(),StartWithResultAsync();

    删除业务中,除了数据处理,还有俩个交互(删除确认框,删除元素动画)。

    通过在同步调用删除确认框/删除元素动画后,再继续往下执行业务。

    属性和字段字义:

    定义命令

    自定义命令,可以详细之前写的博客:自定义Command

     1 private DelegateCommand<CoursewareListItem> _deleteCommand = null;
     2 /// <summary>
     3 /// 删除
     4 /// </summary>
     5 public DelegateCommand<CoursewareListItem> DeleteCommand
     6 {
     7     get
     8     {
     9         if (_deleteCommand == null)
    10         {
    11             _deleteCommand = new DelegateCommand<CoursewareListItem>(DeleteCourseware_OnExecute);
    12 
    13         }
    14         return _deleteCommand;
    15     } 
    16 }

    提示框确认交互/删除动画交互

    1 /// <summary>
    2 /// 弹出删除确认窗口 
    3 /// </summary>
    4 public IUIDelegateOperation<List<CoursewareListItem>, MessageResult> ShowDeleteWaring { get; set; } = new IUIDelegateOperation<List<CoursewareListItem>, MessageResult>();
    5 
    6 /// <summary>
    7 /// 删除动画 
    8 /// </summary>
    9 public IUIDelegateOperation<List<CoursewareListItem>> DeleteCoursewaresAnimation { get; set; } = new IUIDelegateOperation<List<CoursewareListItem>>();

    删除逻辑:

     1 /// <summary>
     2 /// 删除
     3 /// </summary>
     4 /// <param name="item"></param>
     5 /// <returns></returns>
     6 private async void DeleteCourseware_OnExecute(CoursewareListItem item)
     7 {
     8     await DeleteCoursewares(new List<CoursewareListItem>() { item });
     9 }
    10 private async Task DeleteCoursewares(List<CoursewareListItem> items)
    11 {
    12     if (items.Count == 0)
    13     {
    14         return;
    15     }
    16 
    17     //弹出删除确认窗口
    18     var messageResult = await ShowDeleteWaringShow.ExecuteWithResultAsync(items);
    19     if (messageResult == MessageResult.Positive)
    20     {
    21         //删除服务器数据 
    22         Response deleteResponse = await WebService.DeleteItemAsync(items);
    23 
    24         //删除失败
    25         if (!deleteResponse.Success)
    26         {
    27             Notification.ShowInfo(deleteResponse.Message);
    28             return;
    29         }
    30         //删除动画
    31         await DeleteCoursewaresAnimation.ExecuteAsync(items);
    32         
    33         //界面删除子项
    34         items.ForEach(item => ItemsSource.Remove(item));
    35 
    36         //退出编辑模式
    37         if (DocListState == EditStatus.Editing)
    38         {
    39             DocListState = EditStatus.Normal;
    40         }
    41     }
    42 }

    3. Xaml.cs后台

    • 添加依赖属性后,通过属性变更触发,来完成弹出提示框/删除动画等交互。
    • 执行交互时,需要同步等待时,应将动画执行等转化为同步逻辑。

    添加依赖属性 - 删除窗口

    属性变更触发方法,应该是一个异步方法,里面的逻辑应该为同步执行。这样ViewModel中才能同步等待交互的完成,并执行之后的逻辑。

     1 /// <summary>
     2 /// 删除窗口
     3 /// </summary>
     4 public static readonly DependencyProperty ShowDeleteWaringShowProperty = DependencyProperty.Register(
     5     "ShowDeleteWaringShow", typeof(UIDelegateProgress<List<CoursewareListItem>, MessageResult>), typeof(CloudListView), new PropertyMetadata(default(UIDelegateProgress<List<CoursewareListItem>, MessageResult>),
     6         (d, e) => ((UIDelegateProgress<List<CoursewareListItem>, MessageResult>)e.NewValue)?.StartAsync(((CloudListView)d).ShowDeleteWaringShow)));
     7 
     8 private async Task<MessageResult> ShowDeleteWaringShow(List<CoursewareListItem> items)
     9 {
    10     var cmd = await DeleteWaringShow(items);
    11     return cmd.Result;
    12 }
    13 
    14 public static void SetShowDeleteWaringShow(DependencyObject element, UIDelegateProgress<List<CoursewareListItem>, MessageResult> value)
    15 {
    16     element.SetValue(ShowDeleteWaringShowProperty, value);
    17 }
    18 
    19 public static UIDelegateProgress<List<CoursewareListItem>, MessageResult> GetShowDeleteWaringShow(DependencyObject element)
    20 {
    21     return (UIDelegateProgress<List<CoursewareListItem>, MessageResult>)element.GetValue(ShowDeleteWaringShowProperty);
    22 }

    添加依赖属性 - 删除动画

     1 public static readonly DependencyProperty DeleteCoursewaresAnimationProperty = DependencyProperty.Register(
     2     "DeleteCoursewaresAnimation", typeof(UIDelegateProgress<List<CoursewareListItem>>), typeof(CloudListView), new PropertyMetadata(default(UIDelegateProgress<List<CoursewareListItem>>),
     3         (d, e) => ((UIDelegateProgress<List<CoursewareListItem>>)e.NewValue)?.StartAsync(((CloudListView)d).ExecuteDeleteCoursewaresAnimation)));
     4 
     5 private async Task ExecuteDeleteCoursewaresAnimation(List<CoursewareListItem> coursewares)
     6 {
     7     List<Storyboard> storyboards = new List<Storyboard>();
     8     foreach (var courseware in coursewares)
     9     {
    10         var listBoxItem = DocumentsControl.ItemContainerGenerator.ContainerFromItem(courseware) as ListBoxItem;
    11         var border = listBoxItem?.VisualDescendant<Border>();
    12         var storyboard = (Storyboard)border?.Resources["ItemRemovedStoryboard"];
    13         if (storyboard == null)
    14         {
    15             //如果找不到storyBoard,则中断动画的执行。因为删除多个Item,只执行一半的动画,界面会闪现俩次。
    16             return;
    17         }
    18         storyboards.Add(storyboard);
    19     }
    20     //删除界面课件
    21     await AsynchronousTransferHelper.ExecuteStoryboradAsync(storyboards);
    22 }
    23 
    24 public static void SetDeleteCoursewaresAnimation(DependencyObject element, UIDelegateProgress<List<CoursewareListItem>> value)
    25 {
    26     element.SetValue(DeleteCoursewaresAnimationProperty, value);
    27 }
    28 
    29 public static UIDelegateProgress<List<CoursewareListItem>> GetDeleteCoursewaresAnimation(DependencyObject element)
    30 {
    31     return (UIDelegateProgress<List<CoursewareListItem>>)element.GetValue(DeleteCoursewaresAnimationProperty);
    32 }

    动画的执行,怎么转为有同步等待呢?动画完成只有通过触发事件Completed才能确定。

    如何将动画转化为同步,可参考之前写的博客:C# 异步转同步

     1 /// <summary>
     2 /// 执行动画
     3 /// </summary>
     4 /// <param name="storyboard"></param>
     5 /// <returns></returns>
     6 public static async Task ExecuteStoryboradAsync([NotNull] Storyboard storyboard)
     7 {
     8     if (storyboard == null) throw new ArgumentNullException(nameof(storyboard));
     9 
    10     AutoResetEvent autoResetEvent = new AutoResetEvent(false);
    11 
    12     storyboard.Completed += OnStoryboardCompleted;
    13     storyboard.Begin();
    14 
    15     void OnStoryboardCompleted(object sender, EventArgs e)
    16     {
    17         storyboard.Completed -= OnStoryboardCompleted;
    18         autoResetEvent.Set();
    19     }
    20 
    21     await Task.Run(() => { autoResetEvent.WaitOne(); });
    22 }

    4. 交互处理辅助类 UIDelegateOperation 

    在UIDelegateOperation内部,每次调用时,都会新建一个UIDelegateProgress(委托进度)。委托进度,是界面交互的处理~

    UIDelegateOperation:

      1 /// <summary>
      2     /// UI交互处理-提供可调用UI交互的操作
      3     /// </summary>
      4     public class UIDelegateOperation : BindableObject, IUIDelegateAction
      5     {
      6         private UIDelegateProgress _delegateProgress;
      7 
      8         public UIDelegateProgress DelegateProgress
      9         {
     10             get => _delegateProgress;
     11             private set
     12             {
     13                 _delegateProgress = value;
     14                 OnPropertyChanged();
     15             }
     16         }
     17 
     18         /// <summary>
     19         /// 执行
     20         /// </summary>
     21         public void Execute()
     22         {
     23             var delegateProgress = new UIDelegateProgress();
     24             delegateProgress.ProgressCompleted += () =>
     25             {
     26                 _delegateProgress = null;
     27             };
     28             DelegateProgress = delegateProgress;
     29         }
     30 
     31         /// <summary>
     32         /// 异步执行
     33         /// 交互处理完成并回调
     34         /// </summary>
     35         public async Task ExecuteAsync()
     36         {
     37             AutoResetEvent autoResetEvent = new AutoResetEvent(false);
     38 
     39             var delegateProgress = new UIDelegateProgress();
     40             delegateProgress.ProgressCompleted += () =>
     41             {
     42                 _delegateProgress = null;
     43 
     44                 autoResetEvent.Set();
     45             };
     46             DelegateProgress = delegateProgress;
     47             await Task.Run(() => { autoResetEvent.WaitOne(); });
     48         }
     49     }
     50 
     51     /// <summary>
     52     /// UI交互处理-提供可同步调用UI交互的操作
     53     /// </summary>
     54     /// <typeparam name="T">输入/输出类型</typeparam>
     55     public class UIDelegateAction<T> : BindableObject, IUIDelegateAction<T>
     56     {
     57         private UIDelegateProgress<T> _delegateProgress;
     58 
     59         public UIDelegateProgress<T> DelegateProgress
     60         {
     61             get => _delegateProgress;
     62             private set
     63             {
     64                 _delegateProgress = value;
     65                 OnPropertyChanged();
     66             }
     67         }
     68         /// <summary>
     69         /// 执行
     70         /// </summary>
     71         public void Execute(T parameter)
     72         {
     73             var delegateProgress = new UIDelegateProgress<T>(parameter);
     74             delegateProgress.ProgressCompleted += () =>
     75             {
     76                 _delegateProgress = null;
     77             };
     78             DelegateProgress = delegateProgress;
     79         }
     80         /// <summary>
     81         /// 异步执行
     82         /// 交互处理完成并回调
     83         /// </summary>
     84         public async Task ExecuteAsync(T parameter)
     85         {
     86             AutoResetEvent autoResetEvent = new AutoResetEvent(false);
     87 
     88             var delegateProgress = new UIDelegateProgress<T>(parameter);
     89             delegateProgress.ProgressCompleted += () =>
     90             {
     91                 _delegateProgress = null;
     92 
     93                 autoResetEvent.Set();
     94             };
     95             DelegateProgress = delegateProgress;
     96 
     97             await Task.Run(() => { autoResetEvent.WaitOne(); });
     98         }
     99 
    100         /// <summary>
    101         /// 异步执行并返回结果
    102         /// </summary>
    103         public async Task<T> ExecuteWithResultAsync()
    104         {
    105             AutoResetEvent autoResetEvent = new AutoResetEvent(false);
    106 
    107             var delegateProgress = new UIDelegateProgress<T>();
    108             delegateProgress.ProgressCompleted += () =>
    109             {
    110                 _delegateProgress = null;
    111 
    112                 autoResetEvent.Set();
    113             };
    114             DelegateProgress = delegateProgress;
    115 
    116             await Task.Run(() => { autoResetEvent.WaitOne(); });
    117 
    118             return delegateProgress.Result;
    119         }
    120     }
    121 
    122     /// <summary>
    123     /// UI交互处理-提供可同步调用UI交互的操作
    124     /// </summary>
    125     /// <typeparam name="TInput">输入类型</typeparam>
    126     /// <typeparam name="TOut">输出类型</typeparam>
    127     public class UIDelegateAction<TInput, TOut> : BindableObject, IUIDelegateAction<TInput, TOut>
    128     {
    129         private UIDelegateProgress<TInput, TOut> _delegateProgress;
    130 
    131         public UIDelegateProgress<TInput, TOut> DelegateProgress
    132         {
    133             get => _delegateProgress;
    134             private set
    135             {
    136                 _delegateProgress = value;
    137                 OnPropertyChanged();
    138             }
    139         }
    140         /// <summary>
    141         /// 执行
    142         /// </summary>
    143         public void Execute(TInput parameter)
    144         {
    145             var delegateProgress = new UIDelegateProgress<TInput, TOut>(parameter);
    146             delegateProgress.ProgressCompleted += () =>
    147             {
    148                 _delegateProgress = null;
    149             };
    150             DelegateProgress = delegateProgress;
    151         }
    152 
    153         /// <summary>
    154         /// 执行并返回结果
    155         /// </summary>
    156         public TOut ExecuteWithResult(TInput parameter)
    157         {
    158             var delegateProgress = new UIDelegateProgress<TInput, TOut>(parameter);
    159             delegateProgress.ProgressCompleted += () =>
    160             {
    161                 _delegateProgress = null;
    162             };
    163             DelegateProgress = delegateProgress;
    164             return delegateProgress.Result;
    165         }
    166 
    167         /// <summary>
    168         /// 异步执行并返回结果
    169         /// </summary>
    170         public async Task<TOut> ExecuteWithResultAsync(TInput parameter)
    171         {
    172             var delegateProgress = new UIDelegateProgress<TInput, TOut>(parameter);
    173             await SetDelegateProgress(delegateProgress);
    174             return delegateProgress.Result;
    175         }
    176         private async Task SetDelegateProgress(UIDelegateProgress<TInput, TOut> delegateProgress)
    177         {
    178             AutoResetEvent autoResetEvent = new AutoResetEvent(false);
    179 
    180             delegateProgress.ProgressCompleted += () =>
    181             {
    182                 _delegateProgress = null;
    183                 autoResetEvent.Set();
    184             };
    185             DelegateProgress = delegateProgress;
    186             await Task.Run(() => { autoResetEvent.WaitOne(); });
    187         }
    188     }
    189 
    190     /// <summary>
    191     /// UI交互处理接口
    192     /// </summary>
    193     public interface IUIDelegateAction
    194     {
    195 
    196         UIDelegateProgress DelegateProgress { get; }
    197 
    198         /// <summary>
    199         /// 执行
    200         /// </summary>
    201         void Execute();
    202 
    203         /// <summary>
    204         /// 异步执行
    205         /// </summary>
    206         Task ExecuteAsync();
    207     }
    208 
    209     /// <summary>
    210     /// UI交互处理接口
    211     /// </summary>
    212     /// <typeparam name="T">输入/输出类型</typeparam>
    213     public interface IUIDelegateAction<T>
    214     {
    215         UIDelegateProgress<T> DelegateProgress { get; }
    216 
    217         /// <summary>
    218         /// 执行
    219         /// </summary>
    220         void Execute(T parameter);
    221 
    222         /// <summary>
    223         /// 异步执行
    224         /// </summary>
    225         Task ExecuteAsync(T parameter);
    226 
    227         /// <summary>
    228         /// 异步执行并返回结果
    229         /// </summary>
    230         Task<T> ExecuteWithResultAsync();
    231     }
    232 
    233     /// <summary>
    234     /// UI交互处理接口
    235     /// </summary>
    236     /// <typeparam name="TInput">输入类型</typeparam>
    237     /// <typeparam name="TOut">输出类型</typeparam>
    238     public interface IUIDelegateAction<TInput, TOut>
    239     {
    240         UIDelegateProgress<TInput, TOut> DelegateProgress { get; }
    241 
    242         /// <summary>
    243         /// 执行
    244         /// </summary>
    245         void Execute(TInput parameter);
    246 
    247         /// <summary>
    248         /// 执行并返回结果
    249         /// </summary>
    250         TOut ExecuteWithResult(TInput parameter);
    251 
    252         /// <summary>
    253         /// 异步执行并返回结果
    254         /// </summary>
    255         Task<TOut> ExecuteWithResultAsync(TInput parameter);
    256     }
    View Code

    UIDelegateProgress:

      1     /// <summary>
      2     /// 委托进度
      3     /// </summary>
      4     public class UIDelegateProgress
      5     {
      6         public event Action ProgressCompleted;
      7 
      8         /// <summary>
      9         /// UI委托处理
     10         /// </summary>
     11         /// <param name="uiTask"></param>
     12         public async void StartAsync(Func<Task> uiTask)
     13         {
     14             try
     15             {
     16                 await uiTask.Invoke();
     17             }
     18             catch (InvalidOperationException e)
     19             {
     20                 Log.Error("UI交互处理,产生异常!", e);
     21             }
     22             finally
     23             {
     24                 ProgressCompleted?.Invoke();
     25             }
     26         }
     27 
     28         /// <summary>
     29         /// UI委托处理
     30         /// </summary>
     31         /// <param name="uiTask"></param>
     32         public void Start(Action uiTask)
     33         {
     34             try
     35             {
     36                 uiTask.Invoke();
     37             }
     38             catch (InvalidOperationException e)
     39             {
     40                 Log.Error("UI交互处理,产生异常!", e);
     41             }
     42             finally
     43             {
     44                 ProgressCompleted?.Invoke();
     45             }
     46         }
     47     }
     48 
     49     /// <summary>
     50     /// 委托进度
     51     /// </summary>
     52     public class UIDelegateProgress<T>
     53     {
     54         public event Action ProgressCompleted;
     55 
     56         /// <summary>
     57         /// 输入参数
     58         /// </summary>
     59         public T Parameter { get; set; }
     60 
     61         /// <summary>
     62         /// 输出参数
     63         /// </summary>
     64         public T Result { get; set; }
     65 
     66         public UIDelegateProgress()
     67         {
     68 
     69         }
     70         public UIDelegateProgress(T parameter)
     71         {
     72             Parameter = parameter;
     73         }
     74 
     75         /// <summary>
     76         /// UI委托处理
     77         /// </summary>
     78         /// <param name="uiTask"></param>
     79         public void Start(Action<T> uiTask)
     80         {
     81             try
     82             {
     83                 uiTask.Invoke(Parameter);
     84             }
     85             catch (InvalidOperationException e)
     86             {
     87                 Log.Error("UI交互处理,产生异常!", e);
     88             }
     89             finally
     90             {
     91                 ProgressCompleted?.Invoke();
     92             }
     93         }
     94 
     95         /// <summary>
     96         /// UI委托处理
     97         /// </summary>
     98         /// <param name="uiTask"></param>
     99         public async void StartAsync(Func<T, Task> uiTask)
    100         {
    101             try
    102             {
    103                 await uiTask.Invoke(Parameter);
    104             }
    105             catch (InvalidOperationException e)
    106             {
    107                 Log.Error("UI交互处理,产生异常!", e);
    108             }
    109             finally
    110             {
    111                 ProgressCompleted?.Invoke();
    112             }
    113         }
    114 
    115         /// <summary>
    116         /// UI委托处理
    117         /// </summary>
    118         /// <param name="uiTask"></param>
    119         public void Start(Func<T> uiTask)
    120         {
    121             try
    122             {
    123                 Result = uiTask.Invoke();
    124             }
    125             catch (InvalidOperationException e)
    126             {
    127                 Log.Error("UI交互处理,产生异常!", e);
    128             }
    129             finally
    130             {
    131                 ProgressCompleted?.Invoke();
    132             }
    133         }
    134 
    135         /// <summary>
    136         /// UI委托处理
    137         /// </summary>
    138         /// <param name="uiTask"></param>
    139         public async void StartAsync(Func<Task<T>> uiTask)
    140         {
    141             try
    142             {
    143                 Result = await uiTask.Invoke();
    144             }
    145             catch (InvalidOperationException e)
    146             {
    147                 Log.Error("UI交互处理,产生异常!", e);
    148             }
    149             finally
    150             {
    151                 ProgressCompleted?.Invoke();
    152             }
    153         }
    154     }
    155 
    156     /// <summary>
    157     /// 委托进度
    158     /// </summary>
    159     public class UIDelegateProgress<TInput, TOut>
    160     {
    161         public event Action ProgressCompleted;
    162 
    163         /// <summary>
    164         /// 输入参数
    165         /// </summary>
    166         public TInput Parameter { get; set; }
    167 
    168         /// <summary>
    169         /// 输出参数
    170         /// </summary>
    171         public TOut Result { get; set; }
    172 
    173         public UIDelegateProgress(TInput parameter)
    174         {
    175             Parameter = parameter;
    176         }
    177 
    178         /// <summary>
    179         /// UI委托处理
    180         /// </summary>
    181         /// <param name="uiTask"></param>
    182         public async void StartAsync(Func<TInput, Task<TOut>> uiTask)
    183         {
    184             try
    185             {
    186                 Result = await uiTask.Invoke(Parameter);
    187             }
    188             catch (InvalidOperationException e)
    189             {
    190                 Log.Error("UI交互处理,产生异常!", e);
    191             }
    192             finally
    193             {
    194                 ProgressCompleted?.Invoke();
    195             }
    196         }
    197 
    198         /// <summary>
    199         /// UI委托处理
    200         /// </summary>
    201         /// <param name="uiTask"></param>
    202         public void Start(Func<TOut> uiTask)
    203         {
    204             try
    205             {
    206                 uiTask.Invoke();
    207             }
    208             catch (InvalidOperationException e)
    209             {
    210                 Log.Error("UI交互处理,产生异常!", e);
    211             }
    212             finally
    213             {
    214                 ProgressCompleted?.Invoke();
    215             }
    216         }
    217 
    218         /// <summary>
    219         /// UI委托处理
    220         /// </summary>
    221         /// <param name="uiTask"></param>
    222         public void Start(Func<TInput, TOut> uiTask)
    223         {
    224             try
    225             {
    226                 Result = uiTask.Invoke(Parameter);
    227             }
    228             catch (InvalidOperationException e)
    229             {
    230                 Log.Error("UI交互处理,产生异常!", e);
    231             }
    232             finally
    233             {
    234                 ProgressCompleted?.Invoke();
    235             }
    236         }
    237     }
    View Code

    Demo中,举例了界面的删除操作

    https://github.com/Kybs0/MVVM.DataAndInteractionIsolation

    InvokeCommandAction

    详细请参考Command篇

    通过InvokeCommandAction 的使用,WPF任意事件都可以绑定Command,将业务逻辑放在ViewModel中。如:

    1 <TextBlock>
    2     <i:Interaction.Triggers>
    3         <i:EventTrigger EventName="MouseLeftButtonDown">
    4             <i:InvokeCommandAction Command="{Binding MouseLeftButtonDownCommand}"/>
    5         </i:EventTrigger>
    6     </i:Interaction.Triggers>
    7 </TextBlock>

    关键字:UI分离,交互与数据分离,动画同步,单元测试

  • 相关阅读:
    display,visibility,meta知识
    存储过程
    Asp.Net碎知识
    分页
    配置IIS
    SQLAlchemy(三):外键、连表关系
    SQLAlchemy(二):SQLAlchemy对数据的增删改查操作、属性常用数据类型详解
    SQLAlchemy(一):SQLAlchemy去连接数据库、ORM介绍、将ORM模型映射到数据库中
    数据可视化之DAX篇(十)在PowerBI中累计求和的两种方式
    数据可视化之DAX篇(九) 关于DAX中的VAR,你应该避免的一个常见错误
  • 原文地址:https://www.cnblogs.com/kybs0/p/9053683.html
Copyright © 2020-2023  润新知