Wpf 选项卡在DataGrid中创建新行并聚焦新行

Wpf 选项卡在DataGrid中创建新行并聚焦新行,wpf,datagrid,Wpf,Datagrid,我的数据网格有一些问题。我的项目正在将一个Delphi项目转换为.Net。产品所有者希望datagrids具有相同的行为 当定位到最后一个单元格并点击tab或enter时,应发生以下情况: 将添加一个新行 将选中新行中的第一个单元格 datagrid的其他需求包括: 一旦有了焦点,焦点就应该留在数据网格内(ALT+组合键是再次离开数据网格的方式) 数据网格是数据绑定的 数据网格用于MVVM 我们使用.net4.0完整配置文件 有我能找到的最好的解决方案 我更喜欢使用附加属性而不是行为,因为这使

我的数据网格有一些问题。我的项目正在将一个Delphi项目转换为.Net。产品所有者希望datagrids具有相同的行为

当定位到最后一个单元格并点击tab或enter时,应发生以下情况:

  • 将添加一个新行
  • 将选中新行中的第一个单元格
  • datagrid的其他需求包括:

    • 一旦有了焦点,焦点就应该留在数据网格内(ALT+组合键是再次离开数据网格的方式)
    • 数据网格是数据绑定的
    • 数据网格用于MVVM
    • 我们使用.net4.0完整配置文件
      • 有我能找到的最好的解决方案

        我更喜欢使用附加属性而不是行为,因为这使我能够轻松地将其设置为
        DataGrid
        的默认样式。代码如下:

        namespace SampleDataGridApp
        {
            using System.Windows;
            using System.Windows.Controls;
            using System.Windows.Input;
        
            /// <summary>
            /// An attached behavior that modifies the tab behavior for a <see cref="DataGrid"/>.
            /// </summary>
            public static class DataGridBehavior
            {
                /// <summary>
                /// Identifies the <c>NewLineOnTab</c> attached property.
                /// </summary>
                public static readonly DependencyProperty NewLineOnTabProperty = DependencyProperty.RegisterAttached(
                    "NewLineOnTab",
                    typeof(bool),
                    typeof(DataGridBehavior),
                    new PropertyMetadata(default(bool), OnNewLineOnTabChanged));
        
                /// <summary>
                /// Sets the value of the <c>NewLineOnTab</c> attached property.
                /// </summary>
                /// <param name="element">The <see cref="DataGrid"/>.</param>
                /// <param name="value">A value indicating whether to apply the behavior.</param>
                public static void SetNewLineOnTab(DataGrid element, bool value)
                {
                    element.SetValue(NewLineOnTabProperty, value);
                }
        
                /// <summary>
                /// Gets the value of the <c>NewLineOnTab</c> attached property.
                /// </summary>
                /// <param name="element">The <see cref="DataGrid"/>.</param>
                /// <returns>A value indicating whether to apply the behavior.</returns>
                public static bool GetNewLineOnTab(DataGrid element)
                {
                    return (bool)element.GetValue(NewLineOnTabProperty);
                }
        
                /// <summary>
                /// Called when the value of the <c>NewLineOnTab</c> property changes.
                /// </summary>
                /// <param name="sender">The event sender.</param>
                /// <param name="e">The event arguments.</param>
                private static void OnNewLineOnTabChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
                {
                    DataGrid d = sender as DataGrid;
        
                    if (d == null)
                    {
                        return;
                    }
        
                    bool newValue = (bool)e.NewValue;
                    bool oldValue = (bool)e.OldValue;
        
                    if (oldValue == newValue)
                    {
                        return;
                    }
        
                    if (oldValue)
                    {
                        d.PreviewKeyDown -= AssociatedObjectKeyDown;
                    }
                    else
                    {
                        d.PreviewKeyDown += AssociatedObjectKeyDown;
                        KeyboardNavigation.SetTabNavigation(d, KeyboardNavigationMode.Contained);
                    }
                }
        
                /// <summary>
                /// Handles the <see cref="UIElement.KeyDown"/> event for a <see cref="DataGridCell"/>.
                /// </summary>
                /// <param name="sender">The event sender.</param>
                /// <param name="e">The event arguments.</param>
                private static void AssociatedObjectKeyDown(object sender, KeyEventArgs e)
                {
                    if (e.Key != Key.Tab)
                    {
                        return;
                    }
        
                    DataGrid dg = e.Source as DataGrid;
        
                    if (dg == null)
                    {
                        return;
                    }
        
                    if (dg.CurrentColumn.DisplayIndex == dg.Columns.Count - 1)
                    {
                        var icg = dg.ItemContainerGenerator;
        
                        if (dg.SelectedIndex == icg.Items.Count - 2)
                        {
                            dg.CommitEdit(DataGridEditingUnit.Row, false);
                        }
                    }
                }
            }
        }
        

        好的,我已经和这个问题斗争了好几个小时了。我已经尝试了几乎每一个提议的解决方案,这里是我发现对我有用的

            private void grid_PreviewKeyDown(object sender, KeyEventArgs e)
            {
                if (e.Key == Key.Tab)
                {
                    if (grid.SelectedIndex == grid.Items.Count - 2 && grid.CurrentColumn.DisplayIndex == grid.Columns.Count - 1)
                    {
                        grid.CommitEdit(DataGridEditingUnit.Row, false);
                        e.Handled = true;
                    }
                }
            }
        
            private void DataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
            {
                if (grid.SelectedIndex == grid.Items.Count - 2)
                {
                    grid.SelectedIndex = grid.Items.Count - 1;
                    grid.CurrentCell = new DataGridCellInfo(grid.Items[grid.Items.Count - 1], grid.Columns[0]);
                }
            }
        
        此代码的作用是,当您在最后一行的最后一个单元格上按tab键时,它会将焦点移动到新行的第一个单元格。这是您所期望的,但这不是默认行为。默认行为是将焦点移动到下一个控件,而不是提交当前行编辑。这显然是DataGrid中的一个bug,我相信这就是为什么所有建议的解决方案都有一点kluge的味道。我承认我的解决方案闻起来不太好,但如果你同意这是bug,我更喜欢这一点,而不是荒谬的默认行为

        即使网格已排序,此解决方案也可以工作。新输入的行将排序到正确的位置,但焦点将放在新行的第一列


        唯一未解决的问题是,当从顶部向下切换到新行之前的最后一个单元格时,必须在焦点移动到新行之前输入两次tab。我研究了一下这个怪癖,最后放弃了。

        我试过摆弄KeyboardNavigation.TabNavigation=“Contained”和KeyboardNavigation.TabNavigation的其他值。我似乎找不到一个组合。我的所有尝试要么从数据网格中删除选项卡,要么不创建新行。在我看来,我需要一些其他技术来调整datagrid,但我还没有找到。
        private static void AssociatedObjectKeyDown(object sender, KeyEventArgs e)
        {               
            if (e.Key != Key.Tab)
            {
                return;
            }
        
            DataGrid dg = e.Source as DataGrid;
        
            if (dg == null)
            {
                return;
            }
        
            int offSet = 1;
            var columnsReversed = dg.Columns.Reverse();
            foreach (var dataGridColumn in columnsReversed)
            {
                // Bug: This makes the grand assumption that a readonly column's "DataGridCell" has IsTabStop == false;
                if (dataGridColumn.IsReadOnly)
                {
                    offSet++;
                }
                else
                {
                    break;
                }
            }
        
            if (dg.CurrentColumn.DisplayIndex == (dg.Columns.Count - offSet))
            {
                var icg = dg.ItemContainerGenerator;
        
                if (dg.SelectedIndex == icg.Items.Count - 2)
                {
                    dg.CommitEdit(DataGridEditingUnit.Row, false);
                }
            }
        }
        
            private void grid_PreviewKeyDown(object sender, KeyEventArgs e)
            {
                if (e.Key == Key.Tab)
                {
                    if (grid.SelectedIndex == grid.Items.Count - 2 && grid.CurrentColumn.DisplayIndex == grid.Columns.Count - 1)
                    {
                        grid.CommitEdit(DataGridEditingUnit.Row, false);
                        e.Handled = true;
                    }
                }
            }
        
            private void DataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
            {
                if (grid.SelectedIndex == grid.Items.Count - 2)
                {
                    grid.SelectedIndex = grid.Items.Count - 1;
                    grid.CurrentCell = new DataGridCellInfo(grid.Items[grid.Items.Count - 1], grid.Columns[0]);
                }
            }