I've got a bit of a curly problem that I cannot find any complete and definitive information on...
My requirement is very simple: I want to apply a KeyBinding to a DataGridTemplateColumn. The purpose of this is to execute a command when the Delete key is hit for that column - a different command is executed if Delete is hit anywhere else on the grid. Both Delete commands exist in the VM of the grid.
While I could have a single keybinding defined at the grid level and then in the command test which column the active cell belongs to, this is a messy solution because:
that doesn't scale well when the grid is used on several different pages
the column in question is defined as a resource in a ResourceDictionary
Besides, I've seen nothing that indicates that this isn't possible... yet it eludes me.
I believe my problem is because focus is not automatically applied to the CellTemplate when that cell becomes the grid's current cell. There is a focus rectangle on the cell, but no focus events get triggered inside the cell (I've checked this). While readonly controls like TextBlocks can receive focus, if they don't then the keybinding cannot be triggered. The commands for the keybinding are correctly bound, so binding is not an issue.
The grid is defined like this:
<DataGrid x:Name="MyGrid"
ItemsSource="{Binding Path=FilteredSortedData}"
>
<DataGrid.Columns>
<StaticResource ResourceKey="StatusColumn" />
<StaticResource ResourceKey="MySpecialDeleteColumn" />
<StaticResource ResourceKey="...etc..." />
</DataGrid.Columns>
</DataGrid>
Then the column:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
x:Class="MyNamespace.GridColumns"
>
<FrameworkElement x:Key="GridColProxy" Name="GridColProxy" DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, Mode=OneWay}" />
<DataGridTemplateColumn x:Key="MySpecialDeleteColumn"
x:Shared="False"
Header="My Special Delete Column"
IsReadOnly="False"
>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel >
<!-- keybinding doesn't work at this level -->
<StackPanel.Style >
<Style TargetType="StackPanel">
<Setter Property="localBehaviours:InputBindingsBehaviour.AttachedKeyBindings">
<Setter.Value>
<localBehaviours:KeyBindingObservable >
<KeyBinding Key="Delete" Command="{Binding Path=DataContext.DeleteItemsCommand, Source={StaticResource GridColProxy}, Mode=OneWay, NotifyOnTargetUpdated=True}" />
</localBehaviours:KeyBindingObservable>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Style>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding SomeProperty}"
>
<TextBlock.Style>
<Style TargetType="TextBlock" >
<Style.Triggers>
<DataTrigger Binding="{Binding Path=MySpecialFlag, FallbackValue=false}" Value="True">
<Setter Property="localBehaviours:InputBindingsBehaviour.AttachedKeyBindings">
<Setter.Value>
<localBehaviours:KeyBindingObservable >
<KeyBinding Key="Delete" Command="{Binding Path=DataContext.DeleteItemsCommand, Source={StaticResource GridColProxy}, Mode=OneWay, NotifyOnTargetUpdated=True}" />
</localBehaviours:KeyBindingObservable>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Image Source="{Binding ...}"
Grid.Column="1"
/>
</Grid>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}" >
<Style.Triggers>
<!-- placing the keybinding here to apply to the grid cell doesn't have any effect... -->
<DataTrigger Binding="{Binding Path=MySpecialFlag, FallbackValue=false}" Value="True">
<Setter Property="localBehaviours:InputBindingsBehaviour.AttachedKeyBindings">
<Setter.Value>
<localBehaviours:KeyBindingObservable >
<KeyBinding Key="Delete" Command="{Binding Path=DataContext.DeleteItemsCommand, Source={StaticResource GridColProxy}, Mode=OneWay, NotifyOnTargetUpdated=True}" />
</localBehaviours:KeyBindingObservable>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</ResourceDictionary>
Notes:
ignore the
localBehaviours:KeyBindingObservable
, it is simply an attached property to assist with intercepting and inspecting keybindings before assigning them to theInputBindings
property of the attached control. This works great, there is no issue here.I have shown every place I have tried the keybindings, none of them have worked (been triggered). The DataTrigger bindings work correctly, so just ignore those.
CellEditingTemplate has been omitted for brevity
So the question I have is:
- how can I force the CellTemplate items to receive (keyboard) focus, preferably with XAML? Or is there a way to apply the keybinding at cell level so that it works regardless of the template being shown?
If a XAML solution is not possible then a small amount of event triggered (PreviewKeyDown, etc) code in the code behind of the ResourceDictionary could be used.