WPF DataGrid: Tabbing from cell to cell does not set focus on control

August 21, 2009

When DataGrid was introduced  into WPF, I was happy. I really lack this control. As many other developers I found one drawback of new control. When you use DataGridTemplateColumn, it doesn’t set focus automatically on control inside DataGridTemplateColumn.CellEditingTemplate. This requires from you to click 3 times to edit the cell or press tab twice. It is very annoying. Discussion of this bug and possible solutions was made at code plex http://www.codeplex.com/Thread/View.aspx?ProjectName=wpf&ThreadId=35540 . I found my own solution. When you start to edit cell, wpf creates controls for data edit, like textboxes. So we need to transfer focus to them immediately. I created attacher class:

public class FocusAttacher
    {

        public static readonly DependencyProperty FocusProperty = DependencyProperty.RegisterAttached("Focus", typeof(bool), typeof(FocusAttacher), new PropertyMetadata(false, FocusChanged));

        public static bool GetFocus(DependencyObject d)
        {
            return (bool)d.GetValue(FocusProperty);
        }

        public static void SetFocus(DependencyObject d, bool value)
        {
            d.SetValue(FocusProperty, value);
        }

        private static void FocusChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue)
            {
                ((UIElement)sender).Focus();
            }
        }

    }

Public Class FocusAttacher

Public Shared ReadOnly FocusProperty As DependencyProperty = DependencyProperty.RegisterAttached(“Focus”, GetType(Boolean), GetType(FocusAttacher), New PropertyMetadata(False, New PropertyChangedCallback(AddressOf FocusChanged)))

    Public Shared Function GetFocus(ByVal d As DependencyObject) As Boolean
        Return CBool(d.GetValue(FocusProperty))
    End Function

    Public Shared Sub SetFocus(ByVal d As DependencyObject, ByVal value As Boolean)
        d.SetValue(FocusProperty, value)
    End Sub

    Private Shared Sub FocusChanged(ByVal sender As Object, ByVal e As DependencyPropertyChangedEventArgs)
        If CBool(e.NewValue) Then
            DirectCast(sender, UIElement).Focus()
        End If
    End Sub

End Class

As you can see, this class can be attached to any UIElement and calls focus immediately.
You can use it like this:

      <tk:DataGrid CanUserAddRows="True" ItemsSource="{Binding}" AutoGenerateColumns="False">
            <tk:DataGrid.Columns>
                <tk:DataGridTemplateColumn Header="Name">
                    <tk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Padding="3" Text="{Binding Name}" />
                        </DataTemplate>
                    </tk:DataGridTemplateColumn.CellTemplate>
                    <tk:DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <TextBox Text="{Binding Path=Name, Mode=OneWay}" lc:FocusAttacher.Focus="True" />
                        </DataTemplate>
                    </tk:DataGridTemplateColumn.CellEditingTemplate>
                </tk:DataGridTemplateColumn>
                <tk:DataGridTemplateColumn Header="Phone">
                    <tk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path=Phone}" Padding="3"/>
                        </DataTemplate>
                    </tk:DataGridTemplateColumn.CellTemplate>
                    <tk:DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <TextBox Text="{Binding Path=Phone, Mode=OneWay}" lc:FocusAttacher.Focus="True" />
                        </DataTemplate>
                    </tk:DataGridTemplateColumn.CellEditingTemplate>
                </tk:DataGridTemplateColumn>
                <tk:DataGridTemplateColumn Header="Address">
                    <tk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path=Address}" Padding="3"/>
                        </DataTemplate>
                    </tk:DataGridTemplateColumn.CellTemplate>
                    <tk:DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <TextBox Text="{Binding Path=Address, Mode=OneWay}" lc:FocusAttacher.Focus="True" />
                        </DataTemplate>
                    </tk:DataGridTemplateColumn.CellEditingTemplate>
                </tk:DataGridTemplateColumn>
            </tk:DataGrid.Columns>
        </tk:DataGrid>

This completely solves the problem, when you start to edit cell, focus immediately goes to required control.

Sample projects which show such datagrid and attacher class in action is available .

Advertisement

6 Responses to “WPF DataGrid: Tabbing from cell to cell does not set focus on control”

  1. Andreas said

    Thanks, that certainly solved the issue for me. There is however a little bit missing in the VB version of the code. The FocusProperty declaration should be:

    Public Shared ReadOnly FocusProperty As DependencyProperty = DependencyProperty.RegisterAttached(“Focus”, GetType(Boolean), GetType(FocusAttacher), New PropertyMetadata(False, New PropertyChangedCallback(AddressOf FocusChanged)))

  2. Clark said

    How do you do ti, blog.yalovoi.net?

    http://eftgentleness.blogspot.com/

  3. Andrew said

    Thanks!

  4. Jonathan Allen said

    Thank you, that’s really cool.

    Do you happen to know how to automatically switch into editing mode when the user types in the cell? We get this for free with DataGridTextColumn but not for DataGridTemplateColumn.

  5. Alessandro said

    Help me! The link of Sample projects don’t work…
    I try to meke a simple progect but my solution don’t work.. plese explane me where i have to add a class for see the property in the xaml.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.