Skip to content
Open
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
4 changes: 4 additions & 0 deletions samples/WinUI.TableView.SampleApp/Pages/SelectionPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
OnContent="True"
OffContent="False"
IsOn="{Binding IsReadOnly, Mode=TwoWay, ElementName=tableView}" />
<ToggleSwitch Header="TapToEdit"
OnContent="True"
OffContent="False"
IsOn="{Binding TapToEdit, Mode=TwoWay, ElementName=tableView}" />
<InfoBar IsOpen="True"
IsClosable="False"
Title="Selected Item">
Expand Down
14 changes: 14 additions & 0 deletions src/TableView.Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ public partial class TableView
/// </summary>
public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register(nameof(IsReadOnly), typeof(bool), typeof(TableView), new PropertyMetadata(false, OnIsReadOnlyChanged));

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

/// <summary>
/// Identifies the CornerButtonMode dependency property.
/// </summary>
Expand Down Expand Up @@ -485,6 +490,15 @@ public bool IsReadOnly
set => SetValue(IsReadOnlyProperty, value);
}

/// <summary>
/// Gets or sets a value indicating whether tapping an already-selected cell initiates editing.
/// </summary>
public bool TapToEdit
{
get => (bool)GetValue(TapToEditProperty);
set => SetValue(TapToEditProperty, value);
}

/// <summary>
/// Gets or sets the mode of the corner button.
/// </summary>
Expand Down
14 changes: 13 additions & 1 deletion src/TableView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@
{
base.PrepareContainerForItemOverride(element, item);

if (element is TableViewRow { } recycledRow)
{
recycledRow.ApplyEditingHighlight(false);
}

DispatcherQueue.TryEnqueue(() =>
{
if (element is TableViewRow row)
Expand All @@ -111,6 +116,13 @@
row.ApplyCellsSelectionState();
row.RowPresenter?.ApplyDetailsPaneState(item);

// Reset current cell border on all cells in recycled containers
// to clear stale "Current" visual state from previous use.
foreach (var cell in row.Cells)
{
cell.ApplyCurrentCellState();
}

if (CurrentCellSlot.HasValue)
{
row.ApplyCurrentCellState(CurrentCellSlot.Value);
Expand Down Expand Up @@ -184,7 +196,7 @@

do
{
newSlot = GetNextSlot(newSlot, shiftKey, e.Key is VirtualKey.Enter);
newSlot = GetNextSlot(newSlot, shiftKey, e.Key is VirtualKey.Enter || (e.Key is VirtualKey.Tab && SelectionUnit is TableViewSelectionUnit.Row));

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this isn't right because the tab key should always put the next editable cell in the same row. If there is no editable cell in the same row, it will automatically jump to the next row or previous row with shift + tab.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, GetNextSlot already cycles through editable cells and wraps to the next row on its own.

What we were going for is the File Explorer–style behavior where Tab during rename jumps to the next row and starts editing it. Tab feels natural for quick back-to-back editing since it's the standard "next field" key. We tied it to SelectionUnit.Row because in Row mode the user is already working row by row, so it felt like a natural fit.

If we want to keep this separate from selection mode, we could add a TabNavigationMode property (Horizontal/Vertical) on TableView , similar to how TapToEdit works. It would just be one extra check in HandleNavigations, no changes to GetNextSlot itself. Should we go with that, or do you think it should be handled differently?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get your point that in File Explorer, pressing the Tab key while editing a file name jumps to the next row. The key thing to note is that File Explorer only has one editable column and it's the Name column. So, TableView would behave exactly the same when there’s only one editable column in a row, jumping across rows with the Tab key.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think this change is necessary because the Tab key should always move to the next editable cell and put it in editing mode. If there’s no editable cell in the current row, it will automatically move to the next row.


} while (isEditing && Columns[newSlot.Column].IsReadOnly);

Expand Down Expand Up @@ -597,7 +609,7 @@
}
else
{
foreach (var propertyInfo in dataType.GetProperties())

Check warning on line 612 in src/TableView.cs

View workflow job for this annotation

GitHub Actions / build

'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicProperties' in call to 'System.Type.GetProperties()'. The return value of method 'WinUI.TableView.Extensions.ObjectExtensions.GetItemType(IEnumerable)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
{
var displayAttribute = propertyInfo.GetCustomAttributes().OfType<DisplayAttribute>().FirstOrDefault();
var autoGenerateField = displayAttribute?.GetAutoGenerateField();
Expand Down
32 changes: 30 additions & 2 deletions src/TableViewCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ protected override void OnPointerEntered(PointerRoutedEventArgs e)

if ((TableView?.SelectionMode is not ListViewSelectionMode.None
&& TableView?.SelectionUnit is not TableViewSelectionUnit.Row)
|| !TableView.IsReadOnly)
|| !TableView.IsReadOnly
|| (TableView?.SelectionUnit is TableViewSelectionUnit.Row or TableViewSelectionUnit.CellOrRow && !IsReadOnly))
{
VisualStates.GoToState(this, false, VisualStates.StatePointerOver);
}
Expand All @@ -203,7 +204,8 @@ protected override void OnPointerExited(PointerRoutedEventArgs e)

if ((TableView?.SelectionMode is not ListViewSelectionMode.None
&& TableView?.SelectionUnit is not TableViewSelectionUnit.Row)
|| !TableView.IsReadOnly)
|| !TableView.IsReadOnly
|| (TableView?.SelectionUnit is TableViewSelectionUnit.Row or TableViewSelectionUnit.CellOrRow && !IsReadOnly))
{
VisualStates.GoToState(this, false, VisualStates.StateNormal);
}
Expand All @@ -224,6 +226,21 @@ protected override async void OnTapped(TappedRoutedEventArgs e)
if (e.Handled) return;
}

if (TableView?.TapToEdit is true
&& TableView.CurrentCellSlot == Slot
&& !IsReadOnly
&& !TableView.IsEditing
&& Column?.UseSingleElement is not true)
{
if (TableView.SelectionUnit is TableViewSelectionUnit.Row)
{
MakeSelection();
}

e.Handled = await BeginCellEditing(e);
return;
}

if (TableView?.CurrentCellSlot != Slot || TableView?.LastSelectionUnit is TableViewSelectionUnit.Row)
{
MakeSelection();
Expand Down Expand Up @@ -398,6 +415,11 @@ internal void PrepareForEdit(RoutedEventArgs editingArgs)
TableView.UpdateCornerButtonState();
}

if (TableView?.SelectionUnit is TableViewSelectionUnit.Row or TableViewSelectionUnit.CellOrRow)
{
Row?.ApplyEditingHighlight(true);
}

if (editingElement is { IsHitTestVisible: true })
{
_editingArgs = editingArgs;
Expand Down Expand Up @@ -444,6 +466,12 @@ private void OnEditingElementLoaded(object sender, RoutedEventArgs e)
internal void EndEditing(TableViewEditAction editAction)
{
Column?.EndCellEditing(this, Row?.Content, editAction, _uneditedValue);

if (TableView?.SelectionUnit is TableViewSelectionUnit.Row or TableViewSelectionUnit.CellOrRow)
{
Row?.ApplyEditingHighlight(false);
}

SetElement();
}

Expand Down
8 changes: 8 additions & 0 deletions src/TableViewRow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,14 @@ internal void UpdateSelectCheckMarkOpacity()
}
}

/// <summary>
/// Highlights or unhighlights the row to indicate that a cell is being edited.
/// </summary>
internal void ApplyEditingHighlight(bool isEditing)
{
RowPresenter?.ApplyEditingHighlight(isEditing);
}

Comment on lines +606 to +613

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code block seems a bit off. I suggest adding an accent-tinted border or rectangle in the TableViewRowPresenter above the RootPanel, and controlling its visibility from the code-behind. For that, you can use the Cell.PrepareForEdit and Cell.EndEditing methods.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, that code was a bit off. What we were trying to do was mimic the pointer-over look by directly swapping the row's background when editing started and restoring it when done. The problem was it kept clashing with EnsureAlternateColors (we had to add a guard to stop it from overwriting the highlight), and we needed #if WINDOWS branching because Uno handles it differently.

So we went with your suggestion, added a Border overlay in TableViewRowPresenter.xaml sitting above the RootPanel, hit-test invisible, collapsed by default. The code-behind just toggles its visibility. Much cleaner since the highlight is now completely separate from the row's background, no guards, no platform branching. We added a TableViewRowEditingHighlightBackground theme resource in Resources.xaml for Light, Dark and HighContrast. And the visibility is driven from PrepareForEdit/EndEditing as you suggested.

/// <summary>
/// Gets the height of the horizontal gridlines.
/// </summary>
Expand Down
13 changes: 13 additions & 0 deletions src/TableViewRowPresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public partial class TableViewRowPresenter : Control
private ContentPresenter? _detailsPresenter;
private ToggleButton? _detailsToggleButton;
private ListViewItemPresenter? _itemPresenter;
private Border? _editingHighlightOverlay;

/// <summary>
/// Initializes a new instance of the <see cref="TableViewRowPresenter"/> class.
Expand All @@ -56,6 +57,7 @@ protected override void OnApplyTemplate()
_detailsPanel = GetTemplateChild("DetailsPanel") as Panel;
_detailsPresenter = GetTemplateChild("DetailsPresenter") as ContentPresenter;
_detailsToggleButton = GetTemplateChild("DetailsToggleButton") as ToggleButton;
_editingHighlightOverlay = GetTemplateChild("EditingHighlightOverlay") as Border;

_itemPresenter = this.FindAscendant<ListViewItemPresenter>();
TableViewRow = this.FindAscendant<TableViewRow>();
Expand Down Expand Up @@ -239,6 +241,17 @@ internal void ApplyDetailsPaneState(object? item)
}
}

/// <summary>
/// Toggles the editing highlight overlay visibility.
/// </summary>
internal void ApplyEditingHighlight(bool isEditing)
{
if (_editingHighlightOverlay is not null)
{
_editingHighlightOverlay.Visibility = isEditing ? Visibility.Visible : Visibility.Collapsed;
}
}

/// <summary>
/// Sets the DataTemplate for the row details.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions src/Themes/Resources.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
<StaticResource x:Key="TableViewRowSelectionIndicatorDisabledBrush" ResourceKey="AccentFillColorDisabledBrush" />
<StaticResource x:Key="TableViewHorizontalGridLineStroke" ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="TableViewVerticalGridLineStroke" ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="TableViewRowEditingHighlightBackground" ResourceKey="SubtleFillColorSecondaryBrush" />
<SolidColorBrush x:Key="TableViewRowDetailsBackgroundBrush" Color="{StaticResource CardBackgroundFillColorSecondary}" />
</ResourceDictionary>

Expand Down Expand Up @@ -142,6 +143,7 @@
<StaticResource x:Key="TableViewRowSelectionIndicatorDisabledBrush" ResourceKey="AccentFillColorDisabledBrush" />
<StaticResource x:Key="TableViewHorizontalGridLineStroke" ResourceKey="ControlStrokeColorDefaultBrush" />
<StaticResource x:Key="TableViewVerticalGridLineStroke" ResourceKey="ControlStrokeColorDefaultBrush" />
<StaticResource x:Key="TableViewRowEditingHighlightBackground" ResourceKey="SubtleFillColorSecondaryBrush" />
<SolidColorBrush x:Key="TableViewRowDetailsBackgroundBrush" Color="{StaticResource CardBackgroundFillColorSecondary}" />
</ResourceDictionary>

Expand Down Expand Up @@ -211,6 +213,7 @@
<SolidColorBrush x:Key="TableViewRowSelectionIndicatorDisabledBrush" Color="{ThemeResource SystemColorGrayTextColor}" />
<StaticResource x:Key="TableViewHorizontalGridLineStroke" ResourceKey="SystemColorButtonTextColorBrush" />
<StaticResource x:Key="TableViewVerticalGridLineStroke" ResourceKey="SystemColorButtonTextColorBrush" />
<SolidColorBrush x:Key="TableViewRowEditingHighlightBackground" Color="{ThemeResource SystemColorHighlightColor}" Opacity="0.3" />
<SolidColorBrush x:Key="TableViewRowDetailsBackgroundBrush" Color="{ThemeResource SystemColorWindowColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
Expand Down
5 changes: 5 additions & 0 deletions src/Themes/TableViewRowPresenter.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@
</Grid>
</Grid>

<Border x:Name="EditingHighlightOverlay"
IsHitTestVisible="False"
Background="{ThemeResource TableViewRowEditingHighlightBackground}"
Visibility="Collapsed" />

<Rectangle x:Name="HorizontalGridLine"
Grid.Row="1"
HorizontalAlignment="Stretch"
Expand Down
Loading
Loading