取消在DataGrid中添加新行

时间:2012-03-02 18:45:25

标签: wpf mvvm datagrid

我有一个ObservableCollection绑定到DataGrid,我希望User能够将数据添加到网格中,但只有在添加的所有条目的总和小于100%时才会这样。

为了让数据网格聚焦接受数据的单元格,我正在使用处理DataGrid.RowEditEnding事件的代码。这很棘手,但它确实有效。

我们的参赛作品总数为100%。我可以在CollectionChanged事件处理程序中捕获我不想要的添加,但当然我无法在我在那里时更改集合。

任何人都有一个关于抓住并处理不需要的添加的好地方的建议吗?

干杯,
Berryl

ViewModel事件处理程序代码

public ObservableCollection<RatioBagEntryVm> Ratios { get; private set; }

public RatioBagAllocatorVm() {
    RatioBag = new RatioBag();
    Ratios = new ObservableCollection<RatioBagEntryVm>();
    Ratios.CollectionChanged += OnRatiosChanged;
}

private void OnRatiosChanged(object sender, NotifyCollectionChangedEventArgs e) {
    SequencingService.Sequence(Ratios);

    switch (e.Action)
    {
        case NotifyCollectionChangedAction.Add:
            foreach (var entryVm in e.NewItems.Cast<RatioBagEntryVm>()) {
                entryVm.PropertyChanged += OnRatioEntryChanged;
                RatioBag.Add(entryVm.Ratio);
                entryVm.Bag = RatioBag;
            }
            break;
        case NotifyCollectionChangedAction.Remove:
            foreach (var entryVm in e.OldItems.Cast<RatioBagEntryVm>()) {
                RatioBag.RemoveAt(entryVm.SequenceNumber - 1);
            }
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }
}

代码隐藏处理程序

    /// <summary>
    /// Adapted from http://blogs.msdn.com/b/vinsibal/archive/2009/04/14/5-more-random-gotchas-with-the-wpf-datagrid.aspx
    /// </summary>
    private void OnDataGridRowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
    {
        var dg = sender as DataGrid;
        if (e.EditAction != DataGridEditAction.Commit) return;

        //
        // custom commit action:
        // moves to the next row and opens the second cell for edit
        // if the next row is the NewItemPlaceholder
        //

        var wasLastRowInGrid = e.Row.Item == dg.Items[dg.Items.Count - 2];
        if (!wasLastRowInGrid) return;
        if (dg.HasError()) return;


        // set the new cell to be the last row and the second column
        const int colIndex = 1;
        var rowToSelect = dg.Items[dg.Items.Count - 1];
        var colToSelect = dg.Columns[colIndex];
        var rowIndex = dg.Items.IndexOf(rowToSelect);

        switch (dg.SelectionUnit)
        {
            case DataGridSelectionUnit.Cell:
            case DataGridSelectionUnit.CellOrRowHeader:
                // select the new cell
                dg.SelectedCells.Clear();
                dg.SelectedCells.Add(new DataGridCellInfo(rowToSelect, colToSelect));
                break;
            case DataGridSelectionUnit.FullRow:
                e.Row.IsSelected = true;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }

        // this is the extra tricky part
        Dispatcher.BeginInvoke(new DispatcherOperationCallback(param =>
        {
            // get the new cell, set focus, then open for edit
            var cell = dg.GetCell(rowIndex, colIndex);
            cell.Focus();

            dg.BeginEdit();
            return null;
        }), DispatcherPriority.Background, new object[] { null });
    }
}

解决方案(暂时)

原始代码中棘手的部分是使用Dispatcher模仿你想在DataGrid.RowEndedEvent中提供的内容,正如Vincent Sibal编写的idea我基于我的代码。

所以,这也是取消的地方,修改后的代码如下。以这种方式访问​​视图模型几乎不是MVVM摘要中的内容,并且肯定是黑客攻击,但是......它可以工作。

        // this is the extra tricky part
        Dispatcher.BeginInvoke(new DispatcherOperationCallback(param =>
        {
            // get the new cell, set focus, then open for edit
            var cell = dg.GetCell(rowIndex, colIndex);
            cell.Focus();

            // cancel the row commit if we are already fully allocated
            var win = dg.FindVisualParent<Window>();
            var vm = win.DataContext as RatioBagAllocatorVm;
            if(vm.RatioBag.IsAllocatable) 
                e.Cancel = true;
            else {
                dg.BeginEdit();
            }
            return null;
        }), DispatcherPriority.Background, new object[] { null });

1 个答案:

答案 0 :(得分:1)

编辑:留下以下帖子作为上下文,尽管对于取消事件更加清晰,如更新后的问题所示:

e.Cancel = true;

...

为什么不能删除不需要的添加?这样的事情应该有效(原谅任何语法错误!):

private bool revertActive = false;
private void OnRatiosChanged(object sender, NotifyCollectionChangedEventArgs e)
{
   if (revertActive) return;

   if (e.Action == NotifyCollectionChangedAction.Add)
   {
      foreach (var entryVm in e.NewItems.Cast<RatioBagEntryVm>()) 
      {
          entryVm.PropertyChanged += OnRatioEntryChanged;
          RatioBag.Add(entryVm.Ratio);
          entryVm.Bag = RatioBag;
      }

      if (entryVm.Ratio > 100)
      {
          revertActive = true;
          foreach(object newItem in e.NewItems)
          {
              (sender as ObservableCollection).RemoveItem(newItem);
          }
          revertActive = false;

          //...Any other revert code here...
      }
   }
}

也就是说,防止物品首先被添加,而不是在事后删除它们就不那么笨重了。