开发者

C+Ctrl KeyBinding is not causing copying to happen

I have set up a ListBox like so:

<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"
                                  开发者_如何学C  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>

As you can see, in the template, each TextBlock has a context menu that allows the user to copy the text. This works.

Also in the TextBlock there is an KeyBinding to ctrl+c and a CommandBinding to copy. When I press ctrl+c the method KeyCopyLog_Executed is not executed. I've checked with the debugger.

How should I be binding the keys to the TextBlock?


There are a couple of problems here.

First of all, KeyBindings will work only if currently focused element is located inside the element where KeyBindings are defined. In your case you have a ListBoxItem focused, but the KeyBindings are defined on the child element - TextBlock. So, defining a KeyBindings on a TextBlock will not work in any case, since a TextBlock cannot receive focus.

Second of all, you probably need to know what to copy, so you need to pass the currently selected log item as parameter to the Copy command.

Furthermore, if you define a ContextMenu on a TextBlock element it will be opened only if your right-click exactly on the TextBlock. If you click on any other part of the list item, it will not open. So, you need to define the ContextMenu on the list box item itself.

Considering all of that, what I believe you are trying to do can be done in the following way:

<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>

Here we define a KeyBinding on the ListBox itself and specify as a CommandParameter currently selected list box item (log entry).

CommandBinding is also defined at the ListBox level and it is a single binding for both right click menu and the keyboard shortcut.

The ContextMenu we define in the style for ListBoxItem and bind CommandParameter to the data item represented by this ListBoxItem (log entry).

The DataTemplate just declares a TextBlock with binding to current data item.

And finally, there is only one handler for the Copy command in the code-behind:

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;
}


Thanks to Pavlov Glazkov for explaining that the key and command bindings need to go at the ListBox level, rather than the item template level.

This is the solution I now have:

<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>

Where KeyCopyLog_Executed is:

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());
    }
}

and MenuCopyLog_Executed is:

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());
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜