WPF命令
一、命令模型
WPF命令模型具有如下4个重要元素:
命令:命令表示应用程序任务,并且跟踪任务是否能够执行。然而,命令实际上不包含执行应用程序任务的代码。
命令绑定:每个命令绑定针对用户界面的具体局域,将命令连接到相关的应用程序逻辑。这种分解的设计非常重要,因为单个命令可用于应用程序中的多个地方,并且在每个地方具有不同的意义。为处理这一问题,需要将统一命令与不同的命令绑定。
命令源:命令源触发命令。例如,MenuItem 和 Button 都是命令源,单击它们都会执行绑定命令。
命令目标:命令目标是在其中执行命令的元素。根据命令的本质,目标可能很重要,也可能不重要。
二、命令库
WPF提供了基本的命令库,基本命令库中保存的命令超过100条。这些命令通过一下5个专门的静态类的静态属性提供:
ApplicationCommands:该类提供了通用命令,包括剪贴板命令(Copy、Cut、Paste)以及文档命令(New、Open、Save、Save As、Print等)。
NavigationCommands:该类提供了用于导航的命令,包括为基于页面的应用程序设计的一些命令(BrowseBack、BrowseForward、NextPage),以及其他适合于基于文档的应用程序的命令(IncreaseZoom、Refresh)。
EditingCommands:该类提供了许多重要的文档编辑命令,包括用于移动的命令(MoveToLineEnd、MoveLeftByWord、MoveUpByPage等),选择内容的命令(SelectToLineEnd、SelectLeftByWord),以及改变格式的命令(ToggleBold、ToggleUnderLine)。
ComponentCommands:该类提供了由用户界面组件使用的命令,包括用于移动和选择内容的命令,这些命令和 EditingCommands类中一些命令类似(甚至完全相同)。
MediaCommands:该类提供了一组用于处理多媒体的命令(如Play、Pause、NextTrack以及IncreaseVolume)。
三、命令源
命令库中的命令始终可用。触发它们的最简单方法就是将它们关联到实现了 ICommandSource接口的控件,大多数是类似按钮类的控件
ICommandSource接口定义了三大属性:
Command | 指向连接的命令,这是唯一必须的细节 |
---|---|
CommandParameter | 提供其他希望随命令发送的数据 |
CommandTarget | 确定将在其中执行命令的元素 |
<Button Command="ApplicationCommands.New" Content="New"/>
四、命令绑定
当将命令关联到命令源时,由于命令还没有与其关联的绑定,所有按钮被认为是禁用的,通过控制 IsEnabled 属性。
为改变这种状态,需要为命令创建绑定以明确以下三件事情:
- 当命令被触发时执行什么操作
- 如何确定命令是否能够被执行(可选,默认总是可用)
- 命令在何处起作用。例如命令可被限制在单个按钮中使用或在整个窗口中
<Window x:Class="WpfApp3.MainWindow"
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:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed"/>
</Window.CommandBindings>
<Grid>
<Button Command="ApplicationCommands.New" Content="New"/>
</Grid>
</Window>
通过代码创建绑定:
public MainWindow()
{
InitializeComponent();
CommandBinding commandBinding = new CommandBinding(ApplicationCommands.New);
commandBinding.Executed += CommandBinding_Executed;
this.CommandBindings.Add(commandBinding);
}
PS: 如是在CommandBindings中添加两个New命令,Button触发时只会触发第一个New命令Executed的事件。
五、自定义命令
namespace WpfApp3
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
public class DataCommands
{
public static RoutedUICommand Requery { get; private set; }
static DataCommands()
{
InputGestureCollection inputs = new InputGestureCollection();
inputs.Add(new KeyGesture(Key.R, ModifierKeys.Control, "Ctrl+R"));
Requery = new RoutedUICommand("Requery", nameof(Requery), typeof(DataCommands), inputs);
}
}
}
<Window x:Class="WpfApp3.MainWindow"
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:local="clr-namespace:WpfApp3"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow" Width="800" Height="450" mc:Ignorable="d">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed" />
<CommandBinding Command="local:DataCommands.Requery" Executed="CommandBindingRequery_Executed" />
</Window.CommandBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button Command="ApplicationCommands.New" Content="New" />
<TextBox Grid.Row="1" />
</Grid>
</Window>
这个时候在窗体的任何地方按下 Ctrl+R都会触发Requery命令。
1.CommandParameter
<Window x:Class="WpfApp3.MainWindow"
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:local="clr-namespace:WpfApp3"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow" Width="800" Height="450" mc:Ignorable="d">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed" />
<CommandBinding Command="local:DataCommands.Requery" Executed="CommandBinding_Executed_2" />
</Window.CommandBindings>
<StackPanel>
<Button Command="ApplicationCommands.New" Content="New"/>
<Button x:Name="btn1" Command="local:DataCommands.Requery" CommandParameter="1" Content="New"/>
<Button x:Name="btn2" Command="local:DataCommands.Requery" CommandParameter="2" Content="Copy" />
</StackPanel>
</Window>
namespace WpfApp3
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void CommandBindingRequery_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show($"CommandBinding_Executed_2===>{e.Parameter}===>{e.Source}===>{e.OriginalSource}==>{e.Command}");
}
}
}
- 当我们在按钮上通过快捷键 Ctrl+R 触发命令时,sender=Window,e.OriginalSource=Window,e.Parameter=n
- 当我们单击btn1时,sender=Window,e.OriginalSource=btn1,e.Parameter=1
- 当我们单击btn2时,sender=Window,e.OriginalSource=btn2,e.Parameter=2
2.CommandTarget
<Window x:Class="WpfApp3.MainWindow"
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:local="clr-namespace:WpfApp3"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow" Width="800" Height="450" mc:Ignorable="d">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed" />
<CommandBinding Command="local:DataCommands.Requery" Executed="CommandBinding_Executed_2" />
</Window.CommandBindings>
<StackPanel>
<Button Command="ApplicationCommands.New" Content="New"/>
<Button x:Name="btn1" Command="local:DataCommands.Requery" CommandParameter="1" Content="New" CommandTarget="{Binding ElementName=btn2}"/>
<Button x:Name="btn2" Command="local:DataCommands.Requery" CommandParameter="2" Content="Copy" />
</StackPanel>
</Window>
- 当我们在按钮上通过快捷键 Ctrl+R 触发命令时,sender=Window,e.OriginalSource=Window,e.Parameter=null
- 当我们单击btn1时,sender=Window,e.OriginalSource=btn2,e.Parameter=1
- 当我们单击btn2时,sender=Window,e.OriginalSource=btn2,e.Parameter=2
六、ViewModel Command
namespace WpfApp3
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
public class RelayCommand : ICommand
{
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
{
CommandManager.RequerySuggested += value;
}
}
remove
{
if (_canExecute != null)
{
CommandManager.RequerySuggested -= value;
}
}
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
{
return _canExecute(parameter);
}
return true;
}
public virtual void Execute(object parameter)
{
if (_execute != null && CanExecute(parameter))
{
_execute(parameter);
}
}
Action<object> _execute;
Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute) : this(execute, null)
{
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public void RaiseCanExecuteChanged()
{
}
}
}
namespace WpfApp3
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class ViewModel
{
public string Name { get; set; }
public int Age { get; set; }
public RelayCommand Update { get; private set; }
public ViewModel()
{
Update = new RelayCommand(update);
}
void update(object param)
{
}
}
}
<Window x:Class="WpfApp3.MainWindow"
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:local="clr-namespace:WpfApp3"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow" Width="800" Height="450" mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=local:ViewModel}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Name:"/>
<TextBox Text="{Binding Name}" Grid.Column="1"/>
<TextBlock Text="Age:" Grid.Row="1" Grid.Column="0"/>
<TextBox Text="{Binding Age}" Grid.Row="1" Grid.Column="1"/>
<Button Command="{Binding Update}" Grid.Row="2" Grid.ColumnSpan="2" Content="Update" CommandParameter="8"/>
</Grid>
</Window>