【问题标题】:Tab create new row and focus new row in DataGrid选项卡在 DataGrid 中创建新行并聚焦新行
【发布时间】:2012-11-28 09:00:25
【问题描述】:

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

当定位到最后一个单元格并按下制表符或回车时,应该会发生以下情况:

  1. 添加了一个新行
  2. 新行中的第一个单元格被选中

对数据网格的其他需求是:

  • 一旦获得焦点,焦点应保留在数据网格内(ALT + 组合键是再次离开数据网格的方式)。
  • 数据网格是数据绑定的
  • 数据网格用于 MVVM
  • 我们使用 .net4.0 完整配置文件

【问题讨论】:

  • 我尝试过使用 KeyboardNavigation.TabNavigation="Contained" 和 KeyboardNavigation.TabNavigation 的其他值。我似乎找不到组合。我所有的尝试要么从数据网格中选择标签,要么不创建新行。在我看来,我需要一些其他技术来调整数据网格,但我还没有找到。

标签: wpf datagrid


【解决方案1】:

This article 有我能找到的最佳解决方案。

我更喜欢使用附加属性而不是行为,因为这使我能够轻松地将其设置为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);
                }
            }
        }
    }
}

我的默认样式如下所示:

<Style TargetType="DataGrid">
    <Setter Property="GridLinesVisibility" Value="None" />
    <Setter Property="KeyboardNavigation.TabNavigation" Value="Contained" />
    <Setter Property="sampleDataGridApp:DataGridBehavior.NewLineOnTab" Value="True" />
    <Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
</Style>

如果最后一列的 DataGridCell 的 IsTabStop 设置为 false,就像在 this example 中一样,上述将不起作用。

这是一个错误的解决方法:

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

【讨论】:

    【解决方案2】:

    好的,我已经为这个问题奋斗了好几个小时了。我已经尝试了几乎所有提出的解决方案,这就是我发现的适合我的解决方案......

        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 键转到最后一行的最后一个单元格并按 Tab 键时,它会将焦点移至新行的第一个单元格。这是您所期望的,但这不是默认行为。默认行为是将焦点移到下一个控件而不提交当前行编辑。这显然是 DataGrid 中的一个错误,我相信这就是为什么所有提议的解决方案都有一丝杂音。我承认我的解决方案闻起来不太好,但如果你同意这是错误,我更喜欢这种荒谬的默认行为。

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

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

    【讨论】:

      猜你喜欢
      • 2011-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多