I am looking to get the row number into the RowHeader of the WPF 4 DataGrid so it has an Excel-like column for the row numbers of the DataGrid.
The solution I've seen out there on the web suggests adding an index field to the business objects. This isn't really an option because the DataGrid will be getting resorted a lot and we don't want to have to keep track of changing these index fields constantly.
Thanks a lot
PreviewMouseLeftButtonDown event and search in handler the visual tree up to a DatagridRow , which has DatagridRow. GetIndex() method. So you will get always the right row index. Save this answer.
WPF DataGrid (SfDataGrid) provides built-in row called AddNewRow. It allows user to add a new row to underlying collection. You can enable or disable by setting SfDataGrid.
The WPF Data Grid (GridControl) is a data-aware control designed to display and edit data in different layouts: tabular, treelike, and card. The GridControl allows users to manage large amounts of data (sort, group, filter, and so on).
One way is to add them in the LoadingRow event for the DataGrid
.
<DataGrid Name="DataGrid" LoadingRow="DataGrid_LoadingRow" ... />
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(); }
Update
To get this to work with the .NET 3.5 DataGrid in WPF Toolkit a little modification is needed. The index is still generated correctly but the output fails when using virtualization. The following modification to the RowHeaderTemplate
fixes this
<toolkit:DataGrid LoadingRow="DataGrid_LoadingRow"> <toolkit:DataGrid.RowHeaderTemplate> <DataTemplate> <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type toolkit:DataGridRow}}, Path=Header}"/> </DataTemplate> </toolkit:DataGrid.RowHeaderTemplate> </toolkit:DataGrid>
Edit 2012-07-05
If items are added or removed from the source list then the numbers get out of sync until the list is scrolled so LoadingRow
is called again. Working around this issue is a little more complex and the best solution I can think of right now is to keep the LoadingRow
solution above and also
dataGrid.ItemContainerGenerator.ItemsChanged
DataGridRows
in the visual tree DataGridRow
Here is an attached behavior which does this. Use it like this
<DataGrid ItemsSource="{Binding ...}" behaviors:DataGridBehavior.DisplayRowNumber="True">
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<DataGridRowEventArgs> 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<DataGridRow>(dataGrid). ForEach(d => d.Header = d.GetIndex()); }; dataGrid.ItemContainerGenerator.ItemsChanged += itemsChangedHandler; } } #endregion // DisplayRowNumber #region Get Visuals private static List<T> GetVisualChildCollection<T>(object parent) where T : Visual { List<T> visualCollection = new List<T>(); GetVisualChildCollection(parent as DependencyObject, visualCollection); return visualCollection; } private static void GetVisualChildCollection<T>(DependencyObject parent, List<T> 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 }
Edit: Apparently scrolling changes the index so the binding won't work like that...
A (seemingly) clean templating solution:
Xaml:
<Window ... xmlns:local="clr-namespace:Test" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"> <Window.Resources> <local:RowToIndexConv x:Key="RowToIndexConv"/> </Window.Resources> <DataGrid ItemsSource="{Binding GridData}"> <DataGrid.RowHeaderTemplate> <DataTemplate> <TextBlock Margin="2" Text="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Converter={StaticResource RowToIndexConv}}"/> </DataTemplate> </DataGrid.RowHeaderTemplate> </DataGrid> </Window>
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 }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With