How to programmatically select AND focus a cell in the new WPF Datagrid

Answered How to programmatically select AND focus a cell in the new WPF Datagrid

  • Friday, August 22, 2008 11:37 PM
     
     

    It's easy to select and focus a ListViewItem within a WPF ListView using the following code:

    ...
    myListView.SelectedItem = myListView.Items[index];
    myListView.ScrollIntoView(myListView.Items[index]);
    ListViewItem listViewItem = myListView.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
    listViewItem.Focus();
    ...

    How would you do exactly the same for the new WPF DataGrid, i.e. programmatically select a cell within the WPF DataGrid and then programmatically put the focus on that cell, so that you can then navigate through the DataGrid with the arrow keys on the keyboard? Part of the code for selecting would be:

    ...
    myDataGrid.SelectedItem = myDataGrid.Items[index];
    myDataGrid.ScrollIntoView(myDataGrid.Items[index]);
    ...

    However, what is the code for putting the focus on the selected WPF DataGrid Cell? Thanks for any help!

All Replies

  • Saturday, August 23, 2008 12:31 AM
     
     Answered
    Here is one way to set focus to a particular cell programmatically:
     
    DataGridCell cell = GetCell(rowIndex, colIndex);
    cell.Focus;

    See, http://forums.msdn.microsoft.com/en-US/wpf/thread/63974f4f-d9ee-45af-8499-42f29cbc22ae for the implementation of GetCell().
    • Marked As Answer by anbri Saturday, August 23, 2008 1:04 AM
    •  
  • Saturday, August 23, 2008 1:04 AM
     
     Answered
    Oh Great! Vincent, thank you very much for this solution that will also be helpful in other situations.

    Meanwhile, I also found another way to set the focus. Perhaps this is also helpful for somebody else:

    ...
    int index = 11;
    myDataGrid.SelectedItem = myDataGrid.Items[index];
    myDataGrid.ScrollIntoView(myDataGrid.Items[index]);
    DataGridRow dgrow = (DataGridRow
    )myDataGrid.ItemContainerGenerator.ContainerFromItem(myDataGrid.Items[index]);
    dgrow.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
    ...
    • Marked As Answer by anbri Saturday, August 23, 2008 1:04 AM
    • Unmarked As Answer by anbri Saturday, August 23, 2008 1:05 AM
    • Marked As Answer by anbri Saturday, August 23, 2008 1:05 AM
    •  
  • Monday, May 11, 2009 11:43 AM
     
     
    In one approach like it I must select and highlight the row in DataGrid.
    It doesn't function with dgrow.MoveFocus, but functions well with dgrow.IsSelected=True.
    But if any other rows where selected before, they stay selected.
    if you want to have only one row selected, you must do something like:
    1.     foreach (object _rowitem in MyDataGrid.Items) {
    2.         DataGridRow _visualItem = (DataGridRow)MyDataGrid.ItemContainerGenerator.ContainerFromItem(_rowitem);
    3.         _visualItem.IsSelected = false;
    One another problem: if ItemContainerGenerator returns Nothing. It can happen, if items where not really generated.
    In the case ItemContainerGenerator.Status=NotStarted. 
    For example, you create a new datagrid. It is not generated after InitializeComponent(), but only after DataGrid.Loaded event.
    Then you can call your selection routine in or after DataGrid.Loaded.
  • Thursday, June 04, 2009 2:51 PM
     
     
    I would like to navigate between the GridView Cells using directional keys. when user clicks on a cell and press the arrow keys then the focus has to be moved on to the next cell. I could find the Coordinates of the next cell, now with this coordinates I would like to set focus on that cell. Please help me with the code.
           

  • Tuesday, August 25, 2009 4:02 AM
     
     Proposed Answer Has Code
    I haven't had much luck the other ways, so i use this workaround

    //first focus the grid
    grid.Focus();
    //then create a new cell info, with the item we wish to edit and the column number of the cell we want in edit mode
    DataGridCellInfo cellInfo = new DataGridCellInfo(itemToSelect, grid.Columns[0]);
    //set the cell to be the active one
    grid.CurrentCell = cellInfo;
    //scroll the item into view
    grid.ScrollIntoView(itemToSelect);
    //begin the edit
    grid.BeginEdit();

    this works for me. (it would make a nice little static method too :)
    • Proposed As Answer by ddpommes Wednesday, February 02, 2011 9:26 AM
    •  
  • Monday, November 30, 2009 3:07 PM
     
     
    hello everyone!

    i have this probelm with programmically selecting a cell in WPF DataGrid too.
    i'm using .NET 4.0 Beta 2.

    i have MainWindow with DataGrid in it, for example such markup:

            <DataGrid
    Name="myDataGrid"
    AutoGenerateColumns="False"
    GridLinesVisibility="None"
    IsReadOnly="True"
    SelectionUnit="Cell"
    SelectionMode="Single">           
                <DataGrid.Columns>
                    <!-- some columns -->               
                </DataGrid.Columns>           
            </DataGrid>

    important thing is that in my DataGrid i need:

    SelectionUnit="Cell"
    SelectionMode="Single"

    all solutions i've found (including in this thread) not works,
    so i tried to do some stuff by myself, and this is what i got.

    i've experimented with various proerties of DataGrid, it's event handlers, etc.

    and finnaly, i looked at the property "myDataGrid.SelectedCells", in debug i find out,
    that if i select only one cell, in "myDataGrid.SelectedCells" will be 1 item of type
    "DataGridCellInfo", and it's "Column" proprty is not null!

    so when you working with individual cells (like in my case),
    all other properties like:
    SelectedIndex, SelectedItem, CurrentCell, CurrentColumn, Items.*, ItemContainerGenerator.*, etc.
    are useless, so you need to work with the prperty "SelectedCells" even if you are
    working with a single cell.

    so this is my solution wich i'm using now:

    public partial class MainWindow {
        private DataGridCellInfo[,] dataGridAllCells; //here will be stored all cells, displayed in DataGrid, as 2D matrix of cells
    ...
            public MainWindow() {
                InitializeComponent();

    //here goes important stuff, this needs to be done each time, "myDataGrid.ItemsSource" is changed and all cells are regenerated
                myDataGrid.ItemContainerGenerator.ItemsChanged += (sender, e) => {
            //init matrix of cells
            dataGridAllCells = new DataGridCellInfo[myDataGrid.Items.Count, myDataGrid.Columns.Count];
            //changing "SelectionUnit" to "CellOrRowHeader", so we can select all cells of our DataGrid
            myDataGrid.SelectionUnit = DataGridSelectionUnit.CellOrRowHeader;
            //selecting all cells
            myDataGrid.SelectAllCells();
            //getting all selected cells (with proper "Column" property)
            var allCellsArray = myDataGrid.SelectedCells.ToArray();//"ToArray()" is necessary, so we won't loose the cells during selection change
            //initialize items of our cell matrix
            for (int indexRow = 0, arrayCellsIndex = 0; indexRow < myDataGridt.Items.Count; indexRow++)           
                for (int indexColumn = 0; indexColumn < myDataGrid.Columns.Count; indexColumn++)
                    dataGridAllCells[indexRow, indexColumn] = allCellsArray[arrayCellsIndex++];
            //setting back "SelectionUnit"
            myDataGrid.SelectionUnit = DataGridSelectionUnit.Cell;
                };
        }
    ...
            private void MoveToUpperCell_Click(object sender, RoutedEventArgs e) {
                if (myDataGrid.SelectedCells.Count == 1) {
                    int indexRow = myDataGrid.Items.IndexOf(myDataGrid.SelectedCells[0].Item);//getting current selected row
                    int indexColumn = myDataGrid.Columns.IndexOf(myDataGrid.SelectedCells[0].Column);//getting current selected column
                    myDataGrid.SelectedCells.Clear();//clearing the selection
                    myDataGrid.SelectedCells.Add(dataGridAllCells[Math.Max(--indexRow, 0), indexColumn]);//and setting new selection we need
                }
    //selec some default cell if none is selected
                else myDataGrid.SelectedCells.Add(dataGridAllCells[0, 1]);
    //scroll to current selection
            myDataGrid.ScrollIntoView(myDataGrid.SelectedCells[0].Item);
            }

            private void MoveToLowerCell_Click(object sender, RoutedEventArgs e) {
                if (myDataGrid.SelectedCells.Count == 1) {
                    int indexRow = myDataGrid.Items.IndexOf(myDataGrid.SelectedCells[0].Item);
                    int indexColumn = myDataGrid.Columns.IndexOf(myDataGrid.SelectedCells[0].Column);
                    myDataGrid.SelectedCells.Clear();
                    myDataGrid.SelectedCells.Add(dataGridAllCells[Math.Min(++indexRow, myDataGrid.Items.Count - 1), indexColumn]);
                }
                else myDataGrid.SelectedCells.Add(dataGridAllCells[0, 1]);
            myDataGrid.ScrollIntoView(myDataGrid.SelectedCells[0].Item);       
            }
    ...
    }

    so in this way you can programmicaly change selected cell as you need.
    nya-nya!
  • Tuesday, March 02, 2010 1:04 AM
     
      Has Code
    When trying to implement the above in VB.Net 2008 with ToolKit with the following.  SetCellFocus is intended to set the focus to the designate row and column for the designated datagrid and allow input.

        Private Sub SetCellFocus(ByVal dgGrid As DataGrid, ByVal nRow As Integer, ByVal nCol As Integer)
            Dim dgCell As DataGridCell
            dgCell = GetCell(dgGrid, nRow, nCol)
            dgCell.Focus()
            dgGrid.BeginEdit()
        End Sub
    
        Private Function GetCell(ByRef dgGrid As DataGrid, ByVal nRow As Integer, ByVal nCol As Integer) As DataGridCell
    
            Dim dgRow As DataGridRow = GetRow(dgGrid, nRow)
            If dgRow IsNot Nothing Then
                Dim presenter As DataGridCellsPresenter
                presenter = GetVisualChild2(dgRow)
    
                Dim dgCell As DataGridCell
                dgCell = presenter.ItemContainerGenerator.ContainerFromIndex(nCol)
                If dgCell Is Nothing Then
                    dgGrid.ScrollIntoView(dgRow, dgGrid.Columns(nCol))
                    dgCell = presenter.ItemContainerGenerator.ContainerFromIndex(nCol)
                End If
                GetCell = dgCell
            Else
                GetCell = Nothing
            End If
        End Function
    
        Private Function GetRow(ByRef dgGrid As DataGrid, ByVal nRow As Integer) As DataGridRow
            Dim row As DataGridRow = dgGrid.ItemContainerGenerator.ContainerFromIndex(nRow)
            If row Is Nothing Then
                dgGrid.ScrollIntoView(dgGrid.Items(nRow))
                row = dgGrid.ItemContainerGenerator.ContainerFromIndex(nRow)
            End If
            GetRow = row
        End Function
    
        Function GetVisualChild2(ByRef dgRow As Visual) As DataGridCellsPresenter
            Dim v As Visual
            Dim nChildren As Integer
            Dim nChild As Integer
            Dim child As DataGridCellsPresenter
    
            child = Nothing
            nChildren = VisualTreeHelper.GetChildrenCount(dgRow)
            For nChild = 0 To nChildren - 1
                v = VisualTreeHelper.GetChild(dgRow, nChild)
                child = CType(v, DataGridCellsPresenter)
                If child Is Nothing Then
                    child = GetVisualChild2(v)
                Else
                    Exit For
                End If
            Next
            GetVisualChild2 = child
        End Function
    Everything seems to be working except the GetVisualChild implementation.  The GetChildCount of dgRow returns 0.

    Appreciate any help that can be provided.

    Bob
  • Sunday, May 01, 2011 3:50 PM
     
     

    Hello everybody

    we have an important question about your solution.

    Does myDataGrid.ScrollIntoView(dataObject) work in sync with UI Render Thread of WPF?

    it means if we call myDataGrid.ScrollIntoView(dataObject) on the next line can we sure that ItemsContainerGenerator creates correspond DataGridRow for desired dataObject ?

    it means ..

    int index = 11;
    myDataGrid.SelectedItem = myDataGrid.Items[index];
    myDataGrid.ScrollIntoView(myDataGrid.Items[index]);
    DataGridRow dgrow = (DataGridRow
    )myDataGrid.ItemContainerGenerator.ContainerFromItem(myDataGrid.Items[index]);

    //always dgrow!=null?

     
    dgrow.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));

     

    thank you for your answers

    http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/933b65b4-00ca-4fdd-8b15-615b8cc7c4ac/#3160bd4d-876a-4bd6-8b4b-b42d52626528
  • Tuesday, October 18, 2011 8:18 AM
     
     
    Thanks Aran, your solution worked for me
  • Thursday, April 05, 2012 9:28 PM
     
      Has Code
    I haven't had much luck the other ways, so i use this workaround

    //first focus the grid
    grid.Focus();
    //then create a new cell info, with the item we wish to edit and the column number of the cell we want in edit mode
    DataGridCellInfo cellInfo = new DataGridCellInfo(itemToSelect, grid.Columns[0]);
    //set the cell to be the active one
    grid.CurrentCell = cellInfo;
    //scroll the item into view
    grid.ScrollIntoView(itemToSelect);
    //begin the edit
    grid.BeginEdit();
    

    this works for me. (it would make a nice little static method too :)
    Thanks. This works for me too. 
  • Friday, April 06, 2012 7:47 PM
     
      Has Code
    I haven't had much luck the other ways, so i use this workaround

    //first focus the grid
    grid.Focus();
    //then create a new cell info, with the item we wish to edit and the column number of the cell we want in edit mode
    DataGridCellInfo cellInfo = new DataGridCellInfo(itemToSelect, grid.Columns[0]);
    //set the cell to be the active one
    grid.CurrentCell = cellInfo;
    //scroll the item into view
    grid.ScrollIntoView(itemToSelect);
    //begin the edit
    grid.BeginEdit();
    

    this works for me. (it would make a nice little static method too :)
    Worked perfectly for me!! Thanks.
    • Proposed As Answer by VenkyRS Friday, April 06, 2012 7:52 PM
    • Unproposed As Answer by VenkyRS Friday, April 06, 2012 7:52 PM
    •  
  • Friday, April 20, 2012 1:46 PM
     
     

    I have a new datagrid and when the user enters data in the first row the focus must be set to the beginning of second row first cell.  At this time the second row does not exist in the grid. It's added after the first row is comitted.  How can I achieve this using your code above.  Also, in your code could you explain what is itemToSelect will be?

    Thanks

    Ragu

  • Friday, September 07, 2012 12:38 AM
     
      Has Code

    DataGrid's CurrentCell (read/write) Property seemed the most obvious choice and surprise, surprise, actually worked for me!?!  Woo-hoo! 

    NOTE: Unless you're dynamically generating the Columns, you should know, at compile time, the Name of the Column you want to Focus on.  If so, you should give that Column a non-blank Name Property (aka "x.Name" XAML Attribute) and reference it by that name in the code.  That way you avoid hard-coded Column Index Literals that are likely to break without letting you know at compile time, if you reorder / remove the Column.

    FYI, I was doing this inside a Handler (inside the Code Behind of the View) of an Event Raised by an ICommand in the View Model bound to a Button in the View, so YMMV, if you're doing it using a different method.

    <DataGrid x:Name="MyDatGrid"
        ...
        >
        ...
        <DataGridCheckboxColumn x:Name="TargDgCol"
            Header="Targ"
            ...
            >
            ...
        
        </DataGridCheckboxColumn> <!-- "TargDgCol" -->
        ...    
    </DataGrid> <!-- "MyDatGrid" -->
    With MyDatGrd 
    
        .CurrentCell = New DataGridCellInfo(
            .Items(TargRowIdxInt), 
            TargDgCol 
        ) 
    
    End With ' -- MyDatGrd