3

MenuItem の CommandTarget プロパティを何に使用できるか知っている人はいますか? ドキュメントには次のように記載されています

RoutedCommand で使用する場合、コマンド ターゲットは、Executed イベントと CanExecute イベントが発生するオブジェクトです。CommandTarget プロパティが設定されていない場合、キーボード フォーカスのある要素がターゲットとして使用されます。

ただし、実行時に CommandTarget の値は、コマンドの Execute ハンドラーのどこにも表示されません。sender は、CommandBinding が属するウィンドウです。ExecutedRoutedEventArgs は、メニュー項目の遠い祖先への参照でいっぱいです。

ここでの目標は、さまざまなグリッド、リストなどのさまざまなコンテキスト メニューから実行されるコマンドを実装することです。それらにはすべて、特定のインターフェイスをサポートする項目が含まれています。コンテキスト メニューは異なりますが、共通のコマンドがいくつかあります。「Foo」コマンドは「Foo」を実行するため、共通コマンドは、何をクリックしても、同じ Executed および CanExecute ハンドラを使用します。ハンドラーは、クリックしたグリッド/リストに対して選択されたアイテムが何であるかを判断し、それをインターフェイスにキャストしようとし、そのインターフェイスを持っている場合は何かを行います (特定のコマンドによって消費されるインターフェイスがサポートされていない場合)そのコマンドは無効になります)。ContextMenu または MenuItem を送信者として取得すると、PlacementTarget を取得でき、ユーザーが何をクリックしたかがわかります。ただし、それは ContextMenu の XAML 定義で CommandBinding を定義した場合にのみ機能します。つまり、コマンドが使用されるすべての ContextMenu で XAML のブロック全体をコピーして貼り付け、それらのビュー クラスのすべてでハンドラーを再定義します。 . それは私が維持したい混乱ではありません。

これは、これらのハンドラーを複数回記述したり、各ハンドラーを特定のコマンドに複数回関連付けたりする言語に依存しない理由がない場合のようです。しかし、私が知る限り、XAML はハンドラーとターゲットをまとめてバインドすることを望んでいるようです。ハンドラーを一度バインドしてから、別のターゲットに忍び込むことができますか?

更新: コマンドを静的 Command クラスに配置し、ハンドラーを非静的クラス (メイン ビュー、重要ではない) に配置し、インスタンス化して返す静的 Command.GetCommandBinding(command) メソッドを作成することで、これを解決しました。渡すコマンドの CommandBinding。したがって、グリッド バーでコマンド Foo を使用する場合は、バーが存在するビューのコンストラクターで次のように呼び出します。

Bar.CommandBindings.Add(Commands.GetCommandBinding(Commands.Foo));

次に、コマンドが Bar の ContextMenu に属する MenuItem の Command プロパティに割り当てられると、コマンドの Executed イベントと CanExecute イベントの送信者として Bar が渡されます。

ハンドラーは View クラスのメンバーである必要があるため、XAML でバインディングを実行できません。コマンドの CODE のように、コマンドの NAME を再利用するのを非常に面倒にしながら、設計者がコマンドの NAME を再利用するのにこれほど多くの労力を費やしているのは奇妙に思えます。これは Microsoft がこれまで行った中で最もばかげたことではなく、XAML の残りのほとんどは非常に優れています (IMHO)。

別の解決策: メニュー項目をコンテキスト メニューとは別にリソースとして定義し、メニュー項目全体を再利用します。これは Resources.xaml にあり、マージされた辞書として他の XAML ファイルに含めることができます。イベント ハンドラーは Resources.cs にあります。コンシューマーは、GridContextMenu を使用するか、CtxMenuItem_EmailDocument を同じ方法で独自のコンテキスト メニューに挿入できます。

<MenuItem Command="{x:Static vw:Commands.EmailDocument}" 
        x:Key="CtxMenuItem_EmailDocument">
    <MenuItem.CommandBindings>
        <CommandBinding Command="{x:Static vw:Commands.EmailDocument}"
                        Executed="EmailDocument_Executed"
                        CanExecute="EmailDocument_CanExecute"
                        />
    </MenuItem.CommandBindings>
</MenuItem>

<ContextMenu x:Key="GridContextMenu" x:Shared="true">
    <!-- other items -->
    <StaticResource ResourceKey="CtxMenuItem_EmailDocument" />
    <!-- other items -->
</ContextMenu>

CommandTarget は、ボタンに対してまったく異なる動作を示すようです。それか、別のファイルで定義されている場合、またはリソースとして定義されている場合、CommandBindings の動作はまったく異なります。

4

3 に答える 3

6

AndrewS が書いたものはすべて正しいです。CommandTarget がExecuted / CanExecute イベントの送信者になることを追加したいだけです。コマンドを処理できるようにするために、CommandTarget には、対象のコマンドの CommandBinding が必要です。

最小の例:

<StackPanel>
    <Button Command="Open" CommandTarget="{Binding ElementName=TestTextBox}">Open</Button>
    <TextBox x:Name="TestTextBox">
        <TextBox.CommandBindings>
            <CommandBinding Command="Open" Executed="CommandBinding_Executed"/>
        </TextBox.CommandBindings>
    </TextBox>
</StackPanel>
于 2013-06-12T16:35:46.037 に答える
4

CommandTargetは、関連する Command が RoutedCommand である場合に、CommandManager クラスが CanExecute および Execute イベントのルーティングを開始する要素です。そのため、実際には CanExecute/Execute のイベント引数のパラメーターとして表示されることはありません。実際にはOriginalSourceである可能性がありますが、CommandManager がコマンドを再ルーティングする場合 (ツリーを上方向にトラバースするときの新しいFocusScope ) の場合、再ルーティングされ、再ルーティングされたイベントの eventargs の OriginalSource は、再ルーティング先の要素になります。

通常、複数の要素タイプで処理できる RoutedCommands には CommandTarget を設定しません。たとえば、カット/コピー/ペーストのような ApplicationCommands では、エンド ユーザーがコマンドを受信して​​応答するために操作するコントロールが必要です。ただし、エンド ユーザーがフォーカスした要素に関係なく、その ICommandSource (この場合は MenuItem) の Command プロパティに設定した RoutedCommand が特定の要素インスタンスで実行されるようにする必要がある場合があります。次に、CommandTarget をその要素に設定します (通常は ElementName バインディングを使用します)。

編集: 質問を変更したので、回答を増やします。特定のクラス タイプで特定の RoutedCommand の CanExecute および Execute を処理する場合は、CommandManager クラス (具体的にはRegisterClassCommandBindingメソッド) を使用して、特定の RoutedCommand のグローバル Execute/CanExecute ハンドラーを登録します。

于 2013-06-12T14:43:30.320 に答える
1

記録のために、CommandTargetは、 のコンテキストで、およびハンドラーによって受信されたときにプロパティをMenuItem設定します。OriginalSourceExecutedRoutedEventArgsExecutedCanExecute

したがって、「ターゲット」は「ソース」です
(ラクダが委員会によって設計された馬であるのと同じように)

@EdPlunketSenderが述べたように、はコマンド バインディングのサイトです (e.Source実際にもそうです)。

したがって、元の質問に答えるために、CommandTargeta のは、プロパティのイベント引数 (および) で、コマンドのおよびハンドラーMenuItemへの参照として渡されます。したがって、その参照を使用して、ターゲットで好きなことを行うことができます。ExecutedCanExecuteExecutedRoutedEventArgsCanExecuteRoutedEventArgsOriginalSource

実施例

CommandTargetMenuItemComboBox の選択によって制御されます

MainWindow.xaml

<Window x:Class="CommandTarget.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:CommandTarget"
        Title="MainWindow" Height="200" Width="400">

    <StackPanel x:Name="RootPanel">

        <StackPanel.CommandBindings>
            <CommandBinding x:Name="Pause" Command="Pause"
                            Executed="{x:Static local:MainWindow.OnButtonPause}"
                            CanExecute="{x:Static local:MainWindow.OnPauseCanExecute}" />
        </StackPanel.CommandBindings>

        <DockPanel>

            <Menu DockPanel.Dock="Top" >
                <MenuItem Header="Click Me" x:Name="Emitter"
                          Command="Pause"
                          CommandTarget="{Binding ElementName=Button2}" />
            </Menu>

        </DockPanel>

        <StackPanel Name="Buttons">

            <ToggleButton x:Name="Button1" Height="30" HorizontalAlignment="Stretch"
                          Content="Button1" />
            <ToggleButton x:Name="Button2" Height="30" HorizontalAlignment="Stretch"
                          Content="Button2" />

        </StackPanel>

    </StackPanel>
</Window>

MainWindow.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace CommandTarget
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        //PAUSE COMMAND
        // Static, binding callbacks

        // Executed
        public static ExecutedRoutedEventHandler
            OnButtonPause = (sender, e) =>
            {
                e.Handled = ButtonPauseTarget(e, delegate(ToggleButton target)
                {
                    if (!target.IsEnabled) return false;
                    var flag = target.IsChecked ?? false;
                    target.IsChecked = !flag;
                    return true;
                });
            };

        // CanExecute
        public static CanExecuteRoutedEventHandler
            OnPauseCanExecute = (sender, e) => { e.CanExecute = true; };

        // helper to extract the target from the event args
        private static bool ButtonPauseTarget (RoutedEventArgs e,
            Func<ToggleButton, bool> ex)
        {
            var target = e.OriginalSource as ToggleButton;
            if (target == null) return false;
            var handled = ex(target);

            return handled;
        }
    }
}

注: Pause コマンドはランダムな選択にすぎません。ソース要素でサポートされているコマンドのうち、他のコマンドで使用されていないものであればどれでもかまいません。

より完全な例

于 2016-12-04T11:54:13.960 に答える