Tabbing from cell to cell does not set focus on control

Sep 12, 2008 at 2:07 PM
Hey All,

Good start on the grid. I am trying to figure out how I can tab from one cell to another and have the focus set on the edit control, inside the cell, when I tab into the cell. Right now it seems I have to tab twice to set focus on the control inside the cell. I tab once to get into the cell and another time to set the focus on the child control inside the cell. Am I doing something wrong?

Thanks!
Coordinator
Sep 12, 2008 at 10:44 PM
What type of columns have you created?  DataGridTextColumn should work on the first tab.  CheckboxColumn will be fixed for v1.  The template and hyperlink column are by design as they are composed of a set of visuals.  You can however customize the focus by attaching to DataGrid.PreparingCellForEdit() and setting the focus on the particular child element that you want.
Sep 13, 2008 at 12:35 PM
Edited Sep 15, 2008 at 3:02 PM

These are DataTemplate cells I am working with. I will try to use the DataGrid.PreparingCellForEdit() you mentioned.

Sep 15, 2008 at 3:06 PM
DataGrid.PreparingCellForEdit()  does not seem to help the issue. This event only seems to fire when I double click into the cell. What I am trying to do is tab from one cell to the next. When I tab into the cell I need to set the focus on the control that I am editing data with inside the DataTemplate cell. For example I have a checkbox inside my data template. When I tab I want the Check box to get focus immediately. Make sense? Is there anyway to do this now? If not, it would be nice to have a property on DataTemplateColumns where we can specify the name of a control inside the template that gets focus when the cell gets focus (via a tab or click or whatever..)

Thanks!
Sep 16, 2008 at 11:50 AM
We finally got this to work. Well kinda..

We use the code (see below) to get the tab and shift+tab keys to walk us through the DataGrid cells while setting the focus to the control contained in DataTemplate Columns. Our grid is made up of all DataGridTemplateColumns. We may have not approached this in the correct manner but it is working for us, with two exceptions:

1) This solution only works if you have only one control set inside the content of the DataGridCell.
2) We have problems reading the DataGridCell.Content if the Content is a 3rd party control. For example, we are using controls from DevComponents (www.devcomponents.com). For example, we have a grid with three Columns (all DataTemplateColumn). The first column is a simple Wpf textbox. The 2nd column is the DevComponents DoubleInput control. The 3rd column is a simple Wpf textbox. When we tab from the 1st column cell to the 2nd column cell the DoubleInput control gets the focus as expected. However when we tab backwards (shift+tab) from the 3rd column to the 2nd column the focus is not set inside the doubleInput control. Tabbing back and forth from WPF Textbox to WPF Textbox, for example works fine.

Maybe its something we have not handled properly but when we put this problem to DevComponents their response was.

"Thank you very much for taking the time to contact us. I suggest reporting this issue to Microsoft since they are the only ones that can fix it really. The DoubleInput is really plain WPF control.."

It would be nice to have better solution here from the DataGrid team if possible. Or at least some better guideance on how to make this more robust. What would be really nice is for the DataTemplateColumn to have a property that takes the reference to a control inside the DataGridCell.Content that specifies which control will get fcous when the DataGridCell gets focus/ is selected. Or something of that nature.


 

/// <summary>

 

 

/// Used to handle the tabbing action between DataGrid cells

 

 

/// </summary>

 

 

/// <param name="sender"></param>

 

 

/// <param name="e"></param>

 

 

private void dgExposure_PreviewKeyDown(object sender, KeyEventArgs e)

 

{

 

if (!ComponentData.InputDeckObject.IsEditable) return;

 

 

if (e.Key != Key.Tab) return;

 

 

var currow = dgExposure.Items.IndexOf(dgExposure.CurrentCell.Item);

 

 

var curcol = dgExposure.CurrentCell.Column.DisplayIndex;

 

 

var cell = GetCell(currow, curcol);

 

 

if (cell == null) return;

 

 

if (!cell.IsFocused) cell.Focus();

 

 

if (!cell.IsSelected) cell.IsSelected = true;

 

 

if (!cell.IsEditing) cell.IsEditing = true;

 

 

 

if(Keyboard.Modifiers == ModifierKeys.Shift)

 

{

SetNextEditCell(cell,

FocusNavigationDirection.Left);

 

}

 

else

 

{

SetNextEditCell(cell,

FocusNavigationDirection.Right);

 

}

}


 

private void SetNextEditCell(DataGridCell cell, FocusNavigationDirection focusNavigationDirection)

 

{

 

var nextFocusedCell = cell.PredictFocus(focusNavigationDirection) as DataGridCell;

 

 

if (nextFocusedCell == null) return;

 

 

/*

 

BUG There seems to exist a problem in the interaction between the DevComponents DoubleInput Control

and the MS DataGrid Cell Content control. Further examination is needed. For now Shift+Tab from

Calculation Type back to the Exposure control does not set focus in the DoubleInput control. This is a known bug.

The reason it works when the Tab key it hit is that the focus is never put on the Cell with the DoubleInput Control in that case.

*/

 

var nextFocusedControl = nextFocusedCell.Content as Control;

 

 

if (nextFocusedControl == null) return;

 

 

if (nextFocusedControl.IsEnabled) //Check to see if the control inside the DataGridCell is enabled. If so we set focus. If not we go to next DataGridCell

 

{

 

if (focusNavigationDirection == FocusNavigationDirection.Right)

 

{

 

//Gets you to next in tab order

 

cell.MoveFocus(

new TraversalRequest(FocusNavigationDirection.Next));

 

}

 

else if (focusNavigationDirection == FocusNavigationDirection.Left)

 

{

 

//Gets you to previous cell

 

cell.MoveFocus(

new TraversalRequest(FocusNavigationDirection.Previous));

 

 

//Gets you to next in tab order which should be the control

 

nextFocusedControl.MoveFocus(

new TraversalRequest(FocusNavigationDirection.Next));

 

}

}

 

else //Control Inside DataGridCell was not enabled so we move to the next cell

 

{

SetNextEditCell(nextFocusedCell, focusNavigationDirection);

}

}



Coordinator
Sep 17, 2008 at 11:35 PM
Edited Sep 18, 2008 at 12:00 AM
I've been talking it over with a colleague and here is a solution that we thought about.  I haven't actually tested it out yet but from looking at the code I think it should work.  Marco Zhou has this custom implementation on how to set focus to a particular UIElement in a DataTemplate of a ListViewItem, http://social.msdn.microsoft.com/forums/en-US/wpf/thread/98d8423c-9719-4291-94e2-c5bf3d80cd46/.  You can do something similar except listen to the DataGrid.CurrentCellChanged event.  In that event you can get access to the Cell's Content and set the focus on whatever particular derived UIElement you want in the Cell's Content.  By taking this route, you don't have to worry about all the housekeeping that you are doing in the PreviewKeyDown event.  Let me know if this helps.
Sep 22, 2008 at 7:45 PM
We actually tried this before deciding on the implementation above. The problems are as follows (see code at end):

1) This code runs anytime the windows gets focus ( i guess due to virtualization ore repainting). At any rate it makes it hard to debug.
2) The UIElement never receives focus. The cell gets the focus and does not seem to honor the code below that specifies going into the cell's content.


Code:

 

private void dgExposure_CurrentCellChanged(object sender, EventArgs e)

 

{

 

DataGridCell dgc = sender as DataGridCell;

 

 

if (dgc == null) return;

 

 

UIElement uiElem = dgc.Content as UIElement;

 

 

if (uiElem == null) return;

 

 

if (uiElem.IsEnabled) uiElem.Focus();

 

}

Coordinator
Sep 22, 2008 at 11:35 PM
I just tried something along these lines and it seemed to work fine for me:

void

 

DataGrid_Standard_CurrentCellChanged(object sender, EventArgs e)
{
    DataGrid dg = sender as DataGrid;
    if (dg.CurrentColumn is DataGridTemplateColumn)
    {
        DataGridCell cell = GetCell(dg.Items.IndexOf(dg.CurrentCell.Item), dg.Columns.IndexOf(dg.CurrentCell.Column));
        Panel panel = cell.Content as Panel;
        TextBox tb = GetVisualChild<TextBox>(panel);
        tb.Focus();
    }
}

 

<

 

dg:DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <StackPanel Orientation="Horizontal">
            <Image Focusable="True" Source="{Binding Path=Picture}" />
            <CheckBox Content="Test"/>
            <TextBox Focusable="True" Name="tb_Picture" Text="{Binding Path=Picture}" />
        </StackPanel>
    </DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>

 

Sep 22, 2008 at 11:40 PM
Edited Sep 23, 2008 at 10:13 AM

Thanks!

I’ll give it a shot..

From: vinsibal [mailto:notifications@codeplex.com]
Sent: Monday, September 22, 2008 7:35 PM
To: Charles Laymon
Subject: Re: Tabbing from cell to cell does not set focus on control [wpf:35540]

 

From: vinsibal

I just tried something along these lines and it seemed to work fine for me:

void

DataGrid_Standard_CurrentCellChanged(object sender, EventArgs e)
{
DataGrid dg = sender as DataGrid;
if (dg.CurrentColumn is DataGridTemplateColumn)
{
DataGridCell cell = GetCell(dg.Items.IndexOf(dg.CurrentCell.Item), dg.Columns.IndexOf(dg.CurrentCell.Column));
Panel panel = cell.Content as Panel;
TextBox tb = GetVisualChild<TextBox>(panel);
tb.Focus();
}
}

dg:DataGridTemplateColumn.CellTemplate>
<
DataTemplate>
<
StackPanel Orientation="Horizontal">
<
Image Focusable="True" Source="{Binding Path=Picture}" />
<
CheckBox Content="Test"/>
<
TextBox Focusable="True" Name="tb_Picture" Text="{Binding Path=Picture}" />
</
StackPanel>
</
DataTemplate>
</
dg:DataGridTemplateColumn.CellTemplate>

Read the full discussion online.

To add a post to this discussion, reply to this email (wpf@discussions.codeplex.com)

To start a new discussion for this project, email wpf@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com

Sep 23, 2008 at 11:29 AM

Ok, the following code seems to work ok while tabbing forward in the grid. It still has issues becuase it depends on where the focus starts. For example if the focus starts on the DataGridCell the TAB key simply sets focus on the next element in the focus chain, which in this case is the control inside the current DataGridCell. The next tab should place the focus on the next DataGridCell and thus kick off the code below. However it is not consistent. Also, it does not seem to handle going backwards (SHIFT + TAB). What happens in SHIFT + TAB is that focus leaves the control inside the DataGridCell and sets focus on the current DataGridCell. So you need a way to jump back to the next cell to get the code below to fire. At least this is the behavior I seem to be getting. The original way we mentioned is not really perfect but it does seem to handle TAB and SHIFT+TAB. Any suggestions?

In a future release of the Grid can you make this whole process more intuitive? It seems that what we need is a way to navigate from DataGridCell to DataGridCell, not using the MoveFocus method. Also, it would be nice to be able to have a property that we can designate which control gets focus when the DataGridCell is selected, no matter how it is selected... Hope this makes some sense.

 

 

 

 

private void dgExposure_CurrentCellChanged(object sender, EventArgs e)

 

{

 

var dg = sender as DataGrid;

 

 

if (dg == null) return;

 

 

if (!(dg.CurrentColumn is DataGridTemplateColumn)) return;

 

 

var cell = GetCell(dg.Items.IndexOf(dg.CurrentCell.Item), dg.Columns.IndexOf(dg.CurrentCell.Column));

 

 

var control = cell.Content as Control;

 

 

if (control == null) return;

 

 

if(control.IsEnabled && control.IsTabStop)

 

{

control.Focus();

}

 

else

 

 

 

{

cell.MoveFocus(

new TraversalRequest(FocusNavigationDirection.Next));

 

}

}

 

 

 

Sep 23, 2008 at 2:58 PM
Hello MrCharles, Vinsibal,

I'm having a similar focus problem.  In my case I have an overridden textbox, and my problem seems to be that the keyboard does not loose its focus when tabbing to the next cell, nor when exiting the cell editing template.

However, currently I am just trying to follow this article I'm embarrassed to say I can't find the GetCell method.  Would you kindly point out where I can find that method?  Its rather core to obtaining the visual content.

Best regards, Adam.
Coordinator
Sep 24, 2008 at 6:47 PM
This thread has an example implementation of GetCell, http://www.codeplex.com/wpf/Thread/View.aspx?ThreadId=34065.
Coordinator
Sep 26, 2008 at 2:48 PM
MrCharles,

Instead of using the CurrentCellChanged to set focus on a particular element (as I previously proposed), you can use the KeyboardNavigation APIs for each cell.  The reason that this will work is b/c DataGrid sets the tab scope to each cell.  So you can do something along these lines and tab will move to the inner element then to the next cell:

                    <dg:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <Image KeyboardNavigation.IsTabStop="False" Source="{Binding Path=Picture}" />
                                <CheckBox KeyboardNavigation.IsTabStop="False" Content="Test"/>
                                <TextBox TabIndex="1" Name="tb_Picture" Text="{Binding Path=Picture}" />
                                <CheckBox KeyboardNavigation.IsTabStop="False" Content="Test"/>
                            </StackPanel>
                        </DataTemplate>
                    </dg:DataGridTemplateColumn.CellTemplate>

Compared to your proposed solution where you only tab once more, here you can set the tab index to any element within the cell as well as change the ordering if you want.  This also solves the problem of where focus starts.  If you set focus to this particular cell, another tab will take you to the next cell instead of the next element in the cell. 

What this doesn't solve is the Shift + Tab problem.  If you try it out it still takes two Shift + Tabs to move from the cell to the previous cell.  For this case you will have to do something similar to your proposed solution.

Hope this helps. 

Jul 20, 2009 at 11:28 AM

I have stumbled on this problem - have any of you created a more generic solution to this ? The double tab happend on columns I add with this code: (NumberTextBox is just a textbox that filters out non numeric characters and "field" is a class describing the column)

 

                                DataGridTemplateColumn col = new DataGridTemplateColumn();
                                col.Header = field.Caption;

                                FrameworkElementFactory factory = new FrameworkElementFactory(typeof(NumberTextBox));
                                System.Windows.Data.Binding b = new System.Windows.Data.Binding(field.Name);
                                b.Converter = new DecimalConverter();
                                factory.SetValue(NumberTextBox.TextProperty, b);
                                factory.SetValue(NumberTextBox.TextAlignmentProperty, TextAlignment.Right);
                                
                                factory.SetValue(KeyboardNavigation.IsTabStopProperty, true);
                                factory.SetValue(TextBox.TabIndexProperty, 1);
                                DataTemplate cellEditingTemplate = new DataTemplate();
                                cellEditingTemplate.VisualTree = factory;                                
                                col.CellEditingTemplate = cellEditingTemplate;

                                FrameworkElementFactory factory2 = new FrameworkElementFactory(typeof(TextBlock));
                                System.Windows.Data.Binding b2 = new System.Windows.Data.Binding(field.Name);
                                b2.Converter = new DecimalConverter();
                                factory2.SetValue(TextBlock.TextProperty, b2);
                                factory2.SetValue(TextBlock.TextAlignmentProperty, TextAlignment.Right);
                                DataTemplate cellTemplate = new DataTemplate();
                                cellTemplate.VisualTree = factory2;
                                col.CellTemplate = cellTemplate;

                                col.IsReadOnly = field.ReadOnly;
                       
                                dg.Columns.Add(col);

 

Any suggestions ?

 

/Erik

Aug 21, 2009 at 7:24 PM

Erik

I found solution for myself, i hope you will find it useful.

http://blog.yalovoi.net/2009/08/21/wpf-datagrid-tabbing-from-cell-to-cell-does-not-set-focus-on-control/