bovender framework
C# framework that implements MVVM and more
ViewModelBase.cs
1 /* ViewModelBase.cs
2  * part of Daniel's XL Toolbox NG
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.ComponentModel;
20 using System.Windows;
21 using System.Windows.Input;
22 using System.Threading;
23 using System.Windows.Threading;
24 using Bovender.Extensions;
25 using System.Threading.Tasks;
26 
27 namespace Bovender.Mvvm.ViewModels
28 {
29  public abstract class ViewModelBase : INotifyPropertyChanged
30  {
31  #region Public properties
32 
33  public virtual string DisplayString
34  {
35  get
36  {
37  return _displayString;
38  }
39  set
40  {
41  if (value != _displayString)
42  {
43  _displayString = value;
44  OnPropertyChanged("DisplayString");
45  }
46  }
47  }
48 
49  public bool IsSelected
50  {
51  get
52  {
53  return _isSelected;
54  }
55  set
56  {
57  _isSelected = value;
58  OnPropertyChanged("IsSelected");
59  }
60  }
61 
62  public Dispatcher ViewDispatcher { get; set; }
63 
64  #endregion
65 
66  #region Public methods
67 
77  public bool IsViewModelOf(object model)
78  {
79  object wrappedObject = RevealModelObject();
80  if (model == null || wrappedObject == null)
81  {
82  return false;
83  }
84  else
85  {
86  return wrappedObject.Equals(model);
87  }
88  }
89 
90  #endregion
91 
92  #region Public injectors
93 
102  public Window InjectInto<T>() where T : Window, new()
103  {
104  T view = new T();
105  return InjectInto(view);
106  }
107 
114  public Window InjectInto(Window view)
115  {
116  if (view != null)
117  {
118  EventHandler h = null;
119  h = (sender, args) =>
120  {
121  this.RequestCloseView -= h;
122  view.Dispatcher.BeginInvoke(new Action(() =>
123  {
124  view.DataContext = null;
125  view.Close();
126  }));
127  };
128  this.RequestCloseView += h;
129  view.DataContext = this;
130  ViewDispatcher = view.Dispatcher;
131  }
132  return view;
133  }
134 
143  public void InjectAndShowInThread<T>(IntPtr ownerForm) where T: Window, new()
144  {
145  Thread t = new Thread(() =>
146  {
147  T view = new T();
148  EventHandler h = null;
149  h = (sender, args) =>
150  {
151  this.RequestCloseView -= h;
152  // view.Close();
153  view.Dispatcher.Invoke(new Action(view.Close));
154  view.Dispatcher.InvokeShutdown();
155  };
156  this.RequestCloseView += h;
157  ViewDispatcher = view.Dispatcher;
158  view.DataContext = this;
159  // Must shut down the Dispatcher, but this has been moved from
160  // the Closed event of the view to the RequestCloseView event
161  // of the view model in order to prevent premature termination
162  // of the thread if there are other views that this view model
163  // has been injected into.
164  // view.Closed += (sender, args) => view.Dispatcher.InvokeShutdown();
165  view.ShowInForm(ownerForm);
166  System.Windows.Threading.Dispatcher.Run();
167  });
168  t.SetApartmentState(ApartmentState.STA);
169  t.Start();
170  }
171 
177  public void InjectAndShowInThread<T>() where T: Window, new()
178  {
179  InjectAndShowInThread<T>(IntPtr.Zero);
180  }
181 
189  public void InjectAndShowDialogInThread<T>(IntPtr ownerForm) where T : Window, new()
190  {
191  Thread t = new Thread(() =>
192  {
193  T view = new T();
194  EventHandler h = null;
195  h = (sender, args) =>
196  {
197  this.RequestCloseView -= h;
198  // view.Close();
199  view.Dispatcher.Invoke(new Action(view.Close));
200  view.Dispatcher.InvokeShutdown();
201  };
202  this.RequestCloseView += h;
203  ViewDispatcher = view.Dispatcher;
204  view.DataContext = this;
205  // Must shut down the Dispatcher, but this has been moved from
206  // the Closed event of the view to the RequestCloseView event
207  // of the view model in order to prevent premature termination
208  // of the thread if there are other views that this view model
209  // has been injected into.
210  // view.Closed += (sender, args) => view.Dispatcher.InvokeShutdown();
211  view.ShowDialogInForm(ownerForm);
212  // System.Windows.Threading.Dispatcher.Run();
213  });
214  t.SetApartmentState(ApartmentState.STA);
215  t.Start();
216  }
217 
218  #endregion
219 
220  #region Public (abstract) methods
221 
234  public abstract object RevealModelObject();
235 
236  #endregion
237 
238  #region Events
239 
244  public event EventHandler RequestCloseView;
245 
246  #endregion
247 
248  #region Commands
249 
250  public ICommand CloseViewCommand
251  {
252  get
253  {
254  if (_closeViewCommand == null)
255  {
256  _closeViewCommand = new DelegatingCommand(
257  parameter => { DoCloseView(); },
258  parameter => { return CanCloseView(); }
259  );
260  };
261  return _closeViewCommand;
262  }
263  }
264 
265  #endregion
266 
267  #region Constructor
268 
272  protected ViewModelBase()
273  {
274  // Capture the current dispatcher to enable
275  // asynchronous operations that a view can
276  // react to dispite running in another thread.
277  Dispatcher = Dispatcher.CurrentDispatcher;
278 
279  if (SynchronizationContext.Current != null)
280  {
281  SyncContext = TaskScheduler.FromCurrentSynchronizationContext();
282  }
283  }
284 
285  #endregion
286 
287  #region INotifyPropertyChanged interface
288 
289  public event PropertyChangedEventHandler PropertyChanged;
290 
291  #endregion
292 
293  #region Protected methods
294 
295  protected virtual void OnPropertyChanged(string propertyName)
296  {
297  if (PropertyChanged != null)
298  {
299  PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
300  }
301  }
302 
303  protected virtual bool CanCloseView()
304  {
305  return true;
306  }
307 
308  protected virtual void DoCloseView()
309  {
310  if (RequestCloseView != null && CanCloseView())
311  {
312  RequestCloseView(this, EventArgs.Empty);
313  }
314  }
315 
321  protected CancellationToken Dispatch(Action action)
322  {
323  if (SyncContext == null)
324  {
325  Logger.Info("Dispatch: Dispatching with dispatcher");
326  Dispatcher.Invoke(action);
327  return CancellationToken.None;
328  }
329  else
330  {
331  Logger.Info("Dispatch: Dispatching on current synchronization context");
332  // TaskScheduler context = TaskScheduler.FromCurrentSynchronizationContext();
333  CancellationToken token = new CancellationToken();
334  Task.Factory.StartNew(action, token, TaskCreationOptions.None, SyncContext);
335  return token;
336  }
337  }
338 
339  #endregion
340 
341  #region Protected properties
342 
347  protected Dispatcher Dispatcher { get; private set; }
348 
349  protected TaskScheduler SyncContext { get; private set; }
350 
351  #endregion
352 
353  #region Private fields
354 
355  private string _displayString;
356  private DelegatingCommand _closeViewCommand;
357  private bool _isSelected;
358 
359  #endregion
360 
361  #region Class logger
362 
363  private static NLog.Logger Logger { get { return _logger.Value; } }
364 
365  private static readonly Lazy<NLog.Logger> _logger = new Lazy<NLog.Logger>(() => NLog.LogManager.GetCurrentClassLogger());
366 
367  #endregion
368  }
369 }
CancellationToken Dispatch(Action action)
Dispatches an action in the current synchronization context if one exists, or using the Dispatcher...
Command that implements ICommand and accepts delegates that contain the command implementation.
ViewModelBase()
Does not allow public instantiation of this class.
Window InjectInto(Window view)
Injects the view model into an existing view by setting the view&#39;s DataContext.
EventHandler RequestCloseView
Raised by the CloseView Command, signals that associated views are to be closed.
bool IsViewModelOf(object model)
Determines whether the current object is a view model of a particular model object.