2

以下に示すように、マスターの詳細を表示するためのユーザーコントロールがあります。CloseCommandを備えたベースビューモデルを備えた典型的なMVVMアーキテクチャ。

TabItemでcloseコマンドを実行するKeyBindingのスコープを設定しようとしていますが、動作させることができません。

興味深いことに、PersonDetailView(以下に示すように、TabControlが表示する可能性のある2つのUSerControlの1つ)にバインディングを配置すると、動作させることができますが、TabControlまたはそれを含むBorderに配置する必要があります

助言がありますか?

乾杯、
ベリール

UserControl

<Grid>

    <ListBox Style="{StaticResource ListBoxStyle}" />

    <GridSplitter 
        HorizontalAlignment="Right" VerticalAlignment="Stretch" Grid.Column="1" 
        ResizeBehavior="PreviousAndNext" Width="5" Background="#FFBCBCBC" KeyboardNavigation.IsTabStop="False"
                  />

    <Border Grid.Column="2" Background="{StaticResource headerBrush}">

        // ** THIS is the scope I want, but it doesn't work
        <Border.InputBindings>
            <KeyBinding Key="F4" Modifiers="Control" Command="{Binding CloseCommand}"/>
        </Border.InputBindings>

        <TabControl Style="{StaticResource TabControlStyle}" >

            <TabControl.Resources>                   
                <DataTemplate DataType="{x:Type personVm:PersonDetailVm}">
                    <local:PersonDetailView />
                </DataTemplate>
                <DataTemplate DataType="{x:Type orgVm:OrganizationDetailVm}">
                    <local:OrganizationDetailView />
                </DataTemplate>
            </TabControl.Resources>

        </TabControl>
    </Border>

</Grid>

TabItemスタイル

<Style x:Key="OrangeTabItemStyle" TargetType="{x:Type TabItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabItem}">
                <Border AllowDrop="true" ToolTip="{Binding DisplayName}">
                    <Border Name="Border" Background="Transparent" BorderBrush="Transparent" BorderThickness="1,1,1,0" CornerRadius="2,2,0,0">
                        <DockPanel x:Name="TitlePanel" TextElement.Foreground="{StaticResource FileTabTextBrush}">
                            <ctrl:GlyphButton 

                                // ** This works as expected
                                Command="{Binding CloseCommand}" CommandParameter="{Binding}"
                                >
                            </ctrl:GlyphButton>

                        </DockPanel>
                    </Border>

                    // ** Can't get it to work from here either **
                    <Border.InputBindings>
                        <KeyBinding Command="{Binding CloseCommand}" Key="F4" Modifiers="Control" />
                    </Border.InputBindings>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

アップデート

RoutedCommandを自分のスタイルに設定するのに迷っています

<Style x:Key="OrangeTabItemStyle" TargetType="{x:Type TabItem}">
    <Setter Property="beh:RoutedCommandWire.RoutedCommand" Value="F4"/> **** ?? ****
    <Setter Property="beh:RoutedCommandWire.ICommand" Value="{Binding CloseCommand}"/>
</Style>

これがC#での回答コードのように見えると思います

public class RoutedCommandWire
{

    public static readonly DependencyProperty RoutedCommandProperty =
        DependencyProperty.RegisterAttached("RoutedCommand", typeof(RoutedCommand), typeof(RoutedCommandWire), new PropertyMetadata(OnCommandChanged));

    public static RoutedCommand GetRoutedCommand(DependencyObject d) { return (RoutedCommand) d.GetValue(RoutedCommandProperty); }
    public static void SetRoutedCommand(DependencyObject d, RoutedCommand value) { d.SetValue(RoutedCommandProperty, value); }

    public static readonly DependencyProperty ICommandProperty = 
        DependencyProperty.RegisterAttached("Iommand", typeof(ICommand), typeof(RoutedCommandWire));

    public static ICommand GetICommand(DependencyObject d) { return (ICommand) d.GetValue(ICommandProperty); }
    public static void SetICommand(DependencyObject d, ICommand value) { d.SetValue(ICommandProperty, value); }

    private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var fe = d as FrameworkElement;
        if(fe==null) return;

        if (e.OldValue != null) {
            Detach(fe, (RoutedCommand) e.OldValue);
        }
        if (e.NewValue != null) {
            Attach(fe, (RoutedCommand) e.NewValue, Execute, CanExecute);
        }
    }

    private static void CanExecute(object sender, CanExecuteRoutedEventArgs e) {
        var depObj = sender as DependencyObject;
        if (depObj == null) return;

        var command = GetICommand(depObj);
        if (command == null) return;

        e.CanExecute = command.CanExecute(e.Parameter);
        e.Handled = true;
    }

    private static void Execute(object sender, ExecutedRoutedEventArgs e)
    {
        var depObj = sender as DependencyObject;
        if (depObj == null) return;

        var command = GetICommand(depObj);
        if (command == null) return;

        command.Execute(e.Parameter);
        e.Handled = true;
    }

    public static void Detach(FrameworkElement fe, RoutedCommand command) {
        var bindingCollection = fe.CommandBindings;
        if (bindingCollection.Count == 0) return;

        var matches = bindingCollection.Cast<CommandBinding>().Where(binding => binding.Equals(command));
        foreach (var binding in matches) {
            bindingCollection.Remove(binding);
        }
    }

    public static void Attach(FrameworkElement fe, RoutedCommand command, 
        ExecutedRoutedEventHandler executedHandler, CanExecuteRoutedEventHandler canExecuteHandler, bool preview = false)
    {
        if (command == null || executedHandler == null) return;

        var binding = new CommandBinding(command);
        if (preview)
        {
            binding.PreviewExecuted += executedHandler;
            if (canExecuteHandler != null)
            {
                binding.PreviewCanExecute += canExecuteHandler;
            }
        }
        else
        {
            binding.Executed += executedHandler;
            if (canExecuteHandler != null)
            {
                binding.CanExecute += canExecuteHandler;
            }
        }
        fe.CommandBindings.Add(binding);
    }
}
4

1 に答える 1

2

KeyBinding は、キーボード入力を受け入れるコントロールでのみ機能します。国境はそうではありません。一般に、InputBinding は、親要素に CommandBinding を定義できるため、子要素にフォーカスがあるときにコマンドを処理できるという点でも CommandBinding とは異なりますが、親要素に InputBinding を定義して子要素で有効にすることはできません。 .

できることは、デフォルトの InputGesture をコマンドの InputGestures コレクションに追加することです。これにより、キーボード入力を受け入れるすべてのコントロールから、そのキーボード ショートカットを使用してコマンドを使用できるようになります (これは、どこでも InputBindings を指定する必要があるよりもはるかに優れていますね)。これを利用するには、RoutedCommand を使用して MVVM-ICommand を呼び出す必要があります。添付されたプロパティを使用して 2 つを組み合わせることができます。これは、私が「スティッキー コマンド」と呼んでいるパターンで、添付された動作に非常に似ています。

このコードは、添付プロパティを定義します。

    Public Class Close

    Public Shared ReadOnly CommandProperty As DependencyProperty = DependencyProperty.RegisterAttached("Command", GetType(RoutedCommand), GetType(Close), New PropertyMetadata(AddressOf OnCommandChanged))
    Public Shared Function GetCommand(ByVal d As DependencyObject) As RoutedCommand
        Return d.GetValue(CommandProperty)
    End Function
    Public Shared Sub SetCommand(ByVal d As DependencyObject, ByVal value As RoutedCommand)
        d.SetValue(CommandProperty, value)
    End Sub

    Public Shared ReadOnly MVVMCommandProperty As DependencyProperty = DependencyProperty.RegisterAttached("MVVMCommand", GetType(ICommand), GetType(Close))
    Public Shared Function GetMVVMCommand(ByVal d As DependencyObject) As ICommand
        Return d.GetValue(MVVMCommandProperty)
    End Function
    Public Shared Sub SetMVVMCommand(ByVal d As DependencyObject, ByVal value As ICommand)
        d.SetValue(MVVMCommandProperty, value)
    End Sub


    Private Shared Sub OnCommandChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
        If e.OldValue IsNot Nothing Then
            Detach(d, DirectCast(e.OldValue, RoutedCommand))
        End If
        If e.NewValue IsNot Nothing Then
             Attach(d, DirectCast(e.NewValue, RoutedCommand), AddressOf DoCloseCommand, AddressOf CanDoCloseCommand)
        End If
    End Sub

    Private Shared Sub CanDoCloseCommand(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
        If sender IsNot Nothing Then
            Dim com As ICommand = GetMVVMCommand(sender)
            If com IsNot Nothing Then
                e.CanExecute = com.CanExecute(e.Parameter)
                e.Handled = True
            End If
        End If
    End Sub

    Private Shared Sub DoCloseCommand(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
        If sender IsNot Nothing Then
            Dim com As ICommand = GetMVVMCommand(sender)
            If com IsNot Nothing Then
                com.Execute(e.Parameter)
                e.Handled = True
            End If
        End If
    End Sub

    Public Shared Sub Detach(ByVal base As FrameworkElement, ByVal command As RoutedCommand)
        Dim commandBindings As CommandBindingCollection = base.CommandBindings
        If commandBindings IsNot Nothing Then
            Dim bindings = From c As CommandBinding In commandBindings
                           Where c.Command Is command
                           Select c
            Dim bindingList As New List(Of CommandBinding)(bindings)
            For Each c As CommandBinding In bindingList
                commandBindings.Remove(c)
            Next
        End If
    End Sub

    Public Shared Sub Attach(ByVal base As FrameworkElement, ByVal command As RoutedCommand, ByVal executedHandler As ExecutedRoutedEventHandler, ByVal canExecuteHandler As CanExecuteRoutedEventHandler, Optional ByVal preview As Boolean = False)
        If command IsNot Nothing And executedHandler IsNot Nothing Then
            Dim b As CommandBinding = New CommandBinding(command)
            If preview Then
                AddHandler b.PreviewExecuted, executedHandler
                If canExecuteHandler IsNot Nothing Then
                    AddHandler b.PreviewCanExecute, canExecuteHandler
                End If
            Else
                AddHandler b.Executed, executedHandler
                If canExecuteHandler IsNot Nothing Then
                    AddHandler b.CanExecute, canExecuteHandler
                End If
            End If
            base.CommandBindings.Add(b)
            'For Each i As InputGesture In command.InputGestures
            '    GetInputBindings(base).Add(New InputBinding(command, i))
            'Next
        End If
    End Sub

閉じるコマンドを処理する場所であるため、TabItems で両方を使用すると思います。Close.Command を、InputGestures にキーボード ショートカットを持つ RoutedCommand に設定し、Close.MVVMCommand= に設定します。 "{Binding CloseCommand}".

アップデート

ViewModel で次のように RoutedCommand を定義できます。

Public Shared ReadOnly TestCommand As New RoutedUICommand("Test", "TestCommand", GetType(ViewModel))
Shared Sub New()
    TestCommand.InputGestures.Add(New KeyGesture(Key.T, ModifierKeys.Control))
End Sub

静的コンストラクターは、コマンドの既定のキージェスチャを設定します。XAML でそれを行う場合は、カスタム添付プロパティを使用して行うこともできます。とにかく、XAML で次のように RoutedCommand を参照します。

Close.Command="{x:Static my:ViewModel.TestCommand}"
于 2012-06-09T08:30:12.237 に答える