Отмена добавления новой строки в DataGrid - PullRequest
2 голосов
/ 02 марта 2012

У меня есть ObservableCollection, которая привязана к DataGrid, где я хочу, чтобы пользователь мог добавлять данные в сетку, но ТОЛЬКО тогда, когда сумма всех добавленных записей составляет менее 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, согласно Винсенту Сибалу, который написал идею , на которой я основывал свой код.

Итак, это место для отмены, и измененный код ниже. Доступ к модели представления таким способом - едва ли материал, о котором можно прочитать в дайджесте 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 Ответ

1 голос
/ 02 марта 2012

Редактировать: Оставив следующий пост для контекста, хотя он гораздо чище ОТМЕНА событие, как указано в обновленном вопросе:

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

Тем не менее, это менее неуклюже, чтобы предотвратить добавление элементов вообще, а не удалять их по факту.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...