2012-03-02 15 views
2

DataGridにバインドされたObservableCollectionを持っていますが、ユーザーがグリッドにデータを追加できるようにしますが、追加されたすべてのエントリの合計が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で利用できるようにしたい場合は、idea私のコードに基づいて書いたVincent Sibalのとおりです。

これもキャンセルする場所であり、変更されたコードは以下のとおりです。このようにビューモデルにアクセスすることは、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

+1

'OnDataGridRowEditEnding'に' DataGridRowEditEndingEventArgs.Cancel = true'を設定できますか?イベントの比率にアクセスできるように設定すれば、新しい比率を計算することができるので、 'Cancel = true'で行をロールバックできます。私は上記のコードを吐き出す前に、まずそれを考えていたはずです! – ianschol

+0

butt醜いが、butt uglyよりもうまくいかない!私がしたことのための変更されたポストの終わりを見てください。乾杯 – Berryl

関連する問題