Reputation: 1071
.Net 4 WPF DataGrid MVVM
User clicks add button which triggers command on viewmodel. In the viewmodel command execute, I add a new object to the viewcollection of the viewmodel that the grid is bound to. The new row does appear in my grid. However, I also want to send the focus to the first editable cell in that new row.
I even "cheated" mvvm, added an event on my viewmodel that the view listens to, to know when to focus the new row.
I've searched but no luck. I was hopeful when I came across this:
Datagrid Set focus on newly added row
which leads to
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/63974f4f-d9ee-45af-8499-42f29cbc22ae
But the problem that others have reported and no one has answered is how to deal with the virtualizing behaviour of the grid. The newly added row has not yet been created. So that GetCells call fails frequently. And if ScrollIntoView is required, then it's that much more likely to fail.
I've hooked a ton of events including LoadingRow and RequestBringIntoView with no luck. Depending on which event I hook, I have managed to be able to get a reference to the cell. But then I get an error "Cannot call StartAt when content generation is in progress". But I checked the status of the ItemContainerGenerator and it was ContainersGenerated when I made the call to the cell's BeginEdit.
Upvotes: 5
Views: 9518
Reputation: 1
This is work for me:
private void button_Click(object sender, RoutedEventArgs e)
{
//Scroll to the last row
var border = VisualTreeHelper.GetChild(dataGrid, 0) as Decorator;
if (border != null)
{
var scroll = border.Child as ScrollViewer;
if (scroll != null) scroll.ScrollToEnd();
}
//Edit the first cell of the last row
int lastRow = dataGrid.Items.Count - 1;
DataGridCell cell = GetCell(lastRow, 0);
cell.Focus();
dataGrid.BeginEdit();
}
public DataGridCell GetCell(int row, int column)
{
DataGridRow rowContainer = GetRow(row);
if (rowContainer != null)
{
DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
// try to get the cell but it may possibly be virtualized
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
if (cell == null)
{
// now try to bring into view and retreive the cell
dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
}
return cell;
}
return null;
}
public DataGridRow GetRow(int index)
{
DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index);
if (row == null)
{
// may be virtualized, bring into view and try again
dataGrid.ScrollIntoView(dataGrid.Items[index]);
row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index);
}
return row;
}
static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
Upvotes: 0
Reputation: 353
This seemed to work for me:
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
private void SetFocusOnNewRow(DataGrid theDataGrid, Int32 columnIndex)
{
theDataGrid.UnselectAll();
theDataGrid.UpdateLayout();
Int32 newRowIndex = theDataGrid.Items.Count - 1;
theDataGrid.ScrollIntoView(theDataGrid.Items[newRowIndex]);
DataGridRow newDataGridRow = theDataGrid.ItemContainerGenerator.ContainerFromIndex(newRowIndex) as DataGridRow;
DataGridCellsPresenter newDataGridCellsPresenter = GetVisualChild<DataGridCellsPresenter>(newDataGridRow);
if (newDataGridCellsPresenter != null)
{
DataGridCell newDataGridCell = newDataGridCellsPresenter.ItemContainerGenerator.ContainerFromIndex(columnIndex) as DataGridCell;
if (newDataGridCell != null)
newDataGridCell.Focus();
}
}
static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
Upvotes: 0
Reputation: 1067
Here is one way to set focus to a particular cell programmatically:
DataGridCell cell = GetCell(rowIndex, colIndex);
cell.Focus;
Please see the following article for more information on GetCell().
Upvotes: 1