Skip to content

Instantly share code, notes, and snippets.

@jgable
Created March 23, 2011 19:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jgable/883810 to your computer and use it in GitHub Desktop.
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
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&lt;TParmType&gt;"/> 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&lt;TParmType&gt;"/> 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&lt;TParmType&gt;"/> 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