У меня есть 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 });