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

简介: 原文:WPF MVVM UI分离之《交互与数据分离》在我们使用WPF过程中,不可避免并且超级喜欢使用MVVM框架。 那么,使用MVVM的出发点是视觉与业务逻辑分离,即UI与数据分离 诸如下面的问题: 删除操作,假如需要先执行一部分数据的处理,然后删除界面列表中的子项,之后再执行其它数据的处理。
原文: 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

 

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

目录
相关文章
|
22天前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
|
2月前
element ui实现多层级复杂表单的操作(添加与回显)之表单操作交互操作
element ui实现多层级复杂表单的操作(添加与回显)之表单操作交互操作
34 0
|
2月前
使用 SAP UI5 Event Bus 机制,修复 SAP UI5 分页显示数据的一个 bug 试读版
使用 SAP UI5 Event Bus 机制,修复 SAP UI5 分页显示数据的一个 bug 试读版
20 0
|
2月前
|
JavaScript
vue element-ui中有关表格中的数据整条显示红色/绿色等等颜色的问题
vue element-ui中有关表格中的数据整条显示红色/绿色等等颜色的问题
24 1
|
19天前
【UI】 element ui 表格没有数据时用--填充
【UI】 element ui 表格没有数据时用--填充
21 2
|
19天前
【UI】 vue2 修改elementui 表格table 为空时暂无数据样式
【UI】 vue2 修改elementui 表格table 为空时暂无数据样式
16 1
|
3月前
|
前端开发 C# 索引
浅谈WPF之UI布局
一个成功的软件,离不开人性化的UI设计,如何抓住用户第一视觉,让用户产生依赖感,合适优雅的布局必不可少。本文以一些简单的小例子,简述WPF中布局 面板 控件的使用,仅供学习分享使用,如有不足之处,还请指正。
39 1
|
4月前
|
搜索推荐 C# 开发者
3个值得推荐的WPF UI组件库
3个值得推荐的WPF UI组件库
155 0
|
4月前
|
前端开发 JavaScript C#
一个WPF版的Layui前端UI库
一个WPF版的Layui前端UI库
|
5月前
|
搜索推荐 C#
一个适用于定制个性化界面的WPF UI组件库
一个适用于定制个性化界面的WPF UI组件库