Skip to content
分类目录:

Command

Post date:
Author:

ICommand:

bool CanExcecute: 表示命令是否可以执行

Execute(object):执行命令,并输入object参数

CanExecuteChanged:

ICommandSource:

Command, CommandParameter, CommandTarget

CommandBinding

定义和关联Command的具体实现

<Window.CommandBindings>
	<CommandBinding Command="Application.Open" 
      Executed="OpenCmdExecuted"
      CanExecute="OpenCmdCanExecute" />
</Window.CommandBindings>

Routed Command

当调用CanExeucte方法时,分别会产生CanExecute和PreviewCanExecute

当调用Execute方法时,会产生Executed和PreviewExecuted

Command Repository

WPF中内置的命令包括:

ApplicationCommands, NavigationCommands, MediaCommands, EditingCommands, ComponentCommands

public static class ApplicationCommands{
    public static RoutedUICommand Copy{get;}
}

通过CommandBindings来关联Command和具体的执行操作后,子控件会自动查找本身和父控件的CommandBindings绑定信息,从而使用同样的执行逻辑。

但是XAML中的CommandBindings关联的Executed都是事件,直接使用的话,逻辑会放在View中,而不是ViewModel中。

每个控件都继承了CommandBindings属性,比较适合在自定义控件中使用。

<Window>
	<Window.CommandBindings>
  	<CommandBinding Command="ApplicationCommands.New" CanExecute="HelpCanExecute"
          Executed="HelpExecuted" />
  </Window.CommandBindings>
  <Window.InputBindings>
    <KeyBinding Command="Help" Key="H" Modifiers="Ctrl"/>
    <MouseBinding Command="Help" MouseAction="LeftDoubleClick"/>
  </Window.InputBindings>
  <StackPanel>
  	<Button Command="ApplicationCommands.New" Content="帮助"/>
  </StackPanel>
</Window>
private void HelpCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
	e.CanExecute=true;
    e.Handle=true;
}

自定义通用Command,并在控件范围内绑定Command:

public static class ControlCommands
{   
    public static RoutedCommand Prev { get; } 
                = new(nameof(Prev), typeof(ControlCommands));
}

在控件范围内,建立Command和Action的绑定

public class NumericUpDown : Control
{
    private const string ElementTextBox = "PART_TextBox";

    private TextBox _textBox;

    public NumericUpDown()
    {
        CommandBindings.Add(new CommandBinding(ControlCommands.Prev, (s, e) =>
        {
            if (IsReadOnly) return;

            SetCurrentValue(ValueProperty, Value + Increment);
        }));       
    }
}

控件的ControlTemplate中将Button的Command和自定义的Command绑定,进而Button的Command作为了一个入口来触发,同时ControlTemplate中的可视化树中的控件会依次查找父控件的CommandBinding

<ControlTemplate x:Key="NumericUpDownExtendLeftTemplate" TargetType="hc:NumericUpDown">
  <Button x:Name="UpButton" 
          Command="interactivity:ControlCommands.Prev"/>
</ControlTemplate>

Command Parameter:

Parameter的值是支持绑定的,也可以是普通的值,如果是普通的常量值,则CommandParameter的类型会是string

 <!--如果只显示写了Path,没有写数据来源,则Binding会默认从DataContext中查找对应的属性-->
 <TextBlock Text="{Binding SkyColor}"/>
 <Button Content="OpenWithoutParameter" 
         Command="{Binding OpenCommand}"/>

 <!--CommandParameter传入的参数是常量,且Type是string ·-->
 <Button Content="OpenWithStringParameter" 
         Command="{Binding OpenWithParameterCommand}" 
         CommandParameter="1"/>
 
 <!--虽然Command中有参数,但是也可以不设置CommandParameter,此时参数是null -->
 <Button Content="OpenWithStringParameter" 
         Command="{Binding OpenWithParameterCommand}"/>
 
 <!--使用Binding来给CommandParameter赋值,则CommandParameter的类型取决于绑定值的类型,不再局限于string -->
 <Button Content="OpenWithBindingParameter" 
         Command="{Binding OpenWithBindingParameterCommand}" 
         CommandParameter="{Binding Source={x:Static helpers:ParameterDefinitions.MaxCapacity}}"/>

InputBindings

可以将鼠标或者键盘的事件和Command关联起来。

<Label Style="{StaticResource FS_Label}" HorizontalContentAlignment="Left" FontSize="14" Content="{Binding ItemName}" Width="{Binding Path=ActualWidth, ElementName=deviceStatusListBox}" Background="Transparent">
   <Label.InputBindings>
       <MouseBinding MouseAction="LeftDoubleClick" Command="{Binding DataContext.MenuItemDoubleClickCommand, UpdateSourceTrigger=PropertyChanged, ElementName=deviceStatusListBox}" CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}"/>
   </Label.InputBindings>
</Label>

Event to command:

需要使用到Prism的框架。

<Window ...
  xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:prism="http://prismlibrary.com/">
<ListBox Grid.Row="1" Margin="5" ItemsSource="{Binding Items}" SelectionMode="Single">
   <i:Interaction.Triggers>
       <!-- This event trigger will execute the action when the corresponding event is raised by the ListBox. -->
       <i:EventTrigger EventName="SelectionChanged">
           <!-- This action will invoke the selected command in the view model and pass the parameters of the event to it. -->
           <prism:InvokeCommandAction Command="{Binding SelectedCommand}" TriggerParameterPath="AddedItems" />
       </i:EventTrigger>
   </i:Interaction.Triggers>
</ListBox>
</Window>

RoutedEvent

路由策略(Routing Strategies)

RoutedEvent有三种路由策略:

  1. 冒泡(Bubbling):事件从事件源开始,逐级向上冒泡到根元素。大多数WPF事件使用冒泡策略
  • 隧道(Tunneling):事件从根元素开始,逐级向下传递到事件源。隧道事件通常以“Preview”为前缀,例如PreviewMouseDown
  • 直接(Direct):事件仅在事件源上触发,不会传播


public delegate void DragStartedEventHandler(object sender, DragStartedEventArgs e);
/// <summary>
///     Event fires when user press mouse's left button on the thumb.
/// </summary>
public static readonly RoutedEvent DragStartedEvent =
    EventManager.RegisterRoutedEvent("DragStarted", RoutingStrategy.Bubble, 
                                     typeof(DragStartedEventHandler), 
                                     typeof(Thumb));
 protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
    if (!IsDragging)
    {
        e.Handled = true;
        Focus();
        CaptureMouse();
        SetValue(IsDraggingPropertyKey, true);
        _originThumbPoint = e.GetPosition(this);
        _previousScreenCoordPosition = _originScreenCoordPosition = SafeSecurityHelper.ClientToScreen(this,_originThumbPoint);
        bool exceptionThrown = true;
        try
        {
            RaiseEvent(new DragStartedEventArgs(_originThumbPoint.X, _originThumbPoint.Y));
            exceptionThrown = false;
        }
        finally
        {
            if (exceptionThrown)
            {
                CancelDrag();
            }
        }
    }
    else
    {
        // This is weird, Thumb shouldn't get MouseLeftButtonDown event while dragging.
        // This may be the case that something ate MouseLeftButtonUp event, so Thumb never had a chance to
        // reset IsDragging property
        Debug.Assert(false,"Got MouseLeftButtonDown event while dragging!");
    }
    base.OnMouseLeftButtonDown(e);
}
EventManager.RegisterClassHandler(typeof(GridSplitter), Thumb.DragStartedEvent, 
                                  new DragStartedEventHandler(GridSplitter.OnDragStarted));

private static void OnDragStarted(object sender, DragStartedEventArgs e)
{
    GridSplitter splitter = sender as GridSplitter;
    splitter.OnDragStarted(e);
}
豫ICP备2021008859号-1