Created
March 23, 2011 19:47
-
-
Save jgable/883810 to your computer and use it in GitHub Desktop.
MVVMCommon contains a VM Base and Commanding Helpers for Windows Phone 7 or Silverlight 3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.ComponentModel; | |
using System.Reflection; | |
using System.Windows; | |
using System.Windows.Input; | |
/// <summary> | |
/// Base class for Viewmodels providing common INotifyPropertyChanged functionality. | |
/// </summary> | |
public abstract class ViewModelBase : INotifyPropertyChanged | |
{ | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ViewModelBase"/> class. | |
/// </summary> | |
public ViewModelBase() | |
{ } | |
#region INotifyPropertyChanged Members | |
/// <summary> | |
/// Occurs when a property value changes. | |
/// </summary> | |
public event PropertyChangedEventHandler PropertyChanged; | |
/// <summary> | |
/// Called when [property changed]. | |
/// </summary> | |
/// <param name="propName">Name of the prop.</param> | |
protected void OnPropertyChanged(string propName) | |
{ | |
if (PropertyChanged != null) | |
PropertyChanged(this, new PropertyChangedEventArgs(propName)); | |
} | |
#endregion | |
} | |
/// | |
/// This code taken from the Cinch MVVM Framework, cinch.codeplex.com. | |
/// | |
#region SCommandArgs Class | |
/// <summary> | |
/// Allows a CommandParameter to be associated with a SingleEventCommand | |
/// </summary> | |
public class SCommandArgs | |
{ | |
#region Data | |
public object Sender { get; set; } | |
public object EventArgs { get; set; } | |
public object CommandParameter { get; set; } | |
#endregion | |
#region Ctor | |
public SCommandArgs() | |
{ | |
} | |
public SCommandArgs(object sender, object eventArgs, object commandParameter) | |
{ | |
Sender = sender; | |
EventArgs = eventArgs; | |
CommandParameter = commandParameter; | |
} | |
#endregion | |
} | |
#endregion | |
#region SingleEventCommand Class | |
/// <summary> | |
/// This class allows a single command to event mappings. | |
/// It is used to wire up View events to a | |
/// ViewModel ICommand implementation. | |
/// </summary> | |
/// <example> | |
/// <![CDATA[ | |
/// | |
/// <ListBox ... | |
/// Cinch:SingleEventCommand.RoutedEventName="SelectionChanged" | |
/// Cinch:SingleEventCommand.TheCommandToRun="{Binding Path=BoxEditCommand}" | |
/// Cinch:SingleEventCommand.CommandParameter="{Binding ElementName=ListBoxVehicle, Path=SelectedItem}"> | |
/// </ListBox> | |
/// | |
/// ]]> | |
/// </example> | |
public static class SingleEventCommand | |
{ | |
#region TheCommandToRun | |
/// <summary> | |
/// TheCommandToRun : The actual ICommand to run | |
/// </summary> | |
public static readonly DependencyProperty TheCommandToRunProperty = | |
DependencyProperty.RegisterAttached("TheCommandToRun", | |
typeof(ICommand), | |
typeof(SingleEventCommand), | |
new PropertyMetadata((ICommand)null)); | |
/// <summary> | |
/// Gets the TheCommandToRun property. | |
/// </summary> | |
public static ICommand GetTheCommandToRun(DependencyObject d) | |
{ | |
return (ICommand)d.GetValue(TheCommandToRunProperty); | |
} | |
/// <summary> | |
/// Sets the TheCommandToRun property. | |
/// </summary> | |
public static void SetTheCommandToRun(DependencyObject d, ICommand value) | |
{ | |
d.SetValue(TheCommandToRunProperty, value); | |
} | |
#endregion | |
#region RoutedEventName | |
/// <summary> | |
/// RoutedEventName : The event that should actually execute the | |
/// ICommand | |
/// </summary> | |
public static readonly DependencyProperty RoutedEventNameProperty = | |
DependencyProperty.RegisterAttached("RoutedEventName", typeof(String), | |
typeof(SingleEventCommand), | |
new PropertyMetadata((String)String.Empty, | |
new PropertyChangedCallback(OnRoutedEventNameChanged))); | |
/// <summary> | |
/// Gets the RoutedEventName property. | |
/// </summary> | |
public static String GetRoutedEventName(DependencyObject d) | |
{ | |
return (String)d.GetValue(RoutedEventNameProperty); | |
} | |
/// <summary> | |
/// Sets the RoutedEventName property. | |
/// </summary> | |
public static void SetRoutedEventName(DependencyObject d, String value) | |
{ | |
d.SetValue(RoutedEventNameProperty, value); | |
} | |
/// <summary> | |
/// Hooks up a Dynamically created EventHandler (by using the | |
/// <see cref="EventHooker">EventHooker</see> class) that when | |
/// run will run the associated ICommand | |
/// </summary> | |
private static void OnRoutedEventNameChanged(DependencyObject d, | |
DependencyPropertyChangedEventArgs e) | |
{ | |
String routedEvent = (String)e.NewValue; | |
if (d == null || String.IsNullOrEmpty(routedEvent)) | |
return; | |
//If the RoutedEvent string is not null, create a new | |
//dynamically created EventHandler that when run will execute | |
//the actual bound ICommand instance (usually in the ViewModel) | |
EventHooker eventHooker = new EventHooker(); | |
eventHooker.ObjectWithAttachedCommand = d; | |
EventInfo eventInfo = d.GetType().GetEvent(routedEvent, | |
BindingFlags.Public | BindingFlags.Instance); | |
//Hook up Dynamically created event handler | |
if (eventInfo != null) | |
{ | |
eventInfo.RemoveEventHandler(d, | |
eventHooker.GetNewEventHandlerToRunCommand(eventInfo)); | |
eventInfo.AddEventHandler(d, | |
eventHooker.GetNewEventHandlerToRunCommand(eventInfo)); | |
} | |
} | |
#endregion | |
#region CommandParameter | |
public static readonly DependencyProperty CommandParameterProperty = | |
DependencyProperty.RegisterAttached("CommandParameter", typeof(object), | |
typeof(SingleEventCommand), new PropertyMetadata(null)); | |
/// <summary> | |
/// Gets the CommandParameter property. | |
/// </summary> | |
public static object GetCommandParameter(DependencyObject obj) | |
{ | |
return (object)obj.GetValue(CommandParameterProperty); | |
} | |
/// <summary> | |
/// Sets the CommandParameter property. | |
/// </summary> | |
public static void SetCommandParameter(DependencyObject obj, object value) | |
{ | |
obj.SetValue(CommandParameterProperty, value); | |
} | |
#endregion | |
} | |
#endregion | |
#region EventHooker Class | |
/// <summary> | |
/// Contains the event that is hooked into the source RoutedEvent | |
/// that was specified to run the ICommand | |
/// </summary> | |
sealed class EventHooker | |
{ | |
#region Public Methods/Properties | |
/// <summary> | |
/// The DependencyObject, that holds a binding to the actual | |
/// ICommand to execute | |
/// </summary> | |
public DependencyObject ObjectWithAttachedCommand { get; set; } | |
/// <summary> | |
/// Creates a Dynamic EventHandler that will be run the ICommand | |
/// when the user specified RoutedEvent fires | |
/// </summary> | |
/// <param name="eventInfo">The specified RoutedEvent EventInfo</param> | |
/// <returns>An Delegate that points to a new EventHandler | |
/// that will be run the ICommand</returns> | |
public Delegate GetNewEventHandlerToRunCommand(EventInfo eventInfo) | |
{ | |
Delegate del = null; | |
if (eventInfo == null) | |
throw new ArgumentNullException("eventInfo"); | |
if (eventInfo.EventHandlerType == null) | |
throw new ArgumentException("EventHandlerType is null"); | |
if (del == null) | |
del = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, | |
GetType().GetMethod("OnEventRaised", | |
BindingFlags.NonPublic | | |
BindingFlags.Instance)); | |
return del; | |
} | |
#endregion | |
#region Private Methods | |
/// <summary> | |
/// Runs the ICommand when the requested RoutedEvent fires | |
/// </summary> | |
private void OnEventRaised(object sender, EventArgs e) | |
{ | |
ICommand command = (ICommand)(sender as DependencyObject). | |
GetValue(SingleEventCommand.TheCommandToRunProperty); | |
object commandParameter = (sender as DependencyObject). | |
GetValue(SingleEventCommand.CommandParameterProperty); | |
SCommandArgs commandArgs = new SCommandArgs(sender, e, commandParameter); | |
if (command != null) | |
command.Execute(commandArgs); | |
} | |
#endregion | |
} | |
#endregion | |
/// <summary> | |
/// The concept for this class comes from PRISM (Composite Application Guidance) | |
/// </summary> | |
/// <typeparam name="TParmType">The type of the Parameter to use for the command</typeparam> | |
public class DelegateCommand<TParmType> : ICommand | |
{ | |
/// <summary> | |
/// Holder for the command. | |
/// </summary> | |
private Action<TParmType> _command; | |
/// <summary> | |
/// Holder for the predicate to determine if the command can execute. | |
/// </summary> | |
private Predicate<TParmType> _canExecute; | |
/// <summary> | |
/// Holder for whether the command tracks its own execution. | |
/// </summary> | |
private bool _tracksExecuted; | |
/// <summary> | |
/// for testing that a command ran. | |
/// </summary> | |
/// <value><c>true</c> if [command executed]; otherwise, <c>false</c>.</value> | |
public bool CommandExecuted { get; set; } | |
/// <summary> | |
/// Initializes a new instance of the <see cref="DelegateCommand<TParmType>"/> class. | |
/// </summary> | |
/// <param name="command">The command.</param> | |
/// <param name="tracksExecuted">if set to <c>true</c> then the command will track whether it was executed on it's own.</param> | |
public DelegateCommand(Action<TParmType> command, bool tracksExecuted) | |
: this(command) | |
{ | |
_tracksExecuted = tracksExecuted; | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="DelegateCommand<TParmType>"/> class. | |
/// </summary> | |
/// <param name="command">The command.</param> | |
/// <param name="canExecute">A predicate to determine if the command can execute..</param> | |
public DelegateCommand(Action<TParmType> command, Predicate<TParmType> canExecute) | |
: this(command, canExecute, false) | |
{ | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="DelegateCommand<TParmType>"/> class. | |
/// </summary> | |
/// <param name="command">The command.</param> | |
public DelegateCommand(Action<TParmType> command) | |
: this(command, (x) => true, false) | |
{ | |
} | |
/// <summary> | |
/// Creates a new DelegateCommand with the specified values. | |
/// </summary> | |
/// <param name="command">The logic to execute.</param> | |
/// <param name="canExecute">The logic to determine if a command can execute.</param> | |
/// <param name="tracksExecuted">A flag that tells the command not to automatically set CommandExecuted to true.</param> | |
public DelegateCommand(Action<TParmType> command, Predicate<TParmType> canExecute, bool tracksExecuted) | |
{ | |
_command = command; | |
_canExecute = canExecute; | |
_tracksExecuted = tracksExecuted; | |
} | |
#region ICommand Members | |
/// <summary> | |
/// Defines the method that determines whether the command can execute in its current state. | |
/// </summary> | |
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param> | |
/// <returns> | |
/// true if this command can be executed; otherwise, false. | |
/// </returns> | |
public bool CanExecute(object parameter) | |
{ | |
return _canExecute.Invoke((TParmType)parameter); | |
} | |
/// <summary> | |
/// Occurs when changes occur that affect whether or not the command should execute. | |
/// </summary> | |
public event EventHandler CanExecuteChanged; | |
/// <summary> | |
/// Does the can execute changed. | |
/// </summary> | |
protected void DoCanExecuteChanged() | |
{ | |
if (CanExecuteChanged != null) | |
{ | |
CanExecuteChanged(this, EventArgs.Empty); | |
} | |
} | |
/// <summary> | |
/// Defines the method to be called when the command is invoked. | |
/// </summary> | |
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param> | |
public void Execute(object parameter) | |
{ | |
this.CommandExecuted = false; | |
if (CanExecute(parameter)) | |
{ | |
_command.Invoke((TParmType)parameter); | |
if (!_tracksExecuted) | |
this.CommandExecuted = true; | |
} | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment