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分離,交互與數據分離,動畫同步,單元測試


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM