【问题标题】:C+Ctrl KeyBinding is not causing copying to happenC+Ctrl KeyBinding 不会导致复制发生
【发布时间】:2011-06-29 19:15:10
【问题描述】:

我已经像这样设置了ListBox

<ListBox ItemsSource="{Binding Logs, Mode=OneWay}" x:Name="logListView">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=.}">
                <TextBlock.InputBindings>
                    <KeyBinding Key="C"
                                Modifiers="Ctrl"
                                Command="Copy"/>
                </TextBlock.InputBindings>
                <TextBlock.CommandBindings>
                    <CommandBinding Command="Copy"
                                    Executed="KeyCopyLog_Executed"
                                    CanExecute="CopyLog_CanExecute"/>
                </TextBlock.CommandBindings>
                <TextBlock.ContextMenu>
                    <ContextMenu>
                        <MenuItem Command="Copy">
                            <MenuItem.CommandBindings>
                                <CommandBinding Command="Copy"
                                                Executed="MenuCopyLog_Executed"
                                                CanExecute="CopyLog_CanExecute"/>
                            </MenuItem.CommandBindings>
                        </MenuItem>
                    </ContextMenu>
                </TextBlock.ContextMenu>
            </TextBlock>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

如您所见,在模板中,每个TextBlock 都有一个上下文菜单,允许用户复制文本。这行得通。

TextBlock 中还有一个KeyBinding 用于ctrl+c 和一个CommandBinding 用于复制。当我按下 ctrl+c 时,方法 KeyCopyLog_Executed 没有被执行。我已经用调试器检查过了。

我应该如何将密钥绑定到TextBlock

【问题讨论】:

    标签: wpf xaml .net-3.5


    【解决方案1】:

    这里有几个问题。

    首先,KeyBindings 仅在当前焦点元素位于定义 KeyBindings 的元素内部 时才有效。在您的情况下,您有一个 ListBoxItem 焦点,但 KeyBindings 是在子元素上定义的 - TextBlock。因此,在 TextBlock 上定义 KeyBindings 在任何情况下都不起作用,因为 TextBlock 无法获得焦点。

    其次,您可能需要知道要复制什么,因此您需要将当前选择的日志项作为参数传递给Copy 命令。

    此外,如果您在TextBlock 元素上定义ContextMenu,则只有在您右击TextBlock 时才会打开它。如果单击列表项的任何其他部分,它将不会打开。因此,您需要在列表框项本身上定义ContextMenu

    考虑到所有这些,我相信您可以通过以下方式完成:

    <ListBox ItemsSource="{Binding Logs, Mode=OneWay}"
             x:Name="logListView"
             IsSynchronizedWithCurrentItem="True">
        <ListBox.InputBindings>
            <KeyBinding Key="C"
                        Modifiers="Ctrl"
                        Command="Copy"
                        CommandParameter="{Binding Logs/}" />
        </ListBox.InputBindings>
    
        <ListBox.CommandBindings>
            <CommandBinding Command="Copy"
                            Executed="CopyLogExecuted"
                            CanExecute="CanExecuteCopyLog" />
        </ListBox.CommandBindings>
    
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="ContextMenu">
                    <Setter.Value>
                        <ContextMenu>
                            <MenuItem Command="Copy"
                                      CommandParameter="{Binding}" />
                        </ContextMenu>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.ItemContainerStyle>
    
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    

    这里我们在ListBox 本身上定义一个KeyBinding,并指定为CommandParameter 当前选定的列表框项(日志条目)。

    CommandBinding 也在ListBox 级别定义,它是右键菜单和键盘快捷键的单一绑定。

    我们在ListBoxItem 的样式中定义ContextMenu,并将CommandParameter 绑定到此ListBoxItem(日志条目)表示的数据项。

    DataTemplate 只是声明了一个绑定到当前数据项的TextBlock

    最后,代码隐藏中的Copy 命令只有一个处理程序:

    private void CopyLogExecuted(object sender, ExecutedRoutedEventArgs e) {
        var logItem = e.Parameter;
    
        // Copy log item to the clipboard
    }
    
    private void CanExecuteCopyLog(object sender, CanExecuteRoutedEventArgs e) {
        e.CanExecute = true;
    }
    

    【讨论】:

    • 谢谢。我还没有测试它,但是当我回到工作岗位时我会测试它。目前,为了获取我想要复制的日志条目,我在事件处理程序中获取了发送者的TemplatedParentDataContext。它适用于MenuItem。不过我会试试你的方法,因为它看起来更简单。
    • 我已经给出了上面的代码。这是.Net 3.5的吗?我无法让它工作,而且我仅限于.Net 3.5。它抛出的问题是:不能在CommandParameter 属性中使用绑定,不能将ContextMenu 属性的值设置为样式块中的MenuItem 对象。
    • 我已经将我最初的做事方式与您的方式融合在一起,并且可行。我将在其中发布答案。
    • @Matt Ellen - 此代码适用于 .NET 4。但您可以通过同时删除 CommandParameter 绑定来解决您提到的问题。相反,只需将 ListBox 的 SelectedItem 属性绑定到数据上下文中的某个属性(例如 SelectedLogItem),然后在 CopyLogExecuted 处理程序中使用此属性来获取要复制的项目。
    【解决方案2】:

    感谢 Pavlov Glazkov 解释键和命令绑定需要在 ListBox 级别,而不是项目模板级别。

    这是我现在的解决方案:

    <ListBox ItemsSource="{Binding Logs, Mode=OneWay}">
        <ListBox.InputBindings>
            <KeyBinding Key="C"
                        Modifiers="Ctrl"
                        Command="Copy"/>
        </ListBox.InputBindings>
        <ListBox.CommandBindings>
            <CommandBinding Command="Copy"
                            Executed="KeyCopyLog_Executed"
                            CanExecute="CopyLog_CanExecute"/>
        </ListBox.CommandBindings>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Path=.}">
                    <TextBlock.ContextMenu>
                        <ContextMenu>
                            <MenuItem Command="Copy">
                                <MenuItem.CommandBindings>
                                    <CommandBinding Command="Copy"
                                                    Executed="MenuCopyLog_Executed"
                                                    CanExecute="CopyLog_CanExecute"/>
                                </MenuItem.CommandBindings>
                            </MenuItem>
                        </ContextMenu>
                    </TextBlock.ContextMenu>
                </TextBlock>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    

    KeyCopyLog_Executed 在哪里:

    private void KeyCopyLog_Executed(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
    {
        if ((sender as ListBox).SelectedItem != null)
        {
            LogItem item = (LogItem) (sender as ListBox).SelectedItem;
            Clipboard.SetData("Text", item.ToString());
        }
    }
    

    MenuCopyLog_Executed 是:

    private void MenuCopyLog_Executed(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
    {
        LogItem item = (LogItem) ((sender as MenuItem).TemplatedParent as ContentPresenter).DataContext;
        Clipboard.SetData("Text", item.ToString());
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-22
      • 2013-06-10
      • 2012-06-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-24
      • 2011-06-29
      相关资源
      最近更新 更多