bovender framework
C# framework that implements MVVM and more
ProcessViewModelBase.cs
1 /* ProcessViewModelBase.cs
2  * part of Bovender framework
3  *
4  * Copyright 2014-2018 Daniel Kraus
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 using System;
19 using System.Collections.Generic;
20 using System.Linq;
21 using System.Text;
22 using System.Threading;
23 using Bovender.Mvvm.Messaging;
24 using System.Threading.Tasks;
25 
26 namespace Bovender.Mvvm.ViewModels
27 {
69  public abstract class ProcessViewModelBase : ViewModelBase
70  {
71  #region Properties
72 
73  public Bovender.Mvvm.Models.IProcessModel ProcessModel { get; protected set; }
74 
75  public bool IsProcessing
76  {
77  get
78  {
79  return ProcessMessageContent.Processing;
80  }
81  }
82 
83  public bool IsIndeterminate { get { return ProcessMessageContent.IsIndeterminate; } }
84 
85  public bool WasCancelled
86  {
87  get
88  {
89  return ProcessMessageContent.WasCancelled;
90  }
91  }
92 
93  public bool WasSuccessful
94  {
95  get
96  {
97  return ProcessMessageContent.WasSuccessful;
98  }
99  }
100 
101  public virtual Exception Exception { get; protected set; }
102 
103  #endregion
104 
105  #region Public methods
106 
112  public virtual void StartProcess()
113  {
114  Logger.Info("StartProcess: Starting process...");
115  _progressTimer = new Timer(
116  UpdateProgress,
117  null,
118  Properties.Settings.Default.ShowProgressDelay,
119  Properties.Settings.Default.ShowProgressInterval);
120  Execute();
121  }
122 
126  public virtual void CancelProcess()
127  {
128  ProcessMessageContent.WasCancelled = true;
129  ProcessMessageContent.Processing = false;
130  Logger.Info("CancelProcess: CancelProcess was called!");
131  ProcessModel.Cancel();
132  }
133 
134  #endregion
135 
136  #region MVVM messages
137 
144  public Message<ProcessMessageContent> ShowProgressMessage
145  {
146  get
147  {
148  if (_showProgressMessage == null)
149  {
150  _showProgressMessage = new Message<ProcessMessageContent>();
151  }
152  return _showProgressMessage;
153  }
154  }
155 
159  public Message<ProcessMessageContent> ProcessFinishedMessage
160  {
161  get
162  {
163  if (_processFinishedMessage == null)
164  {
165  _processFinishedMessage = new Message<ProcessMessageContent>();
166  }
167  return _processFinishedMessage;
168  }
169  }
170 
171  #endregion
172 
173  #region Abstract methods
174 
185  protected abstract void UpdateProcessMessageContent(ProcessMessageContent processMessageContent);
186 
187  #endregion
188 
189  #region Constructor
190 
191  protected ProcessViewModelBase(Models.IProcessModel processModel)
192  : base()
193  {
194  ProcessModel = processModel;
195  }
196 
197  #endregion
198 
199  #region Protected methods
200 
207  protected virtual bool BeforeStartProcess()
208  {
209  Logger.Info("BeforeStartProcess");
210  return true;
211  }
212 
218  protected virtual void AfterStartProcess()
219  {
220  Logger.Info("AfterStartProcess");
221  CloseViewCommand.Execute(null);
222  }
223 
228  protected virtual void SendProcessFinishedMessage()
229  {
230  Logger.Info("SendProcessFinishedMessage: Sending message");
231  ProcessMessageContent.Processing = false;
232  ProcessMessageContent.Exception = Exception;
233  ProcessMessageContent.WasSuccessful = Exception == null;
234  ProcessFinishedMessage.Send(ProcessMessageContent);
236  }
237 
238  #endregion
239 
240  #region Overrides
241 
246  public override object RevealModelObject()
247  {
248  return ProcessModel;
249  }
250 
251  #endregion
252 
253  #region Protected properties
254 
261  {
262  get
263  {
264  if (_processMessageContent == null)
265  {
266  Logger.Debug("Creating new ProcessMessageContent instance");
267  _processMessageContent = new ProcessMessageContent(this, CancelProcess);
268  }
269  return _processMessageContent;
270  }
271  }
272 
273  #endregion
274 
275  #region Private methods
276 
286  private void Execute()
287  {
288  if (ProcessMessageContent.Processing)
289  {
290  Logger.Fatal("Execute: Process is already running!");
291  throw new InvalidOperationException("Cannot start the process because it is already running");
292  }
293  if (!BeforeStartProcess()) return;
294 
295  ProcessMessageContent.Processing = true;
296  ProcessMessageContent.WasSuccessful = false;
297  ProcessMessageContent.WasCancelled = false;
298  Logger.Info("Execute: Starting task");
299  Task.Factory.StartNew((Action)(() =>
300  {
301  try
302  {
303  ProcessMessageContent.WasSuccessful = ProcessModel.Execute();
304  }
305  catch (Exception e)
306  {
307  Logger.Warn("Execute: Caught an exception!");
308  Logger.Warn(e);
309  Exception = e;
310  }
311  })).ContinueWith((task) => Dispatch(SendProcessFinishedMessage));
312  AfterStartProcess();
313  }
314 
320  private void UpdateProgress(object state)
321  {
322  lock (_lockUpdateProgress)
323  {
324  if (!_showProgressWasSent && ProcessMessageContent.Processing)
325  {
326  _showProgressWasSent = true;
327  Dispatch(() =>
328  {
329  if (IsIndeterminate)
330  {
331  Logger.Info("UpdateProgress: The process is indeterminate");
332  }
333  Logger.Info("UpdateProgress: Sending ShowProgressMessage");
334  ShowProgressMessage.Send(ProcessMessageContent);
335  Logger.Debug("UpdateProgress: ... ShowProgressMessage was sent");
336  });
337  }
338  else
339  {
340  if (ProcessMessageContent.Processing)
341  {
342  if (!IsIndeterminate)
343  {
344  UpdateProcessMessageContent(ProcessMessageContent);
345  Logger.Info("UpdateProgress: PercentCompleted: {0}",
346  ProcessMessageContent.PercentCompleted);
347  }
348  }
349  else
350  {
351  Logger.Info("UpdateProgress: No longer processing, disposing update timer");
352  _progressTimer.Dispose();
353  }
354  }
355  }
356  }
357 
358  #endregion
359 
360  #region Private fields
361 
362  private Message<ProcessMessageContent> _showProgressMessage;
363  private Message<ProcessMessageContent> _processFinishedMessage;
364  private ProcessMessageContent _processMessageContent;
365  private Timer _progressTimer;
366  private bool _showProgressWasSent;
367 
368  #endregion
369 
370  #region Private static fields
371 
372  private static readonly object _lockUpdateProgress = new object();
373 
374  #endregion
375 
376  #region Class logger
377 
378  private static NLog.Logger Logger { get { return _logger.Value; } }
379 
380  private static readonly Lazy<NLog.Logger> _logger = new Lazy<NLog.Logger>(() => NLog.LogManager.GetCurrentClassLogger());
381 
382  #endregion
383  }
384 }
Holds information about percent completion of a process and defines events that occur when the proces...
virtual void CancelProcess()
Cancels the ongoing process.
virtual void Send(T messageContent, Action< T > respond)
Calling this method will raise the Sent event with a message content and a callback method that can b...
Definition: Message.cs:52
virtual void StartProcess()
Entry point to starts the process.
virtual void AfterStartProcess()
Additional work to do after the process has started.
Abstract base class for view models that deal with processes.
Exception Exception
If something in the process went wrong, this will be the corresponding exception. ...
virtual void SendProcessFinishedMessage()
Sends the ProcessMessageContent.CompletedMessage to signal that the process has finished.
override object RevealModelObject()
Returns the associated Bovender.Mvvm.Models.ProcessModel (if any).
virtual bool BeforeStartProcess()
Additional work to do before the process is started.