かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

いつも書いてるViewModelBaseとDelegateCommandクラス

WPFHello world書いたり、ちょっと実験したりというときに毎回書いてるのも馬鹿らしいので、ここにメモ。
Visual Studioのテンプレートに登録してしまうのが楽そうだ。

ViewModelBase

public abstract class ViewModelBase : INotifyPropertyChanged
{
    protected ViewModelBase()
    {
    }

    public event PropertyChangedEventHandler PropertyChanged;
    /// <summary>
    /// Raise PropertyChanged Event.
    /// </summary>
    /// <param name="names"></param>
    protected virtual void OnPropertyChanged(params string[] names)
    {
        var h = PropertyChanged;
        if (h == null) return;

        CheckPropertyName(names);

        foreach (var name in names)
        {
            h(this, new PropertyChangedEventArgs(name));
        }
    }
    [Conditional("DEBUG")]
    private void CheckPropertyName(params string[] names)
    {
        var props = GetType().GetProperties();
        foreach (var name in names)
        {
            var prop = props.Where(p => p.Name == name).SingleOrDefault();
            if (prop == null) throw new ArgumentException(name);
        }
    }
    protected void OnPropertyChanged<T>(params Expression<Func<T>>[] propertyExpression)
    {
        OnPropertyChanged(
            propertyExpression.Select(ex => ((MemberExpression)ex.Body).Member.Name).ToArray());
    }
}

DelegateCommand

#region No parameter DelegateCommand
public sealed class DelegateCommand : ICommand
{
    private Action _execute;
    private Func<bool> _canExecute;

    public DelegateCommand(Action execute) : this(execute, () => true)
    {
    }
    public DelegateCommand(Action execute, Func<bool> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute()
    {
        return _canExecute();
    }

    public void Execute()
    {
        _execute();
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    #region ICommand
    bool ICommand.CanExecute(object parameter)
    {
        return CanExecute();
    }
    void ICommand.Execute(object parameter)
    {
        Execute();
    }
    #endregion
}
#endregion

#region Parameter DelegateCommand
public sealed class DelegateCommand<T> : ICommand
{
    private Action<T> _execute;
    private Func<T, bool> _canExecute;

    private static readonly bool IS_VALUE_TYPE;

    static DelegateCommand()
    {
        IS_VALUE_TYPE = typeof(T).IsValueType;
    }


    public DelegateCommand(Action<T> execute) : this(execute, o => true)
    {
    }

    public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(T parameter)
    {
        return _canExecute(parameter);
    }

    public void Execute(T parameter)
    {
        _execute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    #region ICommand
    bool ICommand.CanExecute(object parameter)
    {
        return CanExecute(Cast(parameter));
    }

    void ICommand.Execute(object parameter)
    {
        Execute(Cast(parameter));
    }
    #endregion

    /// <summary>
    /// convert parameter value
    /// </summary>
    /// <param name="parameter"></param>
    /// <returns></returns>
    private T Cast(object parameter)
    {
        if (parameter == null && IS_VALUE_TYPE)
        {
            return default(T);
        }
        return (T)parameter;
    }
}

#endregion

多分・・・間違ってないはず。
Visual Studio 2008の時のコードテンプレートへの追加の仕方は、前に書いたけど、Visual Studio 2010ではどうやるんだろう??同じところにメニュー項目がないorz