【问题标题】:Add a New Row to DataGrid when user press Enter Key on the last Cell of Last Row当用户在最后一行的最后一个单元格上按 Enter 键时,向 DataGrid 添加一个新行
【发布时间】:2014-06-12 11:44:13
【问题描述】:

我有一个包含 3 列的 DataGrid。

当用户在 DataGrid 的最后一个单元格上点击 Enter 时,我想向 DataGrid 添加一个新行。我已经使用DataGrid.InputBindings 成功完成了所需的所有操作,但这里的问题是,当我在第二列上按 Enter 键时,会添加一个新行。我希望在焦点位于属于第 3 列的单元格上并按 Enter 键时添加它。

这是我的代码:

<DataGrid CanUserAddRows="False" CanUserDeleteRows="True" CanUserReorderColumns="False" CanUserResizeColumns="False" AutoGenerateColumns="False" 
          ItemsSource="{Binding People}" SelectedItem="{Binding SelectedRow}" CurrentCell="{Binding SelectedCell, Mode=OneWayToSource}" 
          DataGridCell.GotFocus="DataGrid_CellGotFocus" SelectionMode="Single">
    <DataGrid.InputBindings>
        <KeyBinding Key="Enter" Command="{Binding DataContext.NewRowCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
    </DataGrid.InputBindings>
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Confirmation" Width="*">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <ComboBox SelectedItem="{Binding DataContext.SelectedConfirmation, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                              Visibility="{Binding DataContext.ConfirmationVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource boolToVisibilityConverter}}">
                        <ComboBox.Items>
                            <sys:String>Add New</sys:String>
                            <sys:String>End Of List</sys:String>
                        </ComboBox.Items>
                    </ComboBox>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="Name" Width="*">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Name}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="Age" Width="*">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Age}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Age}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

在视图模型中:

public class MainWindowViewModel : INotifyPropertyChanged
{

    public MainWindowViewModel()
    {
        People = new ObservableCollection<Person>();
        People.Add(new Person());

        NewRowCommand = new RelayCommand(NewRow);

    }

    private ObservableCollection<Person> _people;
    public ObservableCollection<Person> People
    {
        get
        {
            return _people;
        }
        set
        {
            _people = value;
            OnPropertyChanged("People");
        }
    }

    private Person _selectedRow;
    public Person SelectedRow
    {
        get
        {
            return _selectedRow;
        }
        set
        {
            _selectedRow = value;
            OnPropertyChanged("SelectedRow");

            if (_selectedRow == People.Last())
            {
                ConfirmationVisibility = true;
            }
            else
            {
                ConfirmationVisibility = false;
            }
        }
    }

    private bool _confirmationVisibility;
    public bool ConfirmationVisibility
    {
        get
        {
            return _confirmationVisibility;
        }
        set
        {
            _confirmationVisibility = value;
            OnPropertyChanged("ConfirmationVisibility");
        }
    }

    private string _selectedConfirmation;
    public string SelectedConfirmation
    {
        get
        {
            return _selectedConfirmation;
        }
        set
        {
            _selectedConfirmation = value;
            OnPropertyChanged("SelectedConfirmation");
        }
    }

    private DataGridCellInfo _selectedCell;
    public DataGridCellInfo SelectedCell
    {
        get
        {
            return _selectedCell;
        }
        set
        {
            _selectedCell = value;
            OnPropertyChanged("SelectedCell");
        }
    }

    public ICommand NewRowCommand { get; set; }

    private void NewRow(object obj)
    {
        if (SelectedRow == People.Last())
        {
            if (SelectedConfirmation == "Add New")
            {
                if (SelectedCell.Column.Header.ToString() == "Age")
                {
                    People.Add(new Person());
                }
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

这是在 VS2012 中创建的示例项目:https://drive.google.com/file/d/0B5WyqSALui0bWVNHcVFXU1hOQ00/edit?usp=sharing

【问题讨论】:

    标签: c# wpf datagrid


    【解决方案1】:

    当您按下回车键时,它也会立即移动到第 3 列因此,当您选择标题 == 年龄时,不要创建列,而是在等于确认时创建它。 这是您修改后的工作代码以修复错误

    private void NewRow(object obj)
    {
        if (SelectedRow == People.Last())
        {
            if (SelectedConfirmation == "Add New")
            {
                if (SelectedCell.Column.Header.ToString() == "Confirmation")
                {
                    People.Add(new Person());
                }
            }
        }
    }
    

    只需执行此操作,您的示例就可以在用户在最后一列上按 Enter 时创建新行。
    希望您解决了您的问题,并为自己赢得了我的第一个答案和积分。

    【讨论】:

      【解决方案2】:

      代码的问题在于 CurrentCell 属性绑定。 它在调用 NewRowCommand 之前绑定新的当前单元格。

      假设您在最后一行的“名称”列的位置,那么会发生什么:

      1. 您按下回车键。
      2. 当前单元格更新为新单元格。 (年龄)
      3. 然后调用您的命令,并使用新的当前单元格(年龄)并因此添加新行。

      我建议在 currentcell 更新时保留对前一个单元格的引用。并使用它而不是 currentCell。

       public DataGridCellInfo SelectedCell
          {
              get
              {
                  return _selectedCell;
              }
              set
              {
                  _lastCell = _selectedCell; //here is my edit
                  _selectedCell = value;
                  OnPropertyChanged("SelectedCell");
              }
          }
      

      所以 new NewRow(object obj) 看起来像这样:

       private void NewRow(object obj)
          {
              if (SelectedRow == People.Last())
              {
                  if (SelectedConfirmation == "Add New")
                  {
                      if (_lastCell.Column !=null && _lastCell.Column.Header.ToString() =="Age")//here is my edit
                      {
                          People.Add(new Person());
                      }
                  }
              }
          }
      

      DataGrid_CellGotFocus(object sender, RoutedEventArgs e) 内部也有问题。看看我的编辑,我删除了 row.IsSelected assinment。

       private void DataGrid_CellGotFocus(object sender, RoutedEventArgs e)
          {
              // Lookup for the source to be DataGridCell
              if (e.OriginalSource.GetType() == typeof(DataGridCell))
              {
                  // Starts the Edit on the row;
                  DataGrid grd = (DataGrid)sender;
                  grd.BeginEdit(e);
      
                  Control control = GetFirstChildByType<Control>(e.OriginalSource as DataGridCell);
                  if (control != null)
                  {
                      control.Focus();
                  }
      
                  DataGridCell cell = GetDataGridCell(grd.CurrentCell);
                  DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
                  if (dataGrid != null)
                  {
                      if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
                      {
                          if (!cell.IsSelected)
                              cell.IsSelected = true;
                      }
                  }
              }
          }
      

      我在我的电脑上运行了编辑后的代码,效果很好!如果有任何我可能遗漏的问题,请告诉我。

      【讨论】:

      • 我的问题是从@klugerama 给出的答案中解决的。但是我只有一个问题。问题是:当向数据网格添加新行时,单元格(在添加新行之前具有焦点)即使在失去焦点后也不会离开编辑模式。我已经从您的代码中尝试了DataGrid_CellGotFocus,但结果仍然相同。
      • 在失去焦点后看到您在编辑模式下的其他帖子。此 impl 更改不会导致此编辑问题。
      • 很抱歉,但我认为我已经按照您的建议非常清楚地实施了更改,但我仍然遇到了编辑问题。如果您不再遇到该编辑问题,请提交您的示例项目。
      • 它是您的示例项目,根据我所做的更改进行了修改sugarsync.com/pf/D6837746_80955217_394537
      【解决方案3】:

      任何UIElement 都可以有InputBindings,而不仅仅是DataGrid。那为什么不搬家

      <DataGrid.InputBindings>
          <KeyBinding Key="Enter" Command="{Binding DataContext.NewRowCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
      </DataGrid.InputBindings>
      

      到你想要的栏目:

          <DataGridTemplateColumn Header="Age" Width="*">
              <DataGridTemplateColumn.CellTemplate>
                  <DataTemplate>
                      <TextBlock Text="{Binding Age}">
                          <TextBlock.InputBindings>
                              <KeyBinding Key="Enter" Command="{Binding DataContext.NewRowCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
                          </TextBlock.InputBindings>
                      </TextBlock>
                  </DataTemplate>
              </DataGridTemplateColumn.CellTemplate>
              <DataGridTemplateColumn.CellEditingTemplate>
                  <DataTemplate>
                      <TextBox Text="{Binding Age}">
                          <TextBox.InputBindings>
                              <KeyBinding Key="Enter" Command="{Binding DataContext.NewRowCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
                          </TextBox.InputBindings>
                      </TextBox>
                  </DataTemplate>
              </DataGridTemplateColumn.CellEditingTemplate>
          </DataGridTemplateColumn>
      

      【讨论】:

      • 我已经尝试过您提供的解决方案。但我仍然得到相同的结果。
      • 我留了一个断点,仔细看了看。这里的问题是,当我在第二列的单元格上按 Enter 键时,会调用 NewRowCommand。我认为,计算机认为我在第 3 列的单元格上按了 Enter 键。
      • 删除了InputBindingsDataGrid级别的InputBindings
      • 能否请您在我提供的样本中进行验证?
      • 您还拥有捕获 Enter 键的 EnterKeyTraversal 类。我发现删除“MainWindow.xaml”中的引用解决了它在第 2 列中被捕获的问题,但当然不再移动到下一个单元格。清理事件,以便您没有多个处理程序。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-02
      • 2013-03-06
      • 2011-01-09
      • 1970-01-01
      相关资源
      最近更新 更多