WPF Custom Command And Binding

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Windows.Input;
  4 
  5 namespace WPF.Commands
  6 {
  7   /// <summary>
  8   ///     This class allows delegating the commanding logic to methods passed as parameters,
  9   ///     and enables a View to bind commands to objects that are not part of the element tree.
 10   /// </summary>
 11   public class DelegateCommand : ICommand
 12   {
 13     #region Constructors
 14 
 15     /// <summary>
 16     /// Constructor
 17     /// </summary>
 18     public DelegateCommand(Action executeMethod)
 19         : this(executeMethod, null, false)
 20     {
 21     }
 22 
 23     /// <summary>
 24     ///     Constructor
 25     /// </summary>
 26     public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
 27         : this(executeMethod, canExecuteMethod, false)
 28     {
 29     }
 30 
 31     /// <summary>
 32     ///     Constructor
 33     /// </summary>
 34     public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
 35     {
 36       if (executeMethod == null)
 37       {
 38         throw new ArgumentNullException("executeMethod");
 39       }
 40 
 41       _executeMethod = executeMethod;
 42       _canExecuteMethod = canExecuteMethod;
 43       _isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
 44     }
 45 
 46     #endregion
 47 
 48     #region Public Methods
 49 
 50     /// <summary>
 51     ///     Method to determine if the command can be executed
 52     /// </summary>
 53     public bool CanExecute()
 54     {
 55       if (_canExecuteMethod != null)
 56       {
 57         return _canExecuteMethod();
 58       }
 59       return true;
 60     }
 61 
 62     /// <summary>
 63     ///     Execution of the command
 64     /// </summary>
 65     public void Execute()
 66     {
 67       if (_executeMethod != null)
 68       {
 69         _executeMethod();
 70       }
 71     }
 72 
 73     /// <summary>
 74     ///     Property to enable or disable CommandManager's automatic requery on this command
 75     /// </summary>
 76     public bool IsAutomaticRequeryDisabled
 77     {
 78       get
 79       {
 80         return _isAutomaticRequeryDisabled;
 81       }
 82       set
 83       {
 84         if (_isAutomaticRequeryDisabled != value)
 85         {
 86           if (value)
 87           {
 88             CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
 89           }
 90           else
 91           {
 92             CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
 93           }
 94           _isAutomaticRequeryDisabled = value;
 95         }
 96       }
 97     }
 98 
 99     /// <summary>
100     ///     Raises the CanExecuteChaged event
101     /// </summary>
102     public void RaiseCanExecuteChanged()
103     {
104       OnCanExecuteChanged();
105     }
106 
107     /// <summary>
108     ///     Protected virtual method to raise CanExecuteChanged event
109     /// </summary>
110     protected virtual void OnCanExecuteChanged()
111     {
112       CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
113     }
114 
115     #endregion
116 
117     #region ICommand Members
118 
119     /// <summary>
120     /// ICommand.CanExecuteChanged implementation
121     /// </summary>
122     public event EventHandler CanExecuteChanged
123     {
124       add
125       {
126         if (!_isAutomaticRequeryDisabled)
127         {
128           CommandManager.RequerySuggested += value;
129         }
130         CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
131       }
132       remove
133       {
134         if (!_isAutomaticRequeryDisabled)
135         {
136           CommandManager.RequerySuggested -= value;
137         }
138         CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
139       }
140     }
141 
142     /// <summary>
143     /// 
144     /// </summary>
145     /// <param name="parameter"></param>
146     /// <returns></returns>
147     bool ICommand.CanExecute(object parameter)
148     {
149       return CanExecute();
150     }
151 
152     /// <summary>
153     /// 
154     /// </summary>
155     /// <param name="parameter"></param>
156     void ICommand.Execute(object parameter)
157     {
158       Execute();
159     }
160 
161     #endregion
162 
163     #region Data
164     /// <summary>
165     /// 
166     /// </summary>
167     private readonly Action _executeMethod = null;
168     /// <summary>
169     /// 
170     /// </summary>
171     private readonly Func<bool> _canExecuteMethod = null;
172     /// <summary>
173     /// 
174     /// </summary>
175     private bool _isAutomaticRequeryDisabled = false;
176     /// <summary>
177     /// 
178     /// </summary>
179     private List<WeakReference> _canExecuteChangedHandlers;
180 
181     #endregion
182   }
183 
184   /// <summary>
185   ///     This class allows delegating the commanding logic to methods passed as parameters,
186   ///     and enables a View to bind commands to objects that are not part of the element tree.
187   /// </summary>
188   /// <typeparam name="TExecuteParameter">Type of the parameter passed to the delegates</typeparam>
189   public class DelegateCommand<TExecuteParameter> : ICommand
190   {
191     #region Constructors
192 
193     /// <summary>
194     ///     Constructor
195     /// </summary>
196     public DelegateCommand(Action<TExecuteParameter> onExecute)
197         : this(onExecute, null, false)
198     {
199     }
200 
201     /// <summary>
202     ///     Constructor
203     /// </summary>
204     public DelegateCommand(Action<TExecuteParameter> onExecute, Func<TExecuteParameter, bool> canExecute)
205         : this(onExecute, canExecute, false)
206     {
207     }
208 
209     /// <summary>
210     ///     Constructor
211     /// </summary>
212     public DelegateCommand(Action<TExecuteParameter> onExecute, Func<TExecuteParameter, bool> canExecute, bool isAutomaticRequeryDisabled)
213     {
214       if (onExecute == null)
215       {
216         throw new ArgumentNullException("executeMethod");
217       }
218       _onExecute = onExecute;
219       _canExecute = canExecute;
220       _isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
221     }
222 
223     #endregion
224 
225     #region Public Methods
226 
227     /// <summary>
228     ///     Method to determine if the command can be executed
229     /// </summary>
230     public bool CanExecute(TExecuteParameter parameter)
231     {
232       if (_canExecute != null)
233       {
234         return _canExecute(parameter);
235       }
236       return true;
237     }
238 
239     /// <summary>
240     ///     Execution of the command
241     /// </summary>
242     public void Execute(TExecuteParameter parameter)
243     {
244       if (_onExecute != null)
245       {
246         _onExecute(parameter);
247       }
248     }
249 
250     /// <summary>
251     ///     Raises the CanExecuteChaged event
252     /// </summary>
253     public void RaiseCanExecuteChanged()
254     {
255       OnCanExecuteChanged();
256     }
257 
258     /// <summary>
259     ///     Protected virtual method to raise CanExecuteChanged event
260     /// </summary>
261     protected virtual void OnCanExecuteChanged()
262     {
263       CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
264     }
265 
266     /// <summary>
267     ///     Property to enable or disable CommandManager's automatic requery on this command
268     /// </summary>
269     public bool IsAutomaticRequeryDisabled
270     {
271       get
272       {
273         return _isAutomaticRequeryDisabled;
274       }
275       set
276       {
277         if (_isAutomaticRequeryDisabled != value)
278         {
279           if (value)
280           {
281             CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
282           }
283           else
284           {
285             CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
286           }
287           _isAutomaticRequeryDisabled = value;
288         }
289       }
290     }
291 
292     #endregion
293 
294     #region ICommand Members
295 
296     /// <summary>
297     ///     ICommand.CanExecuteChanged implementation
298     /// </summary>
299     public event EventHandler CanExecuteChanged
300     {
301       add
302       {
303         if (!_isAutomaticRequeryDisabled)
304         {
305           CommandManager.RequerySuggested += value;
306         }
307         CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
308       }
309       remove
310       {
311         if (!_isAutomaticRequeryDisabled)
312         {
313           CommandManager.RequerySuggested -= value;
314         }
315         CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
316       }
317     }
318 
319     /// <summary>
320     /// 
321     /// </summary>
322     /// <param name="parameter"></param>
323     /// <returns></returns>
324     bool ICommand.CanExecute(object parameter)
325     {
326       // if T is of value type and the parameter is not
327       // set yet, then return false if CanExecute delegate
328       // exists, else return true
329       var type = typeof(TExecuteParameter);
330       if (type.IsValueType && type.IsPrimitive)
331       {
332         if (int.TryParse(parameter.ToString(), out int intResult))
333         {
334           return CanExecute((TExecuteParameter)(object)intResult);
335         }
336       }
337 
338       if (parameter == null && type.IsValueType)
339       {
340         return (_canExecute == null);
341       }
342       return CanExecute((TExecuteParameter)parameter);
343     }
344 
345     /// <summary>
346     /// 
347     /// </summary>
348     /// <param name="parameter"></param>
349     void ICommand.Execute(object parameter)
350     {
351       Execute((TExecuteParameter)parameter);
352     }
353 
354     #endregion
355 
356     #region Data
357     /// <summary>
358     /// 
359     /// </summary>
360     private readonly Action<TExecuteParameter> _onExecute = null;
361     /// <summary>
362     /// 
363     /// </summary>
364     private readonly Func<TExecuteParameter, bool> _canExecute = null;
365     /// <summary>
366     /// 
367     /// </summary>
368     private bool _isAutomaticRequeryDisabled = false;
369     /// <summary>
370     /// 
371     /// </summary>
372     private List<WeakReference> _canExecuteChangedHandlers;
373 
374     #endregion
375   }
376 
377   /// <summary>
378   ///     This class contains methods for the CommandManager that help avoid memory leaks by
379   ///     using weak references.
380   /// </summary>
381   internal class CommandManagerHelper
382   {
383     internal static void CallWeakReferenceHandlers(List<WeakReference> handlers)
384     {
385       if (handlers != null)
386       {
387         // Take a snapshot of the handlers before we call out to them since the handlers
388         // could cause the array to me modified while we are reading it.
389 
390         EventHandler[] callees = new EventHandler[handlers.Count];
391         int count = 0;
392 
393         for (int i = handlers.Count - 1; i >= 0; i--)
394         {
395           WeakReference reference = handlers[i];
396           EventHandler handler = reference.Target as EventHandler;
397           if (handler == null)
398           {
399             // Clean up old handlers that have been collected
400             handlers.RemoveAt(i);
401           }
402           else
403           {
404             callees[count] = handler;
405             count++;
406           }
407         }
408 
409         // Call the handlers that we snapshotted
410         for (int i = 0; i < count; i++)
411         {
412           EventHandler handler = callees[i];
413           handler(null, EventArgs.Empty);
414         }
415       }
416     }
417 
418     /// <summary>
419     /// 
420     /// </summary>
421     /// <param name="handlers"></param>
422     internal static void AddHandlersToRequerySuggested(List<WeakReference> handlers)
423     {
424       if (handlers != null)
425       {
426         foreach (WeakReference handlerRef in handlers)
427         {
428           EventHandler handler = handlerRef.Target as EventHandler;
429           if (handler != null)
430           {
431             CommandManager.RequerySuggested += handler;
432           }
433         }
434       }
435     }
436 
437     /// <summary>
438     /// 
439     /// </summary>
440     /// <param name="handlers"></param>
441     internal static void RemoveHandlersFromRequerySuggested(List<WeakReference> handlers)
442     {
443       if (handlers != null)
444       {
445         foreach (WeakReference handlerRef in handlers)
446         {
447           EventHandler handler = handlerRef.Target as EventHandler;
448           if (handler != null)
449           {
450             CommandManager.RequerySuggested -= handler;
451           }
452         }
453       }
454     }
455 
456     /// <summary>
457     /// 
458     /// </summary>
459     /// <param name="handlers"></param>
460     /// <param name="handler"></param>
461     internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler)
462     {
463       AddWeakReferenceHandler(ref handlers, handler, -1);
464     }
465 
466     /// <summary>
467     /// 
468     /// </summary>
469     /// <param name="handlers"></param>
470     /// <param name="handler"></param>
471     /// <param name="defaultListSize"></param>
472     internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler, int defaultListSize)
473     {
474       if (handlers == null)
475       {
476         handlers = (defaultListSize > 0 ? new List<WeakReference>(defaultListSize) : new List<WeakReference>());
477       }
478 
479       handlers.Add(new WeakReference(handler));
480     }
481 
482     /// <summary>
483     /// 
484     /// </summary>
485     /// <param name="handlers"></param>
486     /// <param name="handler"></param>
487     internal static void RemoveWeakReferenceHandler(List<WeakReference> handlers, EventHandler handler)
488     {
489       if (handlers != null)
490       {
491         for (int i = handlers.Count - 1; i >= 0; i--)
492         {
493           WeakReference reference = handlers[i];
494           EventHandler existingHandler = reference.Target as EventHandler;
495           if ((existingHandler == null) || (existingHandler == handler))
496           {
497             // Clean up old handlers that have been collected
498             // in addition to the handler that is to be removed.
499             handlers.RemoveAt(i);
500           }
501         }
502       }
503     }
504   }
505 }
1 <Button Width="60"
2             Height="30"
3             DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
4             Command="{Binding TestCommand}"
5             CommandParameter="123">Test</Button>

 

posted @ 2018-03-15 02:07  红枫一叶  阅读(340)  评论(0编辑  收藏  举报