wpfdatagridkeyboardxceedkeyboard-navigation

Xceed DataGrid: Edit cell with keyboard navigation


I have a datagrid, which uses a DataTemplateSelector to display different editable values as either a textbox, a combobox or a slider. The problem is that I can tab to these cells, but have no way of editing them with the keyboard. I had imagined that once the cell was focused I'd be able to edit the item inside.

Example: Tab to textbox-cell > start typing Example: Tab to slider-cell > is now focused > use arrow keys to edit

As it is now I have to click with the mouse to start editing. There is no way to begin editing using just the keyboard.

Here's my code:

    <UserControl.Resources>

    <!--#region DataTemplateSelector-->
    <local:SettingsDataTemplateSelector x:Key="SettingsDataTemplateSelector" />

    <DataTemplate x:Key="TextboxDataTemplate">
        <xcdg:MaskedTextBox IsTabStop="True" Mask="{Binding EditMask}" Text="{Binding EditValue, IsAsync=False, Mode=TwoWay, UpdateSourceTrigger=LostFocus, ValidatesOnExceptions=True}"/>
    </DataTemplate>

    <DataTemplate x:Key="ComboDataTemplate">
        <ComboBox IsTabStop="True"  ItemsSource="{Binding Path=SelectionValues}"
                                    SelectedValuePath="Value"
                                    SelectedValue="{Binding Path=SelectionValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                    DisplayMemberPath="ValueText">
        </ComboBox>
    </DataTemplate>

    <DataTemplate x:Key="SliderDataTemplate">
        <Slider IsTabStop="True" Value="{Binding EditSliderValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                    Minimum="{Binding MinRangeValue}" 
                    Maximum="{Binding MaxRangeValue}"
                    VerticalAlignment="Bottom" 
                    IsSnapToTickEnabled="True"
                    TickFrequency="1"
                    Margin="0,0,0,0"/>
    </DataTemplate>
    <!--#endregion-->

    <xcdg:DataGridCollectionViewSource x:Key="Features" 
                                        Source ="{Binding Path=Demo.Features}"
                                        AutoFilterMode="And"
                                        AutoCreateDetailDescriptions="False" 
                                        AutoCreateItemProperties="False">
        <xcdg:DataGridCollectionViewSource.DetailDescriptions>
            <xcdg:PropertyDetailDescription RelationName="Settings" AutoCreateDetailDescriptions="False" AutoCreateItemProperties="False"/>
        </xcdg:DataGridCollectionViewSource.DetailDescriptions>
    </xcdg:DataGridCollectionViewSource>
</UserControl.Resources>

<Grid>
    <!--#region Xceed DataGrid-->
    <xcdg:DataGridControl x:Name="datagrid"
                          ItemsSource="{Binding Source={StaticResource Features}}"
                          KeyUp="DatagridKeyUp"
                          AllowDetailToggle="True" 
                          Margin="10"
                          NavigationBehavior="RowOrCell" 
                          CellEditorDisplayConditions="RowIsBeingEdited, 
                          MouseOverCell, MouseOverRow, RowIsCurrent, CellIsCurrent" 
                          EditTriggers="BeginEditCommand, ClickOnCurrentCell, 
                          SingleClick, CellIsCurrent, ActivationGesture, RowIsCurrent"
                          ItemScrollingBehavior="Immediate"
                          AutoCreateColumns="False">

        <xcdg:DataGridControl.Resources>
            <Style TargetType="xcdg:TableViewScrollViewer">
                <Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
                <Setter Property="VerticalScrollBarVisibility" Value="Auto" />
            </Style>
        </xcdg:DataGridControl.Resources>

        <xcdg:DataGridControl.View>
            <xcdg:TableflowView UseDefaultHeadersFooters="False" ColumnStretchMode="Last">
                <xcdg:TableflowView.FixedHeaders>
                    <DataTemplate>
                        <xcdg:ColumnManagerRow />
                    </DataTemplate>
                </xcdg:TableflowView.FixedHeaders>
            </xcdg:TableflowView>
        </xcdg:DataGridControl.View>

        <xcdg:DataGridControl.Columns>
            <xcdg:Column FieldName="FeatureID" Title="FeatureID" ReadOnly="True" />
            <xcdg:Column FieldName="Name" Title="Feature name" ReadOnly="True" />
            <xcdg:Column FieldName="Description" Title="Description" ReadOnly="True" />
            <xcdg:Column FieldName=" "/>
        </xcdg:DataGridControl.Columns>

        <xcdg:DataGridControl.DetailConfigurations>
            <xcdg:DetailConfiguration RelationName="Settings" Title="">
                <xcdg:DetailConfiguration.Columns>
                    <xcdg:Column FieldName="Name" Title="Name" ReadOnly="True"/>
                    <xcdg:Column FieldName="Description" Title="Description" ReadOnly="True"/>
                    <xcdg:Column FieldName="EditValues" Title="Edit Values" ReadOnly="True"/>
                    <xcdg:Column FieldName="EditValueVar" Title="Edit Value" Width="150" ReadOnly="False"
                                 CellContentTemplateSelector="{StaticResource SettingsDataTemplateSelector}"
                                 DisplayMemberBinding="{Binding}" />
                    <xcdg:Column FieldName=" "/>
                </xcdg:DetailConfiguration.Columns>
            </xcdg:DetailConfiguration>
        </xcdg:DataGridControl.DetailConfigurations>
    </xcdg:DataGridControl>
    <!--#endregion-->
</Grid>

Any help is appreciated.


Solution

  • I ended up using Xceed's CellEditors. There seems to be no way around them, if you want to edit using the keyboard.

    Change the EditValueVar column to this:

                        <xcdg:Column FieldName="EditValueVar" Title="Edit Value" Width="150" ReadOnly="False"
                                 CellEditorSelector="{StaticResource SettingsCellEditorSelector}"/>
    

    NOTE: Removed DisplayMemberBinding as it can cause issues with dynamic data

    And here are the CellEditors I use now:

        <xcdg:CellEditor x:Key="maskEditor">
        <xcdg:CellEditor.EditTemplate>
            <DataTemplate>
                <xcdg:MaskedTextBox Mask="{Binding EditMask}"
                                    Text="{Binding Path=(xcdg:Cell.ParentCell).DataContext.EditValue, 
                                                   RelativeSource={RelativeSource Self}, 
                                                   IsAsync=False, Mode=TwoWay, UpdateSourceTrigger=LostFocus, ValidatesOnExceptions=True}"
                                    Style="{StaticResource {x:Type xcdg:MaskedTextBox}}"/>
                <!--Text="{Binding EditValueText, IsAsync=False, Mode=TwoWay, UpdateSourceTrigger=LostFocus, ValidatesOnExceptions=True}"/>-->
            </DataTemplate>
        </xcdg:CellEditor.EditTemplate>
        <xcdg:CellEditor.ActivationGestures>
            <xcdg:TextInputActivationGesture />
        </xcdg:CellEditor.ActivationGestures>
    </xcdg:CellEditor>
    
    <xcdg:CellEditor x:Key="comboEditor">
        <xcdg:CellEditor.EditTemplate>
            <DataTemplate>
                <ComboBox ItemsSource="{Binding Path=(xcdg:Cell.ParentCell).DataContext.SelectionValues, 
                                                RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged}"
                          SelectedValuePath="Value"
                          SelectedValue="{Binding Path=(xcdg:Cell.ParentCell).DataContext.SelectionValue,
                                                  RelativeSource={RelativeSource Self}, 
                                                  UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                          DisplayMemberPath="ValueText"
                          Style="{StaticResource {x:Type ComboBox}}"
                          ItemContainerStyle="{StaticResource {x:Type ComboBoxItem}}"/>
            </DataTemplate>
        </xcdg:CellEditor.EditTemplate>
        <xcdg:CellEditor.ActivationGestures>
            <xcdg:KeyActivationGesture SystemKey="Down"
                                       Modifiers="Alt" />
            <xcdg:KeyActivationGesture Key="Up"
                                       Modifiers="Alt" />
            <xcdg:KeyActivationGesture Key="F4" />
            <xcdg:KeyActivationGesture Key="Space" />
        </xcdg:CellEditor.ActivationGestures>
    </xcdg:CellEditor>
    
    <xcdg:CellEditor x:Key="sliderEditor">
        <xcdg:CellEditor.EditTemplate>
            <DataTemplate>
                <Slider Value="{Binding Path=(xcdg:Cell.ParentCell).DataContext.EditSliderValue, 
                                RelativeSource={RelativeSource Self}, 
                                Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                        Minimum="{Binding Path=(xcdg:Cell.ParentCell).DataContext.MinRangeValue, RelativeSource={RelativeSource Self}}"
                        Maximum="{Binding Path=(xcdg:Cell.ParentCell).DataContext.MaxRangeValue, RelativeSource={RelativeSource Self}}"
                        Style="{StaticResource {x:Type Slider}}"
                        VerticalAlignment="Bottom" 
                        IsSnapToTickEnabled="True"
                        TickFrequency="1"
                        Margin="0,0,0,0"/>
            </DataTemplate>
        </xcdg:CellEditor.EditTemplate>
        <xcdg:CellEditor.ActivationGestures>
            <xcdg:KeyActivationGesture SystemKey="Right"
                                    Modifiers="Alt" />
            <xcdg:KeyActivationGesture Key="Left"
                                    Modifiers="Alt" />
            <xcdg:KeyActivationGesture Key="F4" />
            <xcdg:KeyActivationGesture Key="Space" />
        </xcdg:CellEditor.ActivationGestures>
    </xcdg:CellEditor>
    

    You might notice the weird (xcdg:Cell.ParentCell)-stuff going on and yes, it's ugly as sin, but this is the legit way of doing it. You might have better luck just using xcdg:CellEditorBinding instead.

    xcdg:CellEditorBinding is the equivalent of binding like this: {Binding Path=(xcdg:Cell.ParentCell).Content, Mode=TwoWays, RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged}.

    I added .DataContext before content, because of how my data is structured.

    Hopefully this helps someone!