如何在WPF DataGrid中将焦点聚焦到新行的单元格上?

5

.Net 4 WPF DataGrid MVVM

用户点击添加按钮,触发视图模型中的命令。在视图模型中执行命令时,我会向绑定到网格的视图模型的视图集合中添加一个新对象。新行确实出现在我的网格中。但是,我也想将焦点发送到该新行中的第一个可编辑单元格。

我甚至“欺骗”了mvvm,为我的视图模型添加了一个事件,以便视图知道何时将焦点聚焦到新行。

我搜索过,但没有找到解决方法。当我看到这个链接时,我很有希望:

Datagrid Set focus on newly added row

它指向:

http://social.msdn.microsoft.com/forums/en-US/wpf/thread/63974f4f-d9ee-45af-8499-42f29cbc22ae

但其他人报告的问题却无人回答,即如何处理网格的虚拟化行为。新添加的行尚未创建。因此,GetCells调用经常失败。如果需要ScrollIntoView,则更有可能失败。

我挂了很多事件,包括LoadingRow和RequestBringIntoView,但都没有成功。根据我挂接的事件不同,我设法获得了单元格的一个引用。但是然后我收到错误“在内容生成正在进行时无法调用StartAt”。但是,我检查了ItemContainerGenerator的状态,并在调用单元格的BeginEdit时生成了容器。

3个回答

1

以下是一种通过编程方式将焦点设置到特定单元格的方法:

DataGridCell cell = GetCell(rowIndex, colIndex);
cell.Focus;

请参考以下文章,了解有关GetCell()的更多信息。

1
你有没有看清我的问题?我已经包含了那个页面的链接,而GetCell的问题是如果单元格还没有被创建,它是无法工作的。 - happyfirst
尝试在 ScrollIntoView 之前调用 dg.UpdateLayout()。 - Dmitry Savy
太好了!之前我找到了一个解决方法,必须“调度”一个函数调用,优先级为normal,这样可以工作。但是目前在ScrollIntoView之前调用UpdateLayout似乎更可靠。谢谢! - happyfirst
不用谢 - 对于一开始误读你的问题感到抱歉。 - Dmitry Savy

0

这对我来说似乎有效:

    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;
    }

虽然这段代码可能回答了问题,但最好解释一下它是如何解决问题的,而不引入其他问题,并解释为什么要使用它。仅有代码的答案从长远来看并不实用。 - JAL
很抱歉,我很新手,认为代码中的变量名称足以自说明。 - Doug Dekker

0

这对我来说是工作:

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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接