2012-03-02 86 views
2

我有一個ObservableCollection綁定到DataGrid,我希望用戶能夠將數據添加到網格中,但只有在添加的所有條目總和小於100%。取消在DataGrid中添加新行

爲了讓數據網格聚焦接受數據的單元格,我使用了處理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 }); 
    } 
} 

SOLUTION(現在)

的原代碼的最棘手的部分正在使用Dispatcher模仿wh在你希望在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

編輯:離開後下的背景下,雖然它的很多清潔劑取消的情況下,如在所更新的問題表示:

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

這就是說,它是不太笨重,以防止物品在首位不斷添加,而不是在事後刪除它們。

+0

@ianscol。是的,我寧願阻止添加,但不知道我可以在哪裏做到這一點! DataGrid觸發它... – Berryl 2012-03-02 19:18:47

+1

你可以在'OnDataGridRowEditEnding'中設置'DataGridRowEditEndingEventArgs.Cancel = true'嗎?只要您可以設置事件,以便您可以訪問該事件中的比率,以便計算新的比率,那麼應使用「Cancel = true」來回滾該行。在我吐出上面的代碼之前,我應該首先想到這一點! – ianschol 2012-03-02 19:39:27

+0

對接醜陋,但比屁股醜陋,不起作用!查看修改過的帖子結尾,瞭解我所做的事情。乾杯 – Berryl 2012-03-02 21:33:29