34

I just want to display row numbers in the left-most column of my DataGrid. Is there some attribute to do this?

Keep in mind, this isn't a primary key for my table. I don't want these row numbers to move with their rows when a column is sorted. I basically want a running count. It doesn't even need to have a header.

JohnB
  • 18,046
  • 16
  • 98
  • 110
  • possible duplicate of [How to show row index for wpf toolkit datagrid?](http://stackoverflow.com/questions/2041168/how-to-show-row-index-for-wpf-toolkit-datagrid) – Dan J Jan 11 '11 at 20:38
  • 1
    Maybe I missunderstood the question, do you want to have a separate column for this or is RowHeader ok as well? – Fredrik Hedblad Jan 11 '11 at 21:38
  • 1
    `RowHeader` is perfect, thanks. – JohnB Jan 11 '11 at 21:48
  • If you are using MSSQL Server 2005+, you can try: http://msdn.microsoft.com/en-us/library/ms186734.aspx – nvcnvn Jun 25 '12 at 13:30

8 Answers8

53

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)
{
    e.Row.Header = (e.Row.GetIndex()).ToString(); 
}

When items are added or removed from the source list then the numbers can get out of sync for a while. For a fix to this, see the attached behavior here:
WPF 4 DataGrid: Getting the Row Number into the RowHeader

Useable like this

<DataGrid ItemsSource="{Binding ...}"
          behaviors:DataGridBehavior.DisplayRowNumber="True"> 
Community
  • 1
  • 1
Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266
  • Does this move the row numbers during sorting? or is `LoadingRow` called during sorting as well, which will keep the rownumber always in order? – VoodooChild Jan 11 '11 at 22:56
  • 2
    @VoodooChild: Yes, I've used this a couple of times and the Row-Numbers are always in the right order as far as I can tell, even with sorting, scrolling, change of ItemsSource etc. Although, one person here told me that it didn't work for him. I asked him for more info but he never replied. But for me, I've never seen a situation where this fails :) – Fredrik Hedblad Jan 11 '11 at 23:07
  • 3
    If you want to start at #1: `e.Row.Header = (e.Row.GetIndex() + 1).ToString();` – JohnB Jan 13 '11 at 22:45
  • Make sure an attribute `HeadersVisibility` is set to `All` or `Row`. `Columns` and `None` will not display that. For example: `` – p__d Jun 18 '18 at 12:47
  • 1
    this does not work with virtualization. How can one tweak the code to work with virtualized rows? – JobaDiniz Aug 01 '19 at 13:09
15

Adding a short info about Fredrik Hedblad answer.

<DataGrid Name="DataGrid" LoadingRow="DataGrid_LoadingRow" ...

void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
    e.Row.Header = (e.Row.GetIndex()+1).ToString(); 
}

...If you want to start numbering from 1

JohnnyJaxs
  • 321
  • 3
  • 6
13

If your data grid has its ItemsSource bound to a collection, bind the AlternationCount property of your data grid to either the the count property of your collection, or to the Items.Count property of your DataGrid as follows:

<DataGrid ItemsSource="{Binding MyObservableCollection}" AlternationCount="{Binding MyObservableCollection.Count}" />

Or:

<DataGrid ItemsSource="{Binding MyObservableCollection}" AlternationCount="{Binding Items.Count, RelativeSource={RelativeSource Self}" />

Either should work.

Then, assuming you're using a DataGridTextColumn for your leftmost column you do the following in your DataGrid.Columns definition:

<DataGrid.Columns>
   <DataGridTextColumn Binding="{Binding AlternationIndex, RelativeSource={RelativeSource AncestorType=DataGridRow}}" />
</DataGrid.Columns>

If you don't want to start at 0, you can add a converter to your binding to increment the index.

dotNET
  • 33,414
  • 24
  • 162
  • 251
GrantA
  • 490
  • 5
  • 13
  • 2
    This will not work correctly if you have scrolled in the datagrid and reload your collection. The numbering will start from 0 on the item at the top of where you have scrolled to in the datagrid. – Jesper Feb 28 '17 at 12:55
  • 1
    This worked for me But Index number changes randomly when i scroll on datagrid using mouse HOW to fix this?? – Dharti Sojitra Jun 06 '17 at 09:58
6

It's an old question, but I would like to share something. I had a similar problem, all I needed was a simple RowHeader numeration of rows and Fredrik Hedblad's answer was almost complete for my problem.

While this is great:

<DataGrid Name="DataGrid" LoadingRow="DataGrid_LoadingRow" ...

void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
    e.Row.Header = (e.Row.GetIndex()).ToString(); 
}

my headers messed up when removing and adding items. If you have buttons responsible for that just add dataGrid.Items.Refresh(); under the 'deleting' code as in my case:

private void removeButton_Click(object sender, RoutedEventArgs e)
{
    // delete items

    dataGrid.Items.Refresh();
}

That solved desyncronized numeration for me, because refreshing items calls DataGrig_LoadingRow again.

trollsteen
  • 113
  • 2
  • 6
4

And just to add to the discussion on this... (I spent too much time finding this out!).

You'll need to set the EnableRowVirtualization to False on the datagrid to prevent errors in the row sequencing:

EnableRowVirtualization="False"

The EnableRowVirtualization property is set to true by default. When the EnableRowVirtualization property is set to true, the DataGrid does not instantiate a DataGridRow object for each data item in the bound data source. Instead, the DataGrid creates DataGridRow objects only when they are needed, and reuses them as much as it can. MSDN Reference here

P_Fitz
  • 819
  • 9
  • 16
2

Just another answer to provide almost copy&paste example (not to be encouraged) for new people or people in a rush, inspired by answers inside this post by @GrantA and @Johan Larsson ( + many other people who answered to the numerous posts on that subject)

  • You may not want to add the enumeration inside a column
  • You do not need to re-create your own Attached Property

    <UserControl...
    
    <Grid>
        <DataGrid ItemsSource="{Binding MainData.ProjColl}" 
                  AutoGenerateColumns="False"
                  AlternationCount="{ Binding MainData.ProjColl.Count}" >
            <DataGrid.Columns>
                <!--Columns given here for example-->
                ...
                <DataGridTextColumn Header="Project Name" 
                                    Binding="{Binding CMProjectItemDirName}"
                                    IsReadOnly="True"/>
                ...
                <DataGridTextColumn Header="Sources Dir" 
                                    Binding="{Binding CMSourceDir.DirStr}"/>
                ...
            </DataGrid.Columns>
    
            <!--The problem of the day-->
            <DataGrid.RowHeaderStyle>
                <Style TargetType="{x:Type DataGridRowHeader}">
                    <Setter Property="Content" 
                     Value="{Binding Path=(ItemsControl.AlternationIndex),
                     RelativeSource={RelativeSource AncestorType=DataGridRow}}"/>
                </Style>
            </DataGrid.RowHeaderStyle>
        </DataGrid>
    </Grid>
    
    </UserControl>
    

Note the parenthesis () around (ItemsControl.AlternationIndex) as warned in Fredrik Hedblad answer in Check if Row as odd number

enter image description here

Community
  • 1
  • 1
NGI
  • 852
  • 1
  • 12
  • 31
2

After some Tests withRowHeaderStyle, the repaired and extended sample from NGI:

        <DataGrid EnableRowVirtualization="false" ItemsSource="{Binding ResultView}" AlternationCount="{Binding ResultView.Count}" RowHeaderWidth="10">
            <DataGrid.RowHeaderStyle>
                <Style TargetType="{x:Type DataGridRowHeader}">
                    <Setter Property="Content" Value="{Binding Path=AlternationIndex, RelativeSource={RelativeSource AncestorType=DataGridRow}}" />
                </Style>
            </DataGrid.RowHeaderStyle>
        </DataGrid>
acarlstein
  • 1,799
  • 2
  • 13
  • 21
omei
  • 51
  • 4
0

Using attached properties, full source here.

using System.Windows;
using System.Windows.Controls;

public static class Index
{
    private static readonly DependencyPropertyKey OfPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
        "Of",
        typeof(int),
        typeof(Index),
        new PropertyMetadata(-1));

    public static readonly DependencyProperty OfProperty = OfPropertyKey.DependencyProperty;

    public static readonly DependencyProperty InProperty = DependencyProperty.RegisterAttached(
        "In",
        typeof(DataGrid),
        typeof(Index),
        new PropertyMetadata(default(DataGrid), OnInChanged));

    public static void SetOf(this DataGridRow element, int value)
    {
        element.SetValue(OfPropertyKey, value);
    }

    public static int GetOf(this DataGridRow element)
    {
        return (int)element.GetValue(OfProperty);
    }

    public static void SetIn(this DataGridRow element, DataGrid value)
    {
        element.SetValue(InProperty, value);
    }

    public static DataGrid GetIn(this DataGridRow element)
    {
        return (DataGrid)element.GetValue(InProperty);
    }

    private static void OnInChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var row = (DataGridRow)d;
        row.SetOf(row.GetIndex());
    }
}

Xaml:

<DataGrid ItemsSource="{Binding Data}">
    <DataGrid.RowStyle>
        <Style TargetType="{x:Type DataGridRow}">
            <Setter Property="dataGrid2D:Index.In" 
                    Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
        </Style>
    </DataGrid.RowStyle>

    <DataGrid.RowHeaderStyle>
        <Style TargetType="{x:Type DataGridRowHeader}">
            <Setter Property="Content" 
                    Value="{Binding Path=(dataGrid2D:Index.Of), 
                                    RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" />
        </Style>
    </DataGrid.RowHeaderStyle>
</DataGrid>
Johan Larsson
  • 17,112
  • 9
  • 74
  • 88