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

简介: 在我们使用WPF过程中,不可避免并且超级喜欢使用MVVM框架。 那么,使用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分离,交互与数据分离,动画同步,单元测试

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
目录
相关文章
|
2月前
|
搜索推荐 前端开发 C#
推荐7款美观且功能强大的WPF UI库
推荐7款美观且功能强大的WPF UI库
|
2月前
|
设计模式 前端开发 C#
WPF 项目中 MVVM模式 的简单例子说明
本文通过WPF项目中的加法操作示例,讲解了MVVM模式的结构和实现方法,包括数据模型、视图、视图模型的创建和数据绑定,以及命令的实现和事件通知机制。
|
2月前
|
前端开发 图形学 开发者
【独家揭秘】那些让你的游戏瞬间鲜活起来的Unity UI动画技巧:从零开始打造动态按钮,提升玩家交互体验的绝招大公开!
【9月更文挑战第1天】在游戏开发领域,Unity 是最受欢迎的游戏引擎之一,其强大的跨平台发布能力和丰富的功能集让开发者能够迅速打造出高质量的游戏。优秀的 UI 设计对于游戏至关重要,尤其是在手游市场,出色的 UI 能给玩家留下深刻的第一印象。Unity 的 UGUI 系统提供了一整套解决方案,包括 Canvas、Image 和 Button 等组件,支持添加各种动画效果。
132 3
|
3月前
|
前端开发 C# 设计模式
“深度剖析WPF开发中的设计模式应用:以MVVM为核心,手把手教你重构代码结构,实现软件工程的最佳实践与高效协作”
【8月更文挑战第31天】设计模式是在软件工程中解决常见问题的成熟方案。在WPF开发中,合理应用如MVC、MVVM及工厂模式等能显著提升代码质量和可维护性。本文通过具体案例,详细解析了这些模式的实际应用,特别是MVVM模式如何通过分离UI逻辑与业务逻辑,实现视图与模型的松耦合,从而优化代码结构并提高开发效率。通过示例代码展示了从模型定义、视图模型管理到视图展示的全过程,帮助读者更好地理解并应用这些模式。
95 0
|
3月前
|
vr&ar C# 图形学
WPF与AR/VR的激情碰撞:解锁Windows Presentation Foundation应用新维度,探索增强现实与虚拟现实技术在现代UI设计中的无限可能与实战应用详解
【8月更文挑战第31天】增强现实(AR)与虚拟现实(VR)技术正迅速改变生活和工作方式,在游戏、教育及工业等领域展现出广泛应用前景。本文探讨如何在Windows Presentation Foundation(WPF)环境中实现AR/VR功能,通过具体示例代码展示整合过程。尽管WPF本身不直接支持AR/VR,但借助第三方库如Unity、Vuforia或OpenVR,可实现沉浸式体验。例如,通过Unity和Vuforia在WPF中创建AR应用,或利用OpenVR在WPF中集成VR功能,从而提升用户体验并拓展应用功能边界。
68 0
|
3月前
|
传感器 C# 监控
硬件交互新体验:WPF与传感器的完美结合——从初始化串行端口到读取温度数据,一步步教你打造实时监控的智能应用
【8月更文挑战第31天】本文通过详细教程,指导Windows Presentation Foundation (WPF) 开发者如何读取并处理温度传感器数据,增强应用程序的功能性和用户体验。首先,通过`.NET Framework`的`Serial Port`类实现与传感器的串行通信;接着,创建WPF界面显示实时数据;最后,提供示例代码说明如何初始化串行端口及读取数据。无论哪种传感器,只要支持串行通信,均可采用类似方法集成到WPF应用中。适合希望掌握硬件交互技术的WPF开发者参考。
68 0
|
3月前
|
C# Windows 监控
WPF应用跨界成长秘籍:深度揭秘如何与Windows服务完美交互,扩展功能无界限!
【8月更文挑战第31天】WPF(Windows Presentation Foundation)是 .NET 框架下的图形界面技术,具有丰富的界面设计和灵活的客户端功能。在某些场景下,WPF 应用需与 Windows 服务交互以实现后台任务处理、系统监控等功能。本文探讨了两者交互的方法,并通过示例代码展示了如何扩展 WPF 应用的功能。首先介绍了 Windows 服务的基础知识,然后阐述了创建 Windows 服务、设计通信接口及 WPF 客户端调用服务的具体步骤。通过合理的交互设计,WPF 应用可获得更强的后台处理能力和系统级操作权限,提升应用的整体性能。
104 0
|
3月前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
58 0
|
3月前
|
容器 C# 开发者
XAML语言大揭秘:WPF标记的魅力所在,让你轻松实现界面与逻辑分离,告别复杂代码!
【8月更文挑战第31天】XAML提供了一种直观且易于维护的界面设计方式,使得开发者可以专注于逻辑和业务代码的编写,而无需关心界面细节。通过数据绑定、布局管理和动画效果等特性,XAML可以实现丰富的界面交互和视觉效果。在实际开发过程中,开发者应根据具体需求选择合适的技术方案,以确保应用程序能够满足用户的需求。希望本文的内容能够帮助您在WPF应用程序开发中更好地利用XAML语言。
44 0
|
3月前
|
前端开发 开发者 C#
WPF开发者必读:MVVM模式实战,轻松实现现代桌面应用架构,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离应用程序的逻辑和界面,提高了代码的可维护性和可扩展性。本文介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定和逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种方式,开发者可以构建更加高效和可扩展的桌面应用程序。
159 0