ViewにCommandを持つのかViewModelにCommandを持つのかの違いだけど、個人的な趣味では、ViewにCommandを持つ、この方法が気に入ってきた。今日ひらめいた。
下のようなFrameworkElementを継承したICommandインターフェースを実装したクラスを定義します。
using System; using System.Collections.Generic; using System.Windows; using System.Windows.Input; namespace CommandHolderTest { public class CommandHolder : FrameworkElement, ICommand { #region ExecuteActionプロパティ public Action ExecuteAction { get { return (Action)GetValue(ExecuteActionProperty); } set { SetValue(ExecuteActionProperty, value); } } public static readonly DependencyProperty ExecuteActionProperty = DependencyProperty.Register( "ExecuteAction", typeof(Action), typeof(CommandHolder), new PropertyMetadata(null)); #endregion #region ExecuteActionsプロパティ public IEnumerable<Action> ExecuteActions { get { return (IEnumerable<Action>)GetValue(ExecuteActionsProperty); } set { SetValue(ExecuteActionsProperty, value); } } // Using a DependencyProperty as the backing store for ExecuteActions. This enables animation, styling, binding, etc... public static readonly DependencyProperty ExecuteActionsProperty = DependencyProperty.Register( "ExecuteActions", typeof(IEnumerable<Action>), typeof(CommandHolder), new PropertyMetadata(null)); #endregion public void Execute() { // 何か設定されてたら実行する if (ExecuteAction != null) { ExecuteAction(); } if (ExecuteActions != null) { foreach (var action in ExecuteActions) { action(); } } } #region ICommand Members bool ICommand.CanExecute(object parameter) { // とりあえず常に実行可能 return true; } private EventHandler _canExecuteChanged; event EventHandler ICommand.CanExecuteChanged { add { _canExecuteChanged += value; } remove { _canExecuteChanged -= value; } } void ICommand.Execute(object parameter) { Execute(); } #endregion } }
Commandとして実行する処理をAction型のプロパティで受け取るシンプルなクラスです。こいつをViewに置くようにすると、ViewModelではCommandじゃなくてAction型を公開するだけでOKになります。
多少コードがすっきりします。
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.ComponentModel; namespace CommandHolderTest { public class MainPageViewModel : INotifyPropertyChanged { #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion private string _message; public string Message { get { return _message; } set { _message = value; OnPropertyChanged("Message"); } } // Actionを公開する public Action GreetAction { get { return GreetExecute; } } public void GreetExecute() { Message = "こんにちは@" + DateTime.Now; } } }
XAMLでは、CommandHolderを置いて、ExecuteActionプロパティとGreetActionプロパティをバインドで結び付けます。
<UserControl x:Class="CommandHolderTest.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:CommandHolderTest" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" xmlns:my="clr-namespace:CommandHolderTest"> <UserControl.DataContext> <local:MainPageViewModel /> </UserControl.DataContext> <Grid x:Name="LayoutRoot" Background="White"> <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="button1" VerticalAlignment="Top" Width="75" Command="{Binding ElementName=commandHolder1}" /> <my:CommandHolder x:Name="commandHolder1" ExecuteAction="{Binding Path=GreetAction}" /> <TextBlock HorizontalAlignment="Left" Margin="93,16,0,0" Name="textBlock1" Text="{Binding Path=Message}" VerticalAlignment="Top" /> </Grid> </UserControl>
ButtonのCommandプロパティには、CommandHolderをバインドします。
この方法のもう1つの利点は、Loadedイベントのように、CommandがサポートされてないCommandを発行してくれないイベントでは、イベントハンドラでCommandHolderのExecuteメソッドを呼び出してやればOKという点です。
ViewModelを意識せずにアクションを実行できる。
commandHolder1.Execute();
個人的には、DelegateCommandより好みなのだけど、どうだろう?