Open rainit2006 opened 7 years ago
自定义命令 我们有这么几种方式来自定义命令: 一是直接实现ICommand接口,这是最彻底的方式; 二是继承自RoutedCommand类和RoutedUICommand类,这种方式可以命令路由; 三是使用RoutedCommand类和RoutedUICommand类实例,严格来讲,这种方式只是命令的应用。
cs代码(定义command的代码):
public class SayCommand:ICommand
xaml文件里:
<Window x:Class="CommandDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CommandDemo"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<local:SayCommand x:Key="cmd" />
</Window.Resources>
<Grid>
<StackPanel>
<Button Content="Say" Command="{StaticResource ResourceKey=cmd}" CommandParameter="Hi,WPF"/>
<Button Content="AddCommandHandler" Click="Button_Click" />
</StackPanel>
</Grid>
</Window>
cs代码(对应xaml的cs代码):
private void Button_Click(object sender, RoutedEventArgs e)
{
SayCommand sayCmd = this.FindResource("cmd") as SayCommand;
if(sayCmd == null)return;
sayCmd.CanExecuteTargets += () =>
{
return true;
};
sayCmd.ExecutedTargets += (p) =>
{
MessageBox.Show(p);
};
}
WPF中提供了一组已定义命令,命令包括以下类:ApplicationCommands、NavigationCommands、MediaCommands、EditingCommands 以及ComponentCommands。 这些类提供诸如 Cut、BrowseBack、BrowseForward、Play、Stop 和 Pause 等命令。
ApplicationCommands: 提供一组与应用程序相关的标准命令。 https://msdn.microsoft.com/zh-cn/library/system.windows.input.applicationcommands(v=vs.110).aspx
WPF命令是路由的: 实际上,RoutedCommand上Execute和CanExecute方法并没有包含命令的处理逻辑,而是将触发遍历元素树的事件来查找具有CommandBinding的对象。而真正命令的处理程序包含在CommandBinding的事件处理程序中。 http://www.cnblogs.com/zhili/p/WPFCommand.html
下述代码里点击button执行的都是在CommandBinding里指定的Executed方法,即NewCommand函数。 xaml
<!--定义窗口命令绑定,绑定的命令是New命令,处理程序是NewCommand, -->
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="NewCommand"/>
</Window.CommandBindings>
<!--使用数据绑定,获得正在使用的Command对象,并提取其Text属性-->
<Button Margin="5" Padding="5" Command="ApplicationCommands.New" Content="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}"/>
<Button Margin="5" Padding="5" Visibility="Visible" Click="cmdDoCommand_Click" >DoCommand</Button>
</StackPanel>
cs
private void NewCommand(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("New 命令被触发了,命令源是:" + e.Source.ToString());
}
private void cmdDoCommand_Click(object sender, RoutedEventArgs e)
{
// 直接调用命令的两种方式
ApplicationCommands.New.Execute(null, (Button)sender);
//this.CommandBindings[0].Command.Execute(null);
}
自定义命令: xaml里没有明确的command binding声明
<Button CommandParameter="Teacher" x:Name="button1" Content="New Teacher" Grid.Row ="1"/>
<Button CommandParameter="Student" x:Name="button2" Content="New Student" Grid.Row ="2"/>
在cs里定义command,并binding到目标按钮里
namespace MyCommand
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//
InitCmd();
}
//声明并定义命令
private RoutedCommand newCmd=new RoutedCommand ("NewTS",typeof(MainWindow ));
private void InitCmd()
{
//创建命令关联
CommandBinding cb = new CommandBinding();
cb.Command = newCmd;
cb.CanExecute += new CanExecuteRoutedEventHandler(CanExecute1);
cb.Executed += new ExecutedRoutedEventHandler(Executed1);
//把命令关联安置在外围控件上
mainWindow.CommandBindings.Add(cb);
//把命令赋值给命令源(发送者)
button1.Command = newCmd;
button2.Command = newCmd;
}
private void CanExecute1(object sender,CanExecuteRoutedEventArgs e)
{
//e.CanExecute = true;
if (string.IsNullOrWhiteSpace(txtName.Text))
{
e.CanExecute = false;
}
else
{
e.CanExecute = true;
}
}
private void Executed1(object sender, ExecutedRoutedEventArgs e)
{
string name = txtName.Text;
if (e.Parameter.ToString() == "Teacher")
{
listBox1.Items.Add("New Teacher: " + name);
}
if (e.Parameter.ToString() == "Student")
{
listBox1.Items.Add("New Student: " + name);
}
}
}
}
对于Button来说,直接用click事件不好吗?
まぁ、言われてみればボタンのClickイベントに直接処理を記述した場合、 同じ処理を別のボタンから呼び出すときに、 一旦処理をメソッドに括りだして、 両方のボタンからそのメソッドを呼ぶ。 とかやるわけですから、「最初から分けとけばいいじゃん」的な発想にはそこそこ納得できます。
XAMLからViewModelのメソッドにバインドする~RelayCommand~ http://sourcechord.hatenablog.com/entry/2014/01/13/200039
MainWindow.xaml
<Window x:Class="RelayCommandTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="300"
Height="200">
<Grid>
<TextBlock Margin="10,10,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Text="{Binding Message}"
TextWrapping="Wrap" />
<Button Width="75"
Margin="10,30,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Command="{Binding ChangeMessageCommand}"
Content="Button" />
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
MainWindowViewModel.cs
class MainWindowViewModel : BindableBase
{
private string message;
public string Message
{
get { return message; }
set { this.SetProperty(ref this.message, value); }
}
#region コマンドの実装
private RelayCommand changeMessageCommand;
public RelayCommand ChangeMessageCommand
{
get { return changeMessageCommand = changeMessageCommand ?? new RelayCommand(ChangeMessage); }
}
private void ChangeMessage()
{
this.Message = "コマンドを実行しました。";
}
#endregion
public MainWindowViewModel()
{
this.Message = "初期値です。";
}
}
RelayCommand.cs 通常のRelayCommandの後に、RelayCommandクラスを追加しました。
/// <summary>
/// その機能を中継することのみを目的とするコマンド
/// デリゲートを呼び出すことにより、他のオブジェクトに対して呼び出します。
///CanExecute メソッドの既定の戻り値は 'true' です。
/// <see cref="RaiseCanExecuteChanged"/> は、次の場合は必ず呼び出す必要があります。
/// <see cref="CanExecute"/> は、別の値を返すことが予期されます。
/// </summary>
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
/// <summary>
/// RaiseCanExecuteChanged が呼び出されたときに生成されます。
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// 常に実行可能な新しいコマンドを作成します。
/// </summary>
/// <param name="execute">実行ロジック。</param>
public RelayCommand(Action execute)
: this(execute, null)
{
}
/// <summary>
/// 新しいコマンドを作成します。
/// </summary>
/// <param name="execute">実行ロジック。</param>
/// <param name="canExecute">実行ステータス ロジック。</param>
public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
/// <summary>
/// 現在の状態でこの <see cref="RelayCommand"/> が実行できるかどうかを判定します。
/// </summary>
/// <param name="parameter">
/// コマンドによって使用されるデータ。コマンドが、データの引き渡しを必要としない場合、このオブジェクトを null に設定できます。
/// </param>
/// <returns>このコマンドが実行可能な場合は true、それ以外の場合は false。</returns>
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
/// <summary>
/// 現在のコマンド ターゲットに対して <see cref="RelayCommand"/> を実行します。
/// </summary>
/// <param name="parameter">
/// コマンドによって使用されるデータ。コマンドが、データの引き渡しを必要としない場合、このオブジェクトを null に設定できます。
/// </param>
public void Execute(object parameter)
{
_execute();
}
/// <summary>
/// <see cref="CanExecuteChanged"/> イベントを発生させるために使用されるメソッド
/// <see cref="CanExecute"/> の戻り値を表すために
/// メソッドが変更されました。
/// </summary>
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
/// <summary>
/// 任意の型の引数を1つ受け付けるRelayCommand
/// </summary>
/// <typeparam name="T"></typeparam>
public class RelayCommand<T> : ICommand
{
private readonly Action<T> _execute;
private readonly Func<T, bool> _canExecute;
/// <summary>
/// RaiseCanExecuteChanged が呼び出されたときに生成されます。
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// 常に実行可能な新しいコマンドを作成します。
/// </summary>
/// <param name="execute">実行ロジック。</param>
public RelayCommand(Action<T> execute)
: this(execute, null)
{
}
/// <summary>
/// 新しいコマンドを作成します。
/// </summary>
/// <param name="execute">実行ロジック。</param>
/// <param name="canExecute">実行ステータス ロジック。</param>
public RelayCommand(Action<T> execute, Func<T, bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
/// <summary>
/// 現在の状態でこの <see cref="RelayCommand"/> が実行できるかどうかを判定します。
/// </summary>
/// <param name="parameter">
/// コマンドによって使用されるデータ。コマンドが、データの引き渡しを必要としない場合、このオブジェクトを null に設定できます。
/// </param>
/// <returns>このコマンドが実行可能な場合は true、それ以外の場合は false。</returns>
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute((T)parameter);
}
/// <summary>
/// 現在のコマンド ターゲットに対して <see cref="RelayCommand"/> を実行します。
/// </summary>
/// <param name="parameter">
/// コマンドによって使用されるデータ。コマンドが、データの引き渡しを必要としない場合、このオブジェクトを null に設定できます。
/// </param>
public void Execute(object parameter)
{
_execute((T)parameter);
}
/// <summary>
/// <see cref="CanExecuteChanged"/> イベントを発生させるために使用されるメソッド
/// <see cref="CanExecute"/> の戻り値を表すために
/// メソッドが変更されました。
/// </summary>
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
WPF命令模型主要包含以下几个基本元素:
命令(Command): WPF的命令模型的核心是System.Window.Input.ICommand接口,包含两个方法和一个事件。