我有一个ObservableCollection绑定到DataGrid,我希望User能够将数据添加到网格中,但只有在添加的所有条目的总和小于100%时才会这样。
为了让数据网格聚焦接受数据的单元格,我正在使用处理DataGrid.RowEditEnding事件的代码。这很棘手,但它确实有效。
我们的参赛作品总数为100%。我可以在CollectionChanged事件处理程序中捕获我不想要的添加,但当然我无法在我在那里时更改集合。
任何人都有一个关于抓住并处理不需要的添加的好地方的建议吗?
干杯,
Berryl
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 });
答案 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...
}
}
}
也就是说,防止物品首先被添加,而不是在事后删除它们就不那么笨重了。