1

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 the InputBindings 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.

slugster
  • 49,403
  • 14
  • 95
  • 145
  • Have you tried using the triggers in the `DataTemplate`? i.e. ` ` – XAMlMAX Mar 15 '16 at 14:39
  • If it's for that column only is it safe to assume they'd be editing a cell in that column for the key to be available? In which case wouldn't it be applied to `CellEditingTemplate` instead? – Chris W. Mar 15 '16 at 15:03
  • @ChrisW.Yes they will be editing in that column, but I have some KeyDownPreview code behind which puts the cell into edit mode if alphanumeric keys are pressed. So I can do something programmatically in response to `Delete` being pressed (hence the comment after the question). In any case the keypreview is not marked as handled so other controls will still get it, the event is not stopped. – slugster Mar 15 '16 at 19:41
  • @XAMlMAX I did try that but it doesn't work because you are trying to apply the keybinding to a DataGridColumn, which doesn't inherit from UIElement therefore it doesn't have an InputBindings property. – slugster Mar 16 '16 at 12:01
  • I thought there was something not right, the `DataGridColumn` does not derives from `UIElement` but `DataGridColumnHeader` does. Maybe this will work as I don't want to believe that this functionality wouldn't be available without much work involved. – XAMlMAX Mar 16 '16 at 15:43

0 Answers0