14
votes

I have a datagrid in WPF with a DataGridTextColum and a DataGridTemplateColum.

<DataGridTextColumn Width="4*" IsReadOnly="True" x:Name="dataGridColumnDescription" 
Header="Description" Binding="{Binding Description}">
</DataGridTextColumn>

<DataGridTemplateColumn CellStyle="{StaticResource CellEditing}" IsReadOnly="False" Width="*" Header="Value" 
CellEditingTemplateSelector="{StaticResource myCellEditingTemplateSelectorValue}" 
CellTemplateSelector="{StaticResource myCellTemplateSelectorValue}">
</DataGridTemplateColumn>

The CellTemplateSelectors return a DataTemplate with a TextBlock for the the Celltemplate resp. a TextBox for CellEditing!

<DataTemplate x:Key="dGridStringValueTemplate">
    <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Path=Value}"/>
</DataTemplate>

<DataTemplate x:Key="dGridStringValueTemplateEditing">
    <TextBox TextAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" BorderThickness="1" Text="{Binding Path=Value, UpdateSourceTrigger=LostFocus}"/>
</DataTemplate>

Now I want to automatically Focus the TextBox when the DataGridCell gets the focus. The user should be able to edit the TextBox content without doubleclicking the cell.

I found this article:

DataGrid Tips & Tricks: Single-Click Editing where I can get the Current DataGridCell, but how can I access the content to give the Textbox the focus to edit the content?

This is my style:

<Style x:Key="CellEditing" TargetType="{x:Type DataGridCell}">
    <EventSetter Event="PreviewMouseLeftButtonDown" Handler="myDataGridMain_PreviewMouseLeftButtonDown"></EventSetter>
</Style>

This is my event handler:

private void myDataGridMain_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    DataGridCell cell = sender as DataGridCell;     // cell ist not null

    DataGridTemplateColumn col = cell.Column as DataGridTemplateColumn; //col is not null

    DataTemplate template = col.CellTemplate;  //this is null
}

How can I get the textbox with that event handler?

5
When you want your grid to be editable all the time why you want editing template? just add cell template with textbox in it and change style of textbox itself this way u dont have to write focus stuffWPFKK
@WPFKK That wouldn't work since the datagrid wouldn't switch to "edit mode", therefore the input value wouldn't be bound to source.Rémy Esmery

5 Answers

18
votes

This seems to work :

    <DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellEditingTemplate>
                    <DataTemplate>
                        <TextBox  FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"></TextBox>
                    </DataTemplate>
                </DataGridTemplateColumn.CellEditingTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
3
votes

This approach works for me. It uses the fact that the DataGrid will always create a new instance of the template when the editing starts:

<DataGridTemplateColumn.CellEditingTemplate>
    <DataTemplate>
        <TextBox Text="{Binding MyProperty}" 
                 Loaded="TextBox_Loaded"></TextBox>
    </DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>

and in the code behind:

private void TextBox_Loaded(object sender, RoutedEventArgs e)
{
    ((TextBox)sender).Focus();
    ((TextBox)sender).SelectAll();
}

As an added bonus, it also selects all text in the cell. It should work no matter how you enter the editing mode (double click, single click, pressing F2)

1
votes

I managed it, not the best solution but it works... When Cell gets focus I set it to editing mode.

private void myDataGridMain_OnFocus(object sender, RoutedEventArgs e)
{
    DataGridCell cell = sender as DataGridCell;
    if (cell != null)
        cell.IsEditing = true;
    //var test = FindVisualChild<TextBlock>(cell);
}

On Keydown I search for the visual child and give the focus.

private void myDataGridMain_KeyDown(object sender, KeyEventArgs e)
        {
            DataGridCell cell = sender as DataGridCell;

            if (e.Key == Key.Enter)
            {   //give cell the focus
                cell.Focus();
            }
            else
            {
                if ((cell != null))
                {
                    TextBox textbox = FindVisualChild<TextBox>(cell);
                    if (textbox != null)
                    {   //TextBox has benn found
                        if ((textbox as TextBox).IsFocused == false)
                        {
                            (textbox as TextBox).SelectAll();
                        }
                        (textbox as TextBox).Focus();
                    }

                    CheckBox chkbox = FindVisualChild<CheckBox>(cell);
                    if (chkbox != null)
                    {   //Checkbox has been found
                        (chkbox as CheckBox).Focus();
                    }

                    ComboBox combbox = FindVisualChild<ComboBox>(cell);
                    if (combbox != null)
                    {   //ComboBox has been found
                        (combbox as ComboBox).Focus();
                    }
                }
            }
        }

Find Visual Child!

public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child != null && child is T)
            return (T)child;
        else
        {
            T childOfChild = FindVisualChild<T>(child);
            if (childOfChild != null)
                return childOfChild;
        }
    }
    return null;
}
0
votes

the simple answer for that create new control derived from datagrid control

  using System.Windows.Controls;

   public class CustomDataGrid : DataGrid
   {

    protected override void OnSelectedCellsChanged(SelectedCellsChangedEventArgs e)
    {
        //to make sure cell is selected
        var cells = e.AddedCells.FirstOrDefault();
        if (cells != null)
        {
            this.BeginEdit();

        }
        base.OnSelectedCellsChanged(e);
    }

   }
0
votes

Hisham's suggestion works perfectly for me, but I would use OnCurrentCellChanged instead, since OnSelectedCellsChanged will not work when the SelectionUnit is CellOrRowHeader. In the latter case BeginEdit() would only be triggered when selection moves to a cell in another row. Stepping left or right will not trigger the event at all.

Also, it is probably advisable to add a DependencyProperty to the custom control and check against it before triggering BeginEdit(), to prevent this behavior if so desired (as done by other DataGrids, such as XCeed). But this is not a critic - just something that I usually do.

    protected override void OnCurrentCellChanged(EventArgs e)
    {
        // Make sure a cell is selected and only enter edit mode
        // if this is the desired behavior 
        if (CurrentCell != null && EditTrigger == EditTriggers.CellsCurrent)
        {
            this.BeginEdit();
        }
        base.OnCurrentCellChanged(e);
    }