[WPF] learning chapter thirty-second execution command

  Previous chapters have to command in-depth analysis, the command library base classes and interfaces as well as WPF provides. But not yet include any examples of using these commands.

  As described above, without any hard coded class RoutedUICommand function, but only express command, trigger command has a command source (the code may be used). In response to the command, you need to have command bindings, bind a command to forward to the implementation of the general event handler.

A command source

  Command library commands are always available. They trigger the easiest way is to associate them to implement the controls ICommandSource interface, including inherited from ButtonBase class controls (Button and CheckBox, etc.), separate ListBoxItem objects, HyperLink, and MenuItem.

  ICommandSource interface defines three attributes, as shown in the following table.

Property sheet ICommandSource interface

 

   For example, the following buttons use connected to the Command property ApplicationCommands.New command:

 <Button Command="ApplicationCommands.New">New</Button>

  WPF degree of intelligence is high enough, it can find all five command container class described earlier, which means that can be used in the form of the following abbreviations:

 <Button Command="New">New</Button>

  However, since there is no specified class contains commands, this syntax is not clear enough, not clear enough.

Second, the binding order

  When the command associated with the command source, you will see some interesting phenomena. Command source will be automatically disabled.

  For example, if a button Create New mentioned, the color of the button will not be clicked and shallow, as the property set to false as IsEnabled (shown below). This is because the buttons have a state inquiry command, but also because the command is not bound associated with it, so the button is considered to be disabled.

 

 

   , You need to create a binding order to change this state for the command to clear the following three things:

  What action to perform when the command is triggered.

  How to determine whether the command can be executed (this is optional. If you do not provide this detail, as long as the associated event handler command always available).

  Command works where. For example, commands may be restricted in a single button, or (more common case) in the whole window.

  The following code fragment create a binding for the New commands. May be added to the constructor code window:

//Create the binding
CommandBinding binding=new CommandBinding(ApplicationCommands.New);

//Attach the event handler
binding.Executed+=NewCommand_Executed;

//Register the binding
this.CommandBinding.Add(binding);

  Note, CommandBinding objects created above chant added to the collection that contains CommandBindings window, which works by bubbling event. In fact, when you click the button, CommandBinding.Executed event bubbles from the button to contain elements.

  Although the window is used to add all the bindings, but CommandBindings real property is defined in the UIElement base class. This means that any element of support this property. For example, if you add a command to bind directly to the use of its buttons, a good example of this is still work (although not binding in the reuse and other advanced elements). For maximum flexibility, the command binding is usually added to the top-level window. If you want to use the same same command in multiple windows, you need to create, respectively, in order to bind these windows.

  The above code assumes that the event handler in the same class already called NewCommand_Executed, and the handler is ready to receive commands. The following is an example of this embodiment comprises a simple command source code shown:

private void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("New command triggered by " + e.Source.ToString());
        }

  Now, if you allow the application to be successful, the button is enabled. If you click the button, it will trigger the Executed event, which bubbled to the window, and NewCommand_Executed given above () event handler handles. This is, WPF will tell the event source (button). Also receive commands are invoked by ExecutedRoutedEventArgs object reference (ExecutedRoutedEventArgs.Command), as well as additional data (ExecutedRoutedEventArgs.Parameter) all at the same time passed. In this embodiment, because there is no additional data is transmitted, the parameter is null (the transmitting additional data, if desired, provided CommandParameter win Properties command source; and, if desired some of the information transmitted from another control, also need to use data binding expressions set CommandParameter property).

  In the above example, a code generation command binding. However, if you want to streamline the code-behind file, using XAML way of life associated with the same easy command. The following is required tags:

<Window x:Class="Commands.TestNewCommand"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TestNewCommand" Height="300" Width="300">
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.New" Executed="NewCommand"></CommandBinding>
    </Window.CommandBindings>
    <StackPanel>
        <Button Margin="5" Padding="5" Command="ApplicationCommands.New">New</Button>
    </StackPanel>
</Window>

  Visual Studio does not support the use of command is defined Bind any design. Support for command and control connections are also weak. Use the Command Properties window, set the control's properties, but need to enter the correct name of the command - due not provide drop-down list contains commands, and therefore can not easily select a command from the list.

Third, the use of multi-source command

  By way of example above common trigger events seem less direct. However, when you add more controls use the same commands, command an extra layer of meaning to mention it emerged out. For example, the following can also be used to add a menu item New commands:

        <Menu >
            <MenuItem Header="File">
                <MenuItem Command="New"></MenuItem>
            </MenuItem>
        </Menu>

  Note that this MenuItem objects New command does not set the Header property. This is because the MenuItem class is smart enough, if not set the Header property, it will extract text from the command (Button control does not have this feature). Although this feature with the convenience seem small, but if you plan to use a different language localization applications, this feature is very important. In this case, only you need to modify the text (Text property by setting command) in one place. It is easier to track than in the whole window.

  MenuItem class also has a function: to automatically extract Command.InputBinding first set a shortcut key (if any). For ApplicationCommands.New command object, which means that displays the shortcut keys Ctrl + N (shown below) next to the menu text.

 

 Fourth, the trim command text

  Since the menu item text command with automatic extraction of features, Ken back to wonder whether other ICommandSource class also has a similar function, such as the Button control.

  可以使用两种技术重用命令文本。一种选择是直接从静态命令对象中提取文本。XAML可使用Static标记扩展完成这一任务。下面的示例获取命令名New,并将它作为按钮的文本:

<Button Margin="5" Padding="5" Command="New" Content="{x:Static ApplicationCommands.New}"></Button>

  该方法的问题在于,它指示调用命令对象命令对象的ToString()方法。因此,得到的是命令名,而不是命令的文本(对于哪些名称中包含多个单词的命令,使用命令文本更好些,因为命令文本包含空格)。虽然解决这一问题,但需要完成更多工作。这种方法还存在一个问题,一个按钮将同一个命令使用两次,可能会无意间从错误的命令获取文本)。

  更好的解决方案是使用数据绑定表达式。在此使用的数据绑定有些不寻常,因为他绑定到当前元素吗,获取正在使用的Command对象,并提取Text属性。下面是非常复杂的语法:

 <Button Margin="5" Padding="5" Command="New" Content="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}"></Button>

  可通过另一种更具想象力的方式使用该技术。例如,可使用一幅小图像设置按钮的内容,而在按钮的工具提示中使用数据绑定表达式显示命令名:

<Button Margin="5" Padding="5" Command="ApplicationCommands.New" ToolTip="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}">
     <Image Source="redx.jpg"  Stretch="None"></Image>
</Button>

  按钮的内容可以是形状,也可以是显示为缩略图的位图。

  显然,这种方法比直接在标记中放置命令文本更麻烦些。然而,如果准备使用不同的语言本地化应用程序,使用这个方法是值得的。当应用程序启动时,只需要为所有命令设置命令文本即可(如果在创建了命令绑定后改变命令文本,不会产生任何效果。因为Text属性不是依赖项属性,所以没有自动的更改通知来更新用户界面)。

五、直接调用命令

  并非只能使用实现了ICommandSource接口的类来触发希望执行的命令。也可以用Execute()方法直接调用来自任何事件处理程序的方法。这时需要传递参数值(或null引用)和对目标元素的引用:

ApplicationCommands.New.Execute(null,targetElement);

  目标元素是WPF开始查找命令绑定的地方。可使用包含窗口(具有命令绑定)或嵌套的元素(例如,实际引发事件的元素)。

  也可在关联的CommandBinding对象中调用Execute()方法。在这种情况下,不需要提供目标元素,因为会自动将公开正在使用的CommandBindings集合的元素设置为目标元素。

this.CommandBindings[0].Command.Execute(null);

  这种方法只使用了半个命令模型。虽然也触发命令,但不能响应命令的状态变化。如果希望实现该特性,当命令变为启用或禁用时,也可能希望处理RoutedCommand.CanExecuteChanged事件进行响应。当引发CanExecuteChanged事件时,需要调用RoutedCommand.CanExecute()方法检查命令是否处于可用状态。如果命令不可用。可禁用或改变用户界面中的部分内容。

六、禁用命令

  如果想要创建状态在启用和禁用之间变化的命令,你将体会到命令模型的真正优势。例如,分析下图中显示的单窗口应用程序,它是有菜单、工具栏以及大文本框构成的简单文本编辑器。该应用程序可以打开文件,创建新的(空白)文档,以及保存所执行的操作。

 

   在该应用程序中,保持New、Open、Save、SaveAs以及Close命令一直可用是非常合理的。但还有一种设计,只有当某些操作使文本相对于原来的文件发生了变化时才启用Save命令。根据约定,可在代码中使用简单的Boolean值来跟踪这一细节:

private bool isDirty = false;

  然后当文本发生变化时设置该标志:

private void txt_TextChanged(object sender, RoutedEventArgs e)
        {
            isDirty = true;
        }

  现在需要从窗口命令绑定传递信息,使链接的控件可根据需要进行更新。技巧是处理命令绑定的CanExecute事件。可通过下面的代码为该事件关联事件处理程序:

CommandBinding binding = new CommandBinding(ApplicationCommands.Save);
            binding.Executed += SaveCommand_Executed;
            binding.CanExecute += SaveCommand_CanExecute;
            this.CommandBindings.Add(binding);

  或使用声明方式:

<Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Save" 
                        Executed="SaveCommand_Executed" 
                        CanExecute="SaveCommand_CanExecute"></CommandBinding>
    </Window.CommandBindings>

  在事件处理程序中,只需要检查isDirty变量,并相应地设置CanExecuteRoutedEventArgs.CanExecute属性:

private void SaveCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = isDirty;
        }

  如果isDirty的值时false,就禁用命令。如果isDirty的值为true,就启用命令(如果没有设置CanExecute标志,就会保持最近的值)。

  当使用CanExecute事件时,还需要理解一个问题,由WPF负责调用RoutedCommand.CanExecute()方法来触发事件处理程序,并确定命令的状态。当WPF命令管理器探测到某个确信十分重要的变化——例如,当焦点从一个控件移到另一个控件上时,或执行了某个命令后,WPF命令管理器就会完成该工作。控件还能引发CanExecuteChanged事件以通知WPF重新评估命令——例如,当用户在文本框中按下一个键时会发生该事件。总之,CanExecute事件会被频繁地触发,并且不应在该事件的处理程序中使用耗时的代码。

  然而,其他因素可能影响命令状态。在当前示例中,为响应其他操作,可能会修改isDirty标志。如果发现命令状态未在正确的时间被更新,可强制WPF为所有正在使用的命令调用CanExecute()方法。通过调用静态方法CommandManager.InvalidateRequerySuggested()完成该工作。然后命令管理器触发RequerySuggested事件,通知窗口中的命令源(按钮、菜单项等)。此后命令源会重新查询它们链接的命令并相应地更新它们的状态。

七、具有内置命令的控件

  一个输入控件可自行处理命令事件。例如,TextBox类处理Cut、Copy以及Paste命令(还有Undo、Redo命令,以及一些来自EditingCommd类的用于选择文本以及将光标移到不同位置的命令)。

  当控件具有自己的硬编码命令逻辑时,为使命令工作不需要做其他任何事情。例如,对于上节示例的简单编辑器,添加如下工具栏按钮,就会自动获取对剪切、复制和粘贴文本的支持:

<ToolBar>
     <Button Command="Cut">Cut</Button>
     <Button Command="Copy">Copy</Button>
     <Button Command="Paste">Paste</Button>
 </ToolBar>

  现在淡季这些按钮中的任意一个(当文本框具有焦点时),就可以复制、剪切或从剪贴板粘贴文本。有趣的是,文本框还处理CanExecute事件。如果当前未在文本框中选中任何内容,就会禁用剪切和复制命令。当焦点移到其他不支持这些命令的控件时,会自动禁用所有这三个命令(除非关联自己的CanExecute事件处理程序以启动这些命令)。

  该例有一个有趣的细节。Cut、Copy和Paste命令被具有焦点的文本框处理。然而,由工具栏上的按钮触发的命令时完全独立的元素。在该例中,这个过程之所以能够无缝工作,是因为按钮被放到工具栏上,ToolBar类提供了一些内置逻辑,可将其子元素的CommandTarget属性动态设置为当前具有焦点的控件(从技术角度看,ToolBar控件一直在关注着其父元素,即窗口,并在上下文中查找最近具有焦点的控件,即文本框。ToolBar控件有单独的焦点范围(focus scope),并且在其上下文中按钮是具有焦点的)。

如果在不同容器(不是ToolBar或Menu控件)中放置按钮,就不会获得这些优势。这意味着除非手动设置CommanTarget属性,否则按钮不能工作。为此,必须使用命令目标元素的绑定的表达式。例如,如果文本框被命名为txtDocument,就应该像下面这样定义按钮:

<Button Command="Cut" CommandTarget="{Binding ElementName=txtDocument}">Cut</Button>
<Button Command="Copy" CommandTarget="{Binding ElementName=txtDocument}">Copy</Button>
<Button Command="Paste" CommandTarget="{Binding ElementName=txtDocument}">Paste</Button>

  另一个较简单的选择是使用附加属性FocusManager.IsFocusScope创建新的焦点范围。当触发命令时,该焦点范围会通知WPF在父元素的焦点范围内查找元素:

<StackPanel FocusManager.IsFocusScope="True">
    <Button Command="Cut">Cut</Button>
    <Button Command="Copy">Copy</Button>
    <Button Command="Paste">Paste</Button>
</StackPanel>

  该方法还有一个附加优点,即相同的命令可应用于多个控件,不像上个示例那样对CommandTarget进行硬编码。此外,Menu和ToolBar控件默认将FocusManager.IsFocusScope属性设置为true,但如果希望简化命令路由行为,不在父元素上下文中查找具有焦点的元素,也可将该属性设为false。

  在极少数情况下,你可能发现控件支持内置命令,而你并不想启用它。在这种情况下,可以采用三种方法禁用命令。

  理想情况下,控件提供用于关闭命令支持的属性,从而确保控件移除这些特性并连贯地调整自身。例如,TextBox控件提供了IsUndoEnabled属性,为阻止Undo特性,可将该属性设置为false(如果IsUndoEnabled属性为true,Ctrl+Z组合键将触发Undo命令)。

  如果这种做法行不通,可为希望禁用的命令添加新的命令绑定。然后该命令绑定可提供新的CanExecute事件处理程序,并总是响应false。下面举一个使用该技术删除文本框Cut特性支持的示例:

CommandBinding commandBinding=new CommandBinding(ApplicationCommands.Cut,null,SuppressCommand);
txt.CommandBindings.Add(commandBinding);

  而且该事件处理程序设置CanExecute状态:

private void SupressCommand(object sender,CanExecuteRoutedEventArgs e)
{
  e.CanExecute=false;
  e.Handled=false;    
}

  注意,上面的代码设置了Handled标志以阻止文本框自我执行计算,而文本框可能将CanExecute属性设置为true。

  该方法并不完美。它可成功地为文本框禁用Cut快捷键(Ctrl+X)和上下文菜单中的Cut命令。然而,仍会在上下文菜单中显示处理禁用状态的该选项。

  最后一种选择是,使用InputBinding集合删除触发命令的输入。例如,可使用带阿妈禁用触发TextBox控件中的Copy命令的Ctrl+C组合键,如下所示:

KeyBinding keyBinding=new KeyBinding(ApplicationCommands.NotACommand,Key.C,ModifierKeys.Control);
txt.InputBinding.Add(keyBinding);

  技巧是使用特定的ApplicationCommands.NotACommand值,该命令什么都不做,它专门用于禁用输入绑定。

  当使用这种方法时,仍启用Copy命令。可通过自己创建的按钮触发该命令(或使用文本框的上下文菜单触发命令,除非也通过将ContextMenu属性设置为null删除了上下文菜单)。

 

Guess you like

Origin www.cnblogs.com/Peter-Luo/p/12274329.html