WPF 4 DataGrid: acquisizione del numero di riga in RowHeader

Sto cercando di ottenere il numero di riga nel RowHeader del DataGrid di WPF 4 in modo che abbia una colonna simile a Excel per i numeri di riga di DataGrid.

La soluzione che ho visto sul Web suggerisce l’aggiunta di un campo indice agli oggetti di business. Questa non è un’opzione in quanto il DataGrid verrà sfruttato molto e non vogliamo tenere traccia di modificare costantemente questi campi indice.

molte grazie

Un modo è aggiungerli nell’evento LoadingRow per DataGrid .

  
 void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e) { // Adding 1 to make the row count start at 1 instead of 0 // as pointed out by daub815 e.Row.Header = (e.Row.GetIndex() + 1).ToString(); } 

Aggiornare
Per far funzionare tutto questo con .NET 3.5 DataGrid in WPF Toolkit è necessaria una piccola modifica. L’indice è ancora generato correttamente ma l’output non riesce quando si utilizza la virtualizzazione. La seguente modifica al RowHeaderTemplate corregge questo

        

Modifica 2012-07-05
Se gli elementi vengono aggiunti o rimossi dall’elenco di origine, i numeri vengono fuori sincrono fino a quando l’elenco non viene fatto scorrere in modo che LoadingRow venga chiamato di nuovo. Lavorare su questo problema è un po ‘più complesso e la soluzione migliore che posso pensare adesso è mantenere la soluzione LoadingRow sopra e anche

  • Iscriviti a dataGrid.ItemContainerGenerator.ItemsChanged
  • Nel gestore di eventi, trova tutti i DataGridRows nell’albero visivo
  • Imposta l’intestazione sull’indice per ogni DataGridRow

Ecco un comportamento allegato che fa questo. Usalo in questo modo

  

DisplayRowNumber

 public class DataGridBehavior { #region DisplayRowNumber public static DependencyProperty DisplayRowNumberProperty = DependencyProperty.RegisterAttached("DisplayRowNumber", typeof(bool), typeof(DataGridBehavior), new FrameworkPropertyMetadata(false, OnDisplayRowNumberChanged)); public static bool GetDisplayRowNumber(DependencyObject target) { return (bool)target.GetValue(DisplayRowNumberProperty); } public static void SetDisplayRowNumber(DependencyObject target, bool value) { target.SetValue(DisplayRowNumberProperty, value); } private static void OnDisplayRowNumberChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) { DataGrid dataGrid = target as DataGrid; if ((bool)e.NewValue == true) { EventHandler loadedRowHandler = null; loadedRowHandler = (object sender, DataGridRowEventArgs ea) => { if (GetDisplayRowNumber(dataGrid) == false) { dataGrid.LoadingRow -= loadedRowHandler; return; } ea.Row.Header = ea.Row.GetIndex(); }; dataGrid.LoadingRow += loadedRowHandler; ItemsChangedEventHandler itemsChangedHandler = null; itemsChangedHandler = (object sender, ItemsChangedEventArgs ea) => { if (GetDisplayRowNumber(dataGrid) == false) { dataGrid.ItemContainerGenerator.ItemsChanged -= itemsChangedHandler; return; } GetVisualChildCollection(dataGrid). ForEach(d => d.Header = d.GetIndex()); }; dataGrid.ItemContainerGenerator.ItemsChanged += itemsChangedHandler; } } #endregion // DisplayRowNumber #region Get Visuals private static List GetVisualChildCollection(object parent) where T : Visual { List visualCollection = new List(); GetVisualChildCollection(parent as DependencyObject, visualCollection); return visualCollection; } private static void GetVisualChildCollection(DependencyObject parent, List visualCollection) where T : Visual { int count = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < count; i++) { DependencyObject child = VisualTreeHelper.GetChild(parent, i); if (child is T) { visualCollection.Add(child as T); } if (child != null) { GetVisualChildCollection(child, visualCollection); } } } #endregion // Get Visuals } 

Modifica: Apparentemente lo scorrimento cambia l’indice in modo che la rilegatura non funzioni in quel modo …

Una soluzione per i modelli (apparentemente) pulita:
xaml:

             

Converter:

 public class RowToIndexConv : IValueConverter { #region IValueConverter Members public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { DataGridRow row = value as DataGridRow; return row.GetIndex() + 1; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } #endregion } 

Tutti questi approcci non funzioneranno se aggiungi o rimuovi le righe. Dovresti aggiornare gli indici di riga in questi casi. Guarda questo comportamento:

 public static class DataGridBehavior { #region RowNumbers property public static readonly DependencyProperty RowNumbersProperty = DependencyProperty.RegisterAttached("RowNumbers", typeof (bool), typeof (DataGridBehavior), new FrameworkPropertyMetadata(false, OnRowNumbersChanged)); private static void OnRowNumbersChanged(DependencyObject source, DependencyPropertyChangedEventArgs args) { DataGrid grid = source as DataGrid; if (grid == null) return; if ((bool)args.NewValue) { grid.LoadingRow += onGridLoadingRow; grid.UnloadingRow += onGridUnloadingRow; } else { grid.LoadingRow -= onGridLoadingRow; grid.UnloadingRow -= onGridUnloadingRow; } } private static void refreshDataGridRowNumbers(object sender) { DataGrid grid = sender as DataGrid; if (grid == null) return; foreach (var item in grid.Items) { var row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromItem(item); if (row != null) row.Header = row.GetIndex() + 1; } } private static void onGridUnloadingRow(object sender, DataGridRowEventArgs e) { refreshDataGridRowNumbers(sender); } private static void onGridLoadingRow(object sender, DataGridRowEventArgs e) { refreshDataGridRowNumbers(sender); } [AttachedPropertyBrowsableForType(typeof(DataGrid))] public static void SetRowNumbers(DependencyObject element, bool value) { element.SetValue(RowNumbersProperty, value); } public static bool GetRowNumbers(DependencyObject element) { return (bool) element.GetValue(RowNumbersProperty); } #endregion } 

@Fredrik Hedblad’s answer funziona per me. Grazie!

Ho aggiunto un’altra proprietà per ottenere un valore “offset” in modo che DataGrid possa essere avviato da 0 o da 1 (o da qualsiasi altra impostazione).

Per usare il comportamento, (nota ‘b’ è lo spazio dei nomi)

  

Le classi modificate:

 ///  /// Collection of DataGrid behavior ///  public static class DataGridBehavior { #region DisplayRowNumberOffset ///  /// Sets the starting value of the row header if enabled ///  public static DependencyProperty DisplayRowNumberOffsetProperty = DependencyProperty.RegisterAttached("DisplayRowNumberOffset", typeof(int), typeof(DataGridBehavior), new FrameworkPropertyMetadata(0, OnDisplayRowNumberOffsetChanged)); public static int GetDisplayRowNumberOffset(DependencyObject target) { return (int)target.GetValue(DisplayRowNumberOffsetProperty); } public static void SetDisplayRowNumberOffset(DependencyObject target, int value) { target.SetValue(DisplayRowNumberOffsetProperty, value); } private static void OnDisplayRowNumberOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) { DataGrid dataGrid = target as DataGrid; int offset = (int)e.NewValue; if (GetDisplayRowNumber(target)) { WPFUtil.GetVisualChildCollection(dataGrid). ForEach(d => d.Header = d.GetIndex() + offset); } } #endregion #region DisplayRowNumber ///  /// Enable display of row header automatically ///  ///  /// Source: ///  public static DependencyProperty DisplayRowNumberProperty = DependencyProperty.RegisterAttached("DisplayRowNumber", typeof(bool), typeof(DataGridBehavior), new FrameworkPropertyMetadata(false, OnDisplayRowNumberChanged)); public static bool GetDisplayRowNumber(DependencyObject target) { return (bool)target.GetValue(DisplayRowNumberProperty); } public static void SetDisplayRowNumber(DependencyObject target, bool value) { target.SetValue(DisplayRowNumberProperty, value); } private static void OnDisplayRowNumberChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) { DataGrid dataGrid = target as DataGrid; if ((bool)e.NewValue == true) { int offset = GetDisplayRowNumberOffset(target); EventHandler loadedRowHandler = null; loadedRowHandler = (object sender, DataGridRowEventArgs ea) => { if (GetDisplayRowNumber(dataGrid) == false) { dataGrid.LoadingRow -= loadedRowHandler; return; } ea.Row.Header = ea.Row.GetIndex() + offset; }; dataGrid.LoadingRow += loadedRowHandler; ItemsChangedEventHandler itemsChangedHandler = null; itemsChangedHandler = (object sender, ItemsChangedEventArgs ea) => { if (GetDisplayRowNumber(dataGrid) == false) { dataGrid.ItemContainerGenerator.ItemsChanged -= itemsChangedHandler; return; } WPFUtil.GetVisualChildCollection(dataGrid). ForEach(d => d.Header = d.GetIndex() + offset); }; dataGrid.ItemContainerGenerator.ItemsChanged += itemsChangedHandler; } } #endregion // DisplayRowNumber } 

LoadingRowEvent viene triggersto da questo:

 ICollectionView view = CollectionViewSource.GetDefaultView(_dataGrid.ItemsSource); view.Refresh();