diff --git a/docs/docs/row-headers.md b/docs/docs/row-headers.md
index ba917a90..83ef1e21 100644
--- a/docs/docs/row-headers.md
+++ b/docs/docs/row-headers.md
@@ -40,7 +40,7 @@ Provide a `DataTemplate` via [`RowHeaderTemplate`](xref:WinUI.TableView.TableVie

-> **Note:** The `Tag` of the `TableViewRowHeader` is set to the row's 1-based index. Bind to it with `{Binding Tag, RelativeSource={RelativeSource Mode=TemplatedParent}}` for row numbers.
+> **Note:** The `Tag` of the `TableViewRowHeader` is always set to the row's number (see [Row numbering](#row-numbering) below), whether or not a custom `RowHeaderTemplate` is set. Bind to it with `{Binding Tag, RelativeSource={RelativeSource Mode=TemplatedParent}}` to incorporate the number into a custom template.
### Selecting a template per row
@@ -71,6 +71,25 @@ public class StatusHeaderSelector : DataTemplateSelector
```
+## Row numbering
+
+Set [`ShowRowNumbers`](xref:WinUI.TableView.TableView.ShowRowNumbers) to `true` to display each row's number at the very start of the row, in its own column ahead of the row header content and the row details expander toggle:
+
+```xml
+
+```
+
+The row number is a real, stable position among all rows - it does not renumber when a [group](grouping.md) is collapsed and hides other rows, unlike a naively counted display position. It coexists with `RowHeaderTemplate`, `RowHeaderTemplateSelector`, and the row details expander toggle without taking space from any of them.
+
+Customize its appearance with these theme resources:
+
+| Resource | Type | Description |
+|---|---|---|
+| `TableViewRowNumberFontSize` | `double` | Font size of the row number text |
+| `TableViewRowNumberForeground` | `Brush` | Foreground brush of the row number text |
+| `TableViewRowNumberMargin` | `Thickness` | Margin around the row number text |
+| `TableViewRowNumberMinWidth` | `double` | Minimum width of the row number column |
+
## Sizing the row header
| Property | Default | Description |
diff --git a/samples/WinUI.TableView.SampleApp/Pages/GroupingPage.xaml b/samples/WinUI.TableView.SampleApp/Pages/GroupingPage.xaml
index 83a89be9..0f25129a 100644
--- a/samples/WinUI.TableView.SampleApp/Pages/GroupingPage.xaml
+++ b/samples/WinUI.TableView.SampleApp/Pages/GroupingPage.xaml
@@ -12,7 +12,7 @@
+ Description="Group a column via its options button (try Department or Gender), then use the buttons to add, insert, move, and delete items - a way to check those operations keep group headers, counts, and order correct. Groups sort by key by default; toggle 'Sort Groups by Count' below (or the same option in a grouped column's own flyout) to show the biggest group first instead. Turn on ShowRowNumbers and collapse a group to see row numbers stay real instead of renumbering the rows still visible.">
+
-
+
diff --git a/src/AutomationPeers/TableViewCellAutomationPeer.cs b/src/AutomationPeers/TableViewCellAutomationPeer.cs
index b4e2b7f6..eb54a89b 100644
--- a/src/AutomationPeers/TableViewCellAutomationPeer.cs
+++ b/src/AutomationPeers/TableViewCellAutomationPeer.cs
@@ -70,8 +70,8 @@ protected override string GetNameCore()
? TableViewRowAutomationPeer.GetColumnHeaderText(col)
: string.Empty;
- var rowIndex = _owner.Row?.Index ?? -1;
- var rowDisplay = rowIndex >= 0 ? $"Row {rowIndex + 1}" : string.Empty;
+ var rowNumber = _owner.Row?.RowNumber ?? 0;
+ var rowDisplay = rowNumber > 0 ? $"Row {rowNumber}" : string.Empty;
var cellValue = _owner.Column is TableViewTemplateColumn
? string.Empty
diff --git a/src/AutomationPeers/TableViewRowAutomationPeer.cs b/src/AutomationPeers/TableViewRowAutomationPeer.cs
index 6a0b95f5..8d5b625d 100644
--- a/src/AutomationPeers/TableViewRowAutomationPeer.cs
+++ b/src/AutomationPeers/TableViewRowAutomationPeer.cs
@@ -43,8 +43,8 @@ protected override string GetNameCore()
return name;
}
- var index = _owner.Index;
- return index >= 0 ? $"Row {index + 1}" : "Row";
+ var rowNumber = _owner.RowNumber;
+ return rowNumber > 0 ? $"Row {rowNumber}" : "Row";
}
///
diff --git a/src/AutomationPeers/TableViewRowHeaderAutomationPeer.cs b/src/AutomationPeers/TableViewRowHeaderAutomationPeer.cs
index aed2b94f..23e1cb10 100644
--- a/src/AutomationPeers/TableViewRowHeaderAutomationPeer.cs
+++ b/src/AutomationPeers/TableViewRowHeaderAutomationPeer.cs
@@ -75,8 +75,8 @@ protected override string GetNameCore()
}
}
- // Fall back to the row index
- var rowIndex = _owner.TableViewRow?.Index ?? -1;
- return rowIndex >= 0 ? $"Row {rowIndex + 1}" : base.GetNameCore();
+ // Fall back to the row number
+ var rowNumber = _owner.TableViewRow?.RowNumber ?? 0;
+ return rowNumber > 0 ? $"Row {rowNumber}" : base.GetNameCore();
}
}
diff --git a/src/ItemsSource/CollectionView.cs b/src/ItemsSource/CollectionView.cs
index 820458e7..6acd2e83 100644
--- a/src/ItemsSource/CollectionView.cs
+++ b/src/ItemsSource/CollectionView.cs
@@ -811,6 +811,15 @@ public int IndexOf(object? item)
return _view.IndexOf(item);
}
+ ///
+ /// Returns the item's position among - the full filtered+sorted set,
+ /// in source order - unlike , unaffected by items being hidden by a collapsed group.
+ ///
+ internal int IndexOfSourceItem(object? item)
+ {
+ return item is null ? -1 : _groupingSourceItems.IndexOf(item);
+ }
+
///
/// Inserts an item to the collection at the specified index.
///
diff --git a/src/TableView.Properties.cs b/src/TableView.Properties.cs
index 2077399d..7a939b22 100644
--- a/src/TableView.Properties.cs
+++ b/src/TableView.Properties.cs
@@ -238,6 +238,11 @@ public partial class TableView
///
public static readonly DependencyProperty HeadersVisibilityProperty = DependencyProperty.Register(nameof(HeadersVisibility), typeof(TableViewHeadersVisibility), typeof(TableView), new PropertyMetadata(TableViewHeadersVisibility.All, OnRowHeadersVisibilityChanged));
+ ///
+ /// Identifies the ShowRowNumbers dependency property.
+ ///
+ public static readonly DependencyProperty ShowRowNumbersProperty = DependencyProperty.Register(nameof(ShowRowNumbers), typeof(bool), typeof(TableView), new PropertyMetadata(false, OnShowRowNumbersChanged));
+
///
/// Identifies the RowHeaderContent dependency property.
///
@@ -877,6 +882,15 @@ public TableViewHeadersVisibility HeadersVisibility
set => SetValue(HeadersVisibilityProperty, value);
}
+ ///
+ /// Gets or sets a value indicating whether each row shows its number in the row header area.
+ ///
+ public bool ShowRowNumbers
+ {
+ get => (bool)GetValue(ShowRowNumbersProperty);
+ set => SetValue(ShowRowNumbersProperty, value);
+ }
+
///
/// Gets or sets the data template for the row header.
///
@@ -1303,6 +1317,21 @@ private static void OnRowHeadersVisibilityChanged(DependencyObject d, Dependency
}
}
+ ///
+ /// Handles changes to the ShowRowNumbers property.
+ ///
+ private static void OnShowRowNumbersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is TableView tableView)
+ {
+ foreach (var row in tableView._rows)
+ {
+ row.RowPresenter?.SetRowNumberVisibility();
+ row.RowPresenter?.SetRowNumber();
+ }
+ }
+ }
+
///
/// Handles changes to the RowHeaderTemplate and RowHeaderTemplateSelector properties.
///
diff --git a/src/TableView.cs b/src/TableView.cs
index 013dcda4..276005e4 100644
--- a/src/TableView.cs
+++ b/src/TableView.cs
@@ -143,6 +143,7 @@ private void OnCollectionViewVectorChanged(IObservableVector