PageRenderTime 26ms CodeModel.GetById 14ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

/V2.2/trunk/CAL/Desktop/Composite.Presentation/Commands/CompositeCommand.cs

#
C# | 258 lines | 148 code | 28 blank | 82 comment | 16 complexity | 549fe08e803314a8f07e7acef921f33c MD5 | raw file
  1//===================================================================================
  2// Microsoft patterns & practices
  3// Composite Application Guidance for Windows Presentation Foundation and Silverlight
  4//===================================================================================
  5// Copyright (c) Microsoft Corporation.  All rights reserved.
  6// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
  7// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
  8// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  9// FITNESS FOR A PARTICULAR PURPOSE.
 10//===================================================================================
 11// The example companies, organizations, products, domain names,
 12// e-mail addresses, logos, people, places, and events depicted
 13// herein are fictitious.  No association with any real company,
 14// organization, product, domain name, email address, logo, person,
 15// places, or events is intended or should be inferred.
 16//===================================================================================
 17using System;
 18using System.Collections.Generic;
 19using System.Linq;
 20using System.Windows.Input;
 21
 22using Microsoft.Practices.Composite.Presentation.Properties;
 23
 24namespace Microsoft.Practices.Composite.Presentation.Commands
 25{
 26    /// <summary>
 27    /// The CompositeCommand composites one or more ICommands.
 28    /// </summary>
 29    public partial class CompositeCommand : ICommand
 30    {
 31        private readonly List<ICommand> registeredCommands = new List<ICommand>();
 32        private List<WeakReference> _canExecuteChangedHandlers;
 33        private readonly bool monitorCommandActivity;
 34        private readonly EventHandler onRegisteredCommandCanExecuteChangedHandler;
 35
 36        /// <summary>
 37        /// Initializes a new instance of <see cref="CompositeCommand"/>.
 38        /// </summary>
 39        public CompositeCommand()
 40        {
 41            this.onRegisteredCommandCanExecuteChangedHandler = new EventHandler(this.OnRegisteredCommandCanExecuteChanged);
 42        }
 43
 44        /// <summary>
 45        /// Initializes a new instance of <see cref="CompositeCommand"/>.
 46        /// </summary>
 47        /// <param name="monitorCommandActivity">Indicates when the command activity is going to be monitored.</param>
 48        public CompositeCommand(bool monitorCommandActivity)
 49            : this()
 50        {
 51            this.monitorCommandActivity = monitorCommandActivity;
 52        }
 53
 54        /// <summary>
 55        /// Adds a command to the collection and signs up for the <see cref="ICommand.CanExecuteChanged"/> event of it.
 56        /// </summary>
 57        ///  <remarks>
 58        /// If this command is set to monitor command activity, and <paramref name="command"/> 
 59        /// implements the <see cref="IActiveAware"/> interface, this method will subscribe to its
 60        /// <see cref="IActiveAware.IsActiveChanged"/> event.
 61        /// </remarks>
 62        /// <param name="command">The command to register.</param>
 63        public virtual void RegisterCommand(ICommand command)
 64        {
 65            if (command == this)
 66            {
 67                throw new ArgumentException(Resources.CannotRegisterCompositeCommandInItself);
 68            }
 69
 70            lock (this.registeredCommands)
 71            {
 72                if (this.registeredCommands.Contains(command))
 73                {
 74                    throw new InvalidOperationException(Resources.CannotRegisterSameCommandTwice);
 75                }
 76                this.registeredCommands.Add(command);
 77            }
 78
 79            command.CanExecuteChanged += this.onRegisteredCommandCanExecuteChangedHandler;
 80            this.OnCanExecuteChanged();
 81
 82            if (this.monitorCommandActivity)
 83            {
 84                var activeAwareCommand = command as IActiveAware;
 85                if (activeAwareCommand != null)
 86                {
 87                    activeAwareCommand.IsActiveChanged += this.Command_IsActiveChanged;
 88                }
 89            }
 90        }
 91
 92        /// <summary>
 93        /// Removes a command from the collection and removes itself from the <see cref="ICommand.CanExecuteChanged"/> event of it.
 94        /// </summary>
 95        /// <param name="command">The command to unregister.</param>
 96        public virtual void UnregisterCommand(ICommand command)
 97        {
 98            bool removed;
 99            lock (this.registeredCommands)
100            {
101                removed = this.registeredCommands.Remove(command);
102            }
103
104            if (removed)
105            {
106                command.CanExecuteChanged -= this.onRegisteredCommandCanExecuteChangedHandler;
107                this.OnCanExecuteChanged();
108
109                if (this.monitorCommandActivity)
110                {
111                    var activeAwareCommand = command as IActiveAware;
112                    if (activeAwareCommand != null)
113                    {
114                        activeAwareCommand.IsActiveChanged -= this.Command_IsActiveChanged;
115                    }
116                }
117            }
118        }
119
120        private void OnRegisteredCommandCanExecuteChanged(object sender, EventArgs e)
121        {
122            this.OnCanExecuteChanged();
123        }
124
125        /// <summary>
126        /// Forwards <see cref="ICommand.CanExecute"/> to the registered commands and returns
127        /// <see langword="true" /> if all of the commands return <see langword="true" />.
128        /// </summary>
129        /// <param name="parameter">Data used by the command.
130        /// If the command does not require data to be passed, this object can be set to <see langword="null" />.
131        /// </param>
132        /// <returns><see langword="true" /> if all of the commands return <see langword="true" />; otherwise, <see langword="false" />.</returns>
133        public virtual bool CanExecute(object parameter)
134        {
135            bool hasEnabledCommandsThatShouldBeExecuted = false;
136
137            ICommand[] commandList;
138            lock (this.registeredCommands)
139            {
140                commandList = this.registeredCommands.ToArray();
141            }
142            foreach (ICommand command in commandList)
143            {
144                if (this.ShouldExecute(command))
145                {
146                    if (!command.CanExecute(parameter))
147                    {
148                        return false;
149                    }
150
151                    hasEnabledCommandsThatShouldBeExecuted = true;
152                }
153            }
154
155            return hasEnabledCommandsThatShouldBeExecuted;
156        }
157
158
159        /// <summary>
160        /// Occurs when any of the registered commands raise <seealso cref="CanExecuteChanged"/>.
161        /// </summary>
162        public event EventHandler CanExecuteChanged
163        {
164            add
165            {
166                WeakEventHandlerManager.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
167            }
168            remove
169            {
170                WeakEventHandlerManager.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
171            }
172        }
173
174        /// <summary>
175        /// Forwards <see cref="ICommand.Execute"/> to the registered commands.
176        /// </summary>
177        /// <param name="parameter">Data used by the command.
178        /// If the command does not require data to be passed, this object can be set to <see langword="null" />.
179        /// </param>
180        public virtual void Execute(object parameter)
181        {
182            Queue<ICommand> commands;
183            lock (this.registeredCommands)
184            {
185                commands = new Queue<ICommand>(this.registeredCommands.Where(this.ShouldExecute).ToList());
186            }
187
188            while (commands.Count > 0)
189            {
190                ICommand command = commands.Dequeue();
191                command.Execute(parameter);
192            }
193        }
194
195        /// <summary>
196        /// Evaluates if a command should execute.
197        /// </summary>
198        /// <param name="command">The command to evaluate.</param>
199        /// <returns>A <see cref="bool"/> value indicating whether the command should be used 
200        /// when evaluating <see cref="CompositeCommand.CanExecute"/> and <see cref="CompositeCommand.Execute"/>.</returns>
201        /// <remarks>
202        /// If this command is set to monitor command activity, and <paramref name="command"/>
203        /// implements the <see cref="IActiveAware"/> interface, 
204        /// this method will return <see langword="false" /> if the command's <see cref="IActiveAware.IsActive"/> 
205        /// property is <see langword="false" />; otherwise it always returns <see langword="true" />.</remarks>
206        protected virtual bool ShouldExecute(ICommand command)
207        {
208            var activeAwareCommand = command as IActiveAware;
209
210            if (this.monitorCommandActivity && activeAwareCommand != null)
211            {
212                return activeAwareCommand.IsActive;
213            }
214
215            return true;
216        }
217
218        /// <summary>
219        /// Gets the list of all the registered commands.
220        /// </summary>
221        /// <value>A list of registered commands.</value>
222        /// <remarks>This returns a copy of the commands subscribed to the CompositeCommand.</remarks>
223        public IList<ICommand> RegisteredCommands
224        {
225            get
226            {
227                IList<ICommand> commandList;
228                lock (this.registeredCommands)
229                {
230                    commandList = this.registeredCommands.ToList();
231                }
232
233                return commandList;
234            }
235        }
236
237        /// <summary>
238        /// Raises <see cref="ICommand.CanExecuteChanged"/> on the UI thread so every 
239        /// command invoker can requery <see cref="ICommand.CanExecute"/> to check if the
240        /// <see cref="CompositeCommand"/> can execute.
241        /// </summary>
242        protected virtual void OnCanExecuteChanged()
243        {
244            WeakEventHandlerManager.CallWeakReferenceHandlers(this, _canExecuteChangedHandlers);
245        }
246
247
248        /// <summary>
249        /// Handler for IsActiveChanged events of registered commands.
250        /// </summary>
251        /// <param name="sender">The sender.</param>
252        /// <param name="e">EventArgs to pass to the event.</param>
253        private void Command_IsActiveChanged(object sender, EventArgs e)
254        {
255            this.OnCanExecuteChanged();
256        }
257    }
258}