wpfwpfdatagrid

WPF 4 DataGrid: Getting the Row Number into the RowHeader


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


Solution

  • 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

    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
    }