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());
}
精彩评论