Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/docs/sorting.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ Disable sorting for a specific column:
<tv:TableViewNumberColumn Header="Price" Binding="{Binding Price}" CanSort="False" />
```

## Showing a hint icon for sortable columns

By default, an unsorted column shows no indicator, even if it's sortable — there's no way to tell a column supports sorting until you click it. Set [`ShowSortableColumnIcon`](xref:WinUI.TableView.TableView.ShowSortableColumnIcon) to `True` to show a subtle icon on sortable columns that aren't currently sorted, hinting that clicking the header will sort them:

```xml
<tv:TableView ShowSortableColumnIcon="True" />
```

The hint icon only appears where sorting is actually possible — it respects both `CanSortColumns` and the column's own `CanSort`. It disappears once the column is sorted, replaced by the usual ascending/descending indicator. `ShowSortableColumnIcon` is `False` by default, so existing apps see no visual change unless they opt in.

## Sorting by a different property (SortMemberPath)

By default, clicking a column header sorts by the column's bound property path. Use [`SortMemberPath`](xref:WinUI.TableView.TableViewColumn.SortMemberPath) to sort by a **different** property than the one displayed in the cell.
Expand Down Expand Up @@ -167,6 +177,7 @@ var descriptions = tableView.SortDescriptions; // the active SortDescription lis
|---|---|
| [`CanSortColumns`](xref:WinUI.TableView.TableView.CanSortColumns) | Enables or disables sorting for all columns |
| [`CanSort`](xref:WinUI.TableView.TableViewColumn.CanSort) | Per-column sort toggle |
| [`ShowSortableColumnIcon`](xref:WinUI.TableView.TableView.ShowSortableColumnIcon) | Shows a hint icon on sortable, unsorted columns. `False` by default |
| [`SortMemberPath`](xref:WinUI.TableView.TableViewColumn.SortMemberPath) | Property path used for sorting, overriding the display binding |
| [`SortDirection`](xref:WinUI.TableView.TableViewColumn.SortDirection) | Current sort direction (`Ascending`, `Descending`, or `null`) |
| [`SortDescriptions`](xref:WinUI.TableView.TableView.SortDescriptions) | Collection of active sort descriptions |
Expand Down
14 changes: 12 additions & 2 deletions samples/WinUI.TableView.SampleApp/Pages/SortingPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
Description="Showcasing built in column sorting feature.">
<controls:SamplePresenter.Example>
<tv:TableView ItemsSource="{Binding Items}"
CanSortColumns="{Binding IsOn, ElementName=canSort, Mode=TwoWay}" />
CanSortColumns="{Binding IsOn, ElementName=canSort, Mode=TwoWay}"
ShowSortableColumnIcon="{Binding IsOn, ElementName=showSortableColumnIcon, Mode=TwoWay}"
AutoGeneratingColumn="{x:Bind local:ExampleModelColumnsHelper.OnAutoGeneratingColumns}"/>
</controls:SamplePresenter.Example>
<controls:SamplePresenter.Options>
<StackPanel VerticalAlignment="Top">
Expand All @@ -23,17 +25,25 @@
Header="Can Sort Columns"
OnContent="True"
OffContent="False" />
<ToggleSwitch x:Name="showSortableColumnIcon"
IsOn="False"
Header="Show Sortable Column Icon"
OnContent="True"
OffContent="False" />
</StackPanel>
</controls:SamplePresenter.Options>
<controls:SamplePresenter.Xaml>
<x:String xml:space="preserve">
&lt;tv:TableView ItemsSource="{Binding Items}"
CanSortColumns="$(CanSortColumns)">
CanSortColumns="$(CanSortColumns)"
ShowSortableColumnIcon="$(ShowSortableColumnIcon)">
</x:String>
</controls:SamplePresenter.Xaml>
<controls:SamplePresenter.Substitutions>
<controls:CodeSubstitution Key="CanSortColumns"
Value="{x:Bind canSort.IsOn, Mode=OneWay}" />
<controls:CodeSubstitution Key="ShowSortableColumnIcon"
Value="{x:Bind showSortableColumnIcon.IsOn, Mode=OneWay}" />
</controls:SamplePresenter.Substitutions>
</controls:SamplePresenter>
</Grid>
Expand Down
13 changes: 12 additions & 1 deletion src/Columns/TableViewColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,17 @@ private static void OnCanFilterChanged(DependencyObject d, DependencyPropertyCha
}
}

/// <summary>
/// Handles changes to the CanSort property.
/// </summary>
private static void OnCanSortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TableViewColumn column && column.HeaderControl is not null)
{
column.HeaderControl.OnSortDirectionChanged();
}
}

/// <summary>
/// Handles changes to the HeaderStyle property.
/// </summary>
Expand Down Expand Up @@ -666,7 +677,7 @@ public string? SortMemberPath
/// <summary>
/// Identifies the CanSort dependency property.
/// </summary>
public static readonly DependencyProperty CanSortProperty = DependencyProperty.Register(nameof(CanSort), typeof(bool), typeof(TableViewColumn), new PropertyMetadata(true));
public static readonly DependencyProperty CanSortProperty = DependencyProperty.Register(nameof(CanSort), typeof(bool), typeof(TableViewColumn), new PropertyMetadata(true, OnCanSortChanged));

/// <summary>
/// Identifies the CanFilter dependency property.
Expand Down
44 changes: 43 additions & 1 deletion src/TableView.Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,18 @@ public partial class TableView
/// <summary>
/// Identifies the CanSortColumns dependency property.
/// </summary>
public static readonly DependencyProperty CanSortColumnsProperty = DependencyProperty.Register(nameof(CanSortColumns), typeof(bool), typeof(TableView), new PropertyMetadata(true));
public static readonly DependencyProperty CanSortColumnsProperty = DependencyProperty.Register(nameof(CanSortColumns), typeof(bool), typeof(TableView), new PropertyMetadata(true, OnCanSortColumnsChanged));

/// <summary>
/// Identifies the CanFilterColumns dependency property.
/// </summary>
public static readonly DependencyProperty CanFilterColumnsProperty = DependencyProperty.Register(nameof(CanFilterColumns), typeof(bool), typeof(TableView), new PropertyMetadata(true, OnCanFilterColumnsChanged));

/// <summary>
/// Identifies the ShowSortableColumnIcon dependency property.
/// </summary>
public static readonly DependencyProperty ShowSortableColumnIconProperty = DependencyProperty.Register(nameof(ShowSortableColumnIcon), typeof(bool), typeof(TableView), new PropertyMetadata(false, OnShowSortableColumnIconChanged));

/// <summary>
/// Identifies the MinColumnWidth dependency property.
/// </summary>
Expand Down Expand Up @@ -691,6 +696,15 @@ public bool CanFilterColumns
set => SetValue(CanFilterColumnsProperty, value);
}

/// <summary>
/// Gets or sets a value indicating whether a subtle icon is shown on sortable columns that are not currently sorted.
/// </summary>
public bool ShowSortableColumnIcon
{
get => (bool)GetValue(ShowSortableColumnIconProperty);
set => SetValue(ShowSortableColumnIconProperty, value);
}

/// <summary>
/// Gets or sets the minimum width of columns. This can be overridden by the individual column.
/// </summary>
Expand Down Expand Up @@ -1152,6 +1166,34 @@ private static void OnCanFilterColumnsChanged(DependencyObject d, DependencyProp
}
}

/// <summary>
/// Handles changes to the CanSortColumns property.
/// </summary>
private static void OnCanSortColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TableView tableView && tableView._headerRow is not null)
{
foreach (var header in tableView._headerRow.Headers)
{
header.OnSortDirectionChanged();
}
}
}

/// <summary>
/// Handles changes to the ShowSortableColumnIcon property.
/// </summary>
private static void OnShowSortableColumnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TableView tableView && tableView._headerRow is not null)
{
Comment thread
w-ahmad marked this conversation as resolved.
foreach (var header in tableView._headerRow.Headers)
{
header.OnSortDirectionChanged();
}
}
}

/// <summary>
/// Handles changes to the ColumnAutoWidthMode property.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/TableViewColumnHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
[TemplateVisualState(Name = VisualStates.StateUnsorted, GroupName = VisualStates.GroupSort)]
[TemplateVisualState(Name = VisualStates.StateSortAscending, GroupName = VisualStates.GroupSort)]
[TemplateVisualState(Name = VisualStates.StateSortDescending, GroupName = VisualStates.GroupSort)]
[TemplateVisualState(Name = VisualStates.StateSortable, GroupName = VisualStates.GroupSort)]
[TemplateVisualState(Name = VisualStates.StateFiltered, GroupName = VisualStates.GroupFilter)]
[TemplateVisualState(Name = VisualStates.StateUnfiltered, GroupName = VisualStates.GroupFilter)]
public partial class TableViewColumnHeader : ContentControl
Expand Down Expand Up @@ -326,12 +327,12 @@

/// <summary>
/// Computes the next sort direction for a tap/invoke on this header. A grouped column with no
/// independent <see cref="ColumnSortDescription"/> of its own (<see cref="HasGroupSortCompanion"/>)

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (x64)

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (x64)

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-uno-desktop

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (x86)

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (x86)

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (ARM64)

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (ARM64)

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved

Check warning on line 330 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-uno-wasm

XML comment has cref attribute 'HasGroupSortCompanion' that could not be resolved
/// cycles between just <see cref="SD.Ascending"/> and <see cref="SD.Descending"/> - its order is owned
/// entirely by its group and always needs a direction, so it never lands on the cleared/unsorted state
/// a three-state cycle's third click does. A grouped column that still has a leftover independent sort
/// from before it was grouped keeps three-state cycling until that description is cleared (see
/// <see cref="HandOffToGroupDescription"/>), at which point it falls into the two-state case above.

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (x64)

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (x64)

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-uno-desktop

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (x86)

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (x86)

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (ARM64)

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-winui-sample (ARM64)

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved

Check warning on line 335 in src/TableViewColumnHeader.cs

View workflow job for this annotation

GitHub Actions / build-uno-wasm

XML comment has cref attribute 'HandOffToGroupDescription' that could not be resolved
/// </summary>
private SD? GetNextSortDirection()
{
Expand Down Expand Up @@ -381,6 +382,7 @@
SetOptionCommands();
SetFilterButtonVisibility();
EnsureGridLines();
OnSortDirectionChanged();
}

/// <summary>
Expand All @@ -404,6 +406,10 @@
{
VisualStates.GoToState(this, false, VisualStates.StateSortDescending);
}
else if (CanSort && _tableView?.ShowSortableColumnIcon is true)
{
VisualStates.GoToState(this, false, VisualStates.StateSortable);
}
else
{
VisualStates.GoToState(this, false, VisualStates.StateUnsorted);
Expand Down
3 changes: 3 additions & 0 deletions src/Themes/Resources.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ResourceDictionary x:Key="Light">
<x:String x:Key="SortIconAscending">&#xf0ad;</x:String>
<x:String x:Key="SortIconDescending">&#xf0ae;</x:String>
<x:String x:Key="SortIconUnsorted">&#xe8cb;</x:String>
<x:String x:Key="FilterIcon">&#xe71c;</x:String>
<x:String x:Key="OptionsButtonIcon">&#xe712;</x:String>
<x:String x:Key="SelectAllButtonIcon">&#xE936;</x:String>
Expand Down Expand Up @@ -88,6 +89,7 @@
<ResourceDictionary x:Key="Dark">
<x:String x:Key="SortIconAscending">&#xf0ad;</x:String>
<x:String x:Key="SortIconDescending">&#xf0ae;</x:String>
<x:String x:Key="SortIconUnsorted">&#xe8cb;</x:String>
<x:String x:Key="FilterIcon">&#xe71c;</x:String>
<x:String x:Key="OptionsButtonIcon">&#xe712;</x:String>
<x:String x:Key="SelectAllButtonIcon">&#xE936;</x:String>
Expand Down Expand Up @@ -166,6 +168,7 @@
<ResourceDictionary x:Key="HighContrast">
<x:String x:Key="SortIconAscending">&#xf0ad;</x:String>
<x:String x:Key="SortIconDescending">&#xf0ae;</x:String>
<x:String x:Key="SortIconUnsorted">&#xe8cb;</x:String>
<x:String x:Key="FilterIcon">&#xe71c;</x:String>
<x:String x:Key="OptionsButtonIcon">&#xe712;</x:String>
<x:String x:Key="SelectAllButtonIcon">&#xE936;</x:String>
Expand Down
10 changes: 10 additions & 0 deletions src/Themes/TableViewColumnHeader.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@
</VisualStateGroup>
<VisualStateGroup x:Name="SortStates">
<VisualState x:Name="Unsorted" />
<VisualState x:Name="Sortable">
<VisualState.Setters>
<Setter Target="SortIcon.Visibility"
Value="Visible" />
<Setter Target="SortIcon.Glyph"
Value="{ThemeResource SortIconUnsorted}" />
<Setter Target="SortIcon.Opacity"
Value="0.5" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="SortAscending">
<VisualState.Setters>
<Setter Target="SortIcon.Visibility"
Expand Down
5 changes: 5 additions & 0 deletions src/VisualStates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ internal static class VisualStates
/// </summary>
public const string StateSortDescending = "SortDescending";

/// <summary>
/// Sortable (unsorted, but sortable) state
/// </summary>
public const string StateSortable = "Sortable";

/// <summary>
/// Sort state group
/// </summary>
Expand Down
Loading
Loading