前の章では、詳細な分析コマンドにコマンドライブラリの基本クラスとインタフェースを持つだけでなく、WPFが提供されます。しかし、まだ、これらのコマンドを使用して、任意の例が挙げられます。
任意のハードコード化されたクラスRoutedUICommand機能せず、上述したが、唯一のコマンドを発現するように、トリガ・コマンドは、コマンドソース(コードを使用することができる)を有しています。コマンドに応答して、コマンドバインディングを持っている必要があり、バインドコマンドは、一般的なイベントハンドラの実装に転送します。
コマンドソース
コマンドライブラリコマンドは、常に利用可能です。彼らは、最も簡単な方法は、ButtonBaseクラスのコントロール(ボタンやチェックボックスなど)、別のListBoxItemオブジェクト、ハイパーリンク、およびMenuItemのから継承など、コントロールのICommandSourceインタフェースを実装するためにそれらを関連付けることでトリガ。
次の表に示すようにICommandSourceインタフェースは、3つの属性を定義します。
プロパティシートICommandSourceインタフェース
たとえば、次のボタンは、コマンドプロパティApplicationCommands.Newコマンドに接続を使用します。
< ボタンコマンド= "ApplicationCommands.New" > 新しい</ ボタン>
インテリジェンスのWPF度が十分に高いが、それはすべての5つのコマンドのコンテナクラスには、先に次の略語の形で使用することのできる手段を説明見つけることができます:
< ボタンコマンド= "新しい" >新しい</ ボタン>
何も指定されたクラスは、コマンドが含まれていないがあるのでしかし、この構文は明確では十分ではなく、明確では十分ではありません。
第二に、結合順
コマンドは、コマンドソースに関連付けられているとき、あなたはいくつかの興味深い現象が表示されます。コマンドソースは自動的に無効になります。
例えば、新規に述べたボタンを作成した場合、ボタンの色がでIsEnabledとしてfalseにプロパティセットとして(下記参照)、をクリックして浅いことはありません。これは、ボタンが状態問合せコマンドを持っているためですが、また、コマンドは、それに関連付けられているので、ボタンは無効であると考えられて拘束されていないため。
、あなたは、次の3つをクリアするためのコマンドのために、この状態を変更する結合順序を作成する必要があります。
どのようなアクションコマンドがトリガーされたときに実行すること。
コマンドを実行できるかどうかを確認する方法(これはオプションです。あなたがいる限り、常に利用可能に関連するイベントハンドラコマンドとして、この詳細を提供していない場合)。
コマンドはどこに動作します。例えば、コマンドは、ウィンドウ全体で単一のボタン、または(より一般的な場合)に制限することができます。
次のコードは、新しいコマンドのためのバインディングを作成するフラグメント。コンストラクタのコードウィンドウに追加されることがあります。
// バインディングを作成 いるCommandBinding結合= 新しいいるCommandBinding(ApplicationCommands.Newを)。 // アタッチイベントハンドラ binding.Executed + = NewCommand_Executed。 // 登録バインディング この .CommandBinding.Add(結合);
イベントをバブリングすることによって動作CommandBindingsウィンドウを含むコレクションに追加聖歌の上に作成されたCommandBindingオブジェクト注意、。実際には、あなたがボタンをクリックすると、CommandBinding.Executedイベントは、要素を含むように、ボタンから泡。
窓はすべてのバインディングを追加するために使用されていますが、CommandBindings不動産は、UIElementの基本クラスで定義されているが。いずれかの要素がこのプロパティをサポートしていることをこれが意味。あなたがそのボタンの使用に直接結合するコマンドを追加した場合、この良い例が、まだ(再利用およびその他の高度な要素に結合しないが)の作品です。最大の柔軟性のために、結合コマンドは、通常、トップレベルウィンドウに追加されます。あなたが複数のウィンドウに同じ同じコマンドを使用したい場合は、これらのウィンドウをバインドするためには、それぞれ、作成する必要があります。
上記のコードは、同じクラスでイベントハンドラがすでにNewCommand_Executedと呼ばれることを前提としていて、ハンドラがコマンドを受信する準備ができています。以下、本実施形態の例で示す単純なコマンドソースコードを含みます。
プライベート 無効 NewCommand_Executed(オブジェクト送信者、ExecutedRoutedEventArgs E) { MessageBox.Show(" によってトリガ新コマンド" + e.Source.ToString()); }
あなたは、アプリケーションが成功することを許可した場合さて、ボタンが有効になります。あなたがボタンをクリックすると、それがウィンドウに泡立て、および()イベントハンドラハンドル上に与えられたNewCommand_Executed実行されたイベントをトリガします。これは、WPFは、イベントソース(ボタンを)教えてくれます、です。また、コマンドが渡さ同時にExecutedRoutedEventArgsオブジェクト参照(ExecutedRoutedEventArgs.Command)、ならびに追加のデータ(ExecutedRoutedEventArgs.Parameter)全てによって呼び出される受け取ります。送信される追加のデータが存在しないので、この実施形態では、パラメータがnullである(送信追加データは、提供CommandParameter勝利プロパティコマンドソースを所望の場合、他のコントロールから送信された情報の一部を、所望の場合にも、結合用データに必要式は)CommandParameterプロパティを設定します。
上記の例では、コード生成コマンドが結合します。しかし、あなたは同じ簡単なコマンドに関連する生活のXAMLの方法を使用して、コードビハインドファイルを効率化したい場合。以下は、必要なタグです。
< ウィンドウ×:クラス= "Commands.TestNewCommand" のxmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" のxmlnsを:X = "http://schemas.microsoft.com/winfx/2006 / XAML」 タイトル= "TestNewCommand" 高さ= "300" 幅= "300" > < Window.CommandBindings > < いるCommandBinding コマンド"=" ApplicationCommands.New 実行さNewCommand "=" > </ いるCommandBinding > </ Window.CommandBindings > <StackPanel > < ボタン証拠金= "5" パディング= "5" コマンド= "ApplicationCommands.New" >新しい</ ボタン> </ StackPanelの> </ ウィンドウ] >
Visual Studioは、コマンドの使用がバインドに任意のデザインを定義されているサポートしていません。コマンドおよびコントロール接続のサポートも弱いです。、コマンドプロパティ]ウィンドウを使用してコントロールのプロパティを設定しますが、コマンドの正しい名前を入力する必要があります - 原因ドロップダウンリストには、コマンドが含まれていませので、簡単にリストからコマンドを選択することはできません。
第三に、マルチソースコマンドの使用
共通トリガイベント上記の例を挙げれば以下の直接的ようです。あなたはより多くのコントロールが同じコマンドを使用して追加した場合しかし、それが出て登場言及する意味の余分な層を命じます。たとえば、次のようにもメニュー項目の新しいコマンドを追加するために使用することができます。
< メニュー> < MenuItemのヘッダー= "ファイル" > < MenuItemのコマンド= "新しい" > </ のMenuItem > </ のMenuItem > </ メニュー>
このMenuItemには、新しいコマンドはHeaderプロパティを設定していないオブジェクトことに注意してください。これは、MenuItemクラスは、スマート十分であるため、Headerプロパティを設定していない場合、それは(Buttonコントロールがこの機能を持っていません)コマンドからテキストを抽出しますです。便利でこの機能は小規模に見えるが、あなたは異なる言語のローカライズアプリケーションを使用する場合、この機能は非常に重要ですが。この場合は、あなただけが一つの場所にテキスト(コマンドを設定することで、Textプロパティ)を変更する必要があります。これは、ウィンドウ全体に比べてトラックに簡単です。
自動的に最初のセットをショートカットキー(もしあれば)をCommand.InputBinding抽出する:MenuItemクラスはまた、機能を有しています。メニューテキストの隣ApplicationCommands.Newコマンドオブジェクトの、どの手段が表示されているショートカットキーはCtrl + N(下記参照)。
第四に、トリムコマンドテキスト
Buttonコントロールなどの機能の自動抽出、他のICommandSourceクラスでも同様の機能を持っているかどうかを不思議にケンバック、とメニュー項目のテキストコマンド以降。
可以使用两种技术重用命令文本。一种选择是直接从静态命令对象中提取文本。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删除了上下文菜单)。