Skip to content

Commit 5dbf5b6

Browse files
author
rashmithakur
committed
cell editing in row mode highlighting fixes
1 parent 6ad6f6d commit 5dbf5b6

4 files changed

Lines changed: 400 additions & 6 deletions

File tree

src/TableView.cs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ public partial class TableView : ListView
3434
private ScrollViewer? _scrollViewer;
3535
private RowDefinition? _headerRowDefinition;
3636
private bool _shouldThrowSelectionModeChangedException;
37+
private bool _isUpdatingBaseItemsSource;
3738
private bool _ensureColumns = true;
39+
private TableViewRow? _editingHighlightRow;
40+
private int _editingHighlightRowIndex = -1;
3841
private readonly List<TableViewRow> _rows = [];
3942
private readonly CollectionView _collectionView = [];
4043

@@ -103,6 +106,17 @@ protected override void PrepareContainerForItemOverride(DependencyObject element
103106
{
104107
base.PrepareContainerForItemOverride(element, item);
105108

109+
// Reset editing highlight state on recycled containers to prevent
110+
// stale _hasEditingHighlight from blocking EnsureAlternateColors.
111+
if (element is TableViewRow { } recycledRow)
112+
{
113+
recycledRow.ApplyEditingHighlight(false);
114+
if (_editingHighlightRow == recycledRow)
115+
{
116+
_editingHighlightRow = null;
117+
}
118+
}
119+
106120
DispatcherQueue.TryEnqueue(() =>
107121
{
108122
if (element is TableViewRow row)
@@ -111,10 +125,25 @@ protected override void PrepareContainerForItemOverride(DependencyObject element
111125
row.ApplyCellsSelectionState();
112126
row.RowPresenter?.ApplyDetailsPaneState(item);
113127

128+
// Reset current cell border on all cells in recycled containers
129+
// to clear stale "Current" visual state from previous use.
130+
foreach (var cell in row.Cells)
131+
{
132+
cell.ApplyCurrentCellState();
133+
}
134+
114135
if (CurrentCellSlot.HasValue)
115136
{
116137
row.ApplyCurrentCellState(CurrentCellSlot.Value);
117138
}
139+
140+
// Apply editing highlight when the editing row scrolls into view
141+
var rowIndex = Items.IndexOf(item);
142+
if (_editingHighlightRowIndex >= 0 && rowIndex == _editingHighlightRowIndex)
143+
{
144+
_editingHighlightRow = row;
145+
row.ApplyEditingHighlight(true);
146+
}
118147
}
119148
});
120149
}
@@ -184,7 +213,7 @@ private async Task HandleNavigations(KeyRoutedEventArgs e, bool shiftKey, bool c
184213

185214
do
186215
{
187-
newSlot = GetNextSlot(newSlot, shiftKey, e.Key is VirtualKey.Enter);
216+
newSlot = GetNextSlot(newSlot, shiftKey, e.Key is VirtualKey.Enter || (e.Key is VirtualKey.Tab && SelectionUnit is TableViewSelectionUnit.Row));
188217

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

@@ -196,6 +225,21 @@ private async Task HandleNavigations(KeyRoutedEventArgs e, bool shiftKey, bool c
196225
{
197226
SetIsEditing(false);
198227
}
228+
else if (SelectionUnit is TableViewSelectionUnit.Row or TableViewSelectionUnit.CellOrRow && newSlot.Row != currentCell.Slot.Row)
229+
{
230+
// Editing moved to a different row — move the highlight
231+
_editingHighlightRow?.ApplyEditingHighlight(false);
232+
_editingHighlightRowIndex = newSlot.Row;
233+
if (ContainerFromIndex(newSlot.Row) is TableViewRow newRow)
234+
{
235+
_editingHighlightRow = newRow;
236+
newRow.ApplyEditingHighlight(true);
237+
}
238+
else
239+
{
240+
_editingHighlightRow = null;
241+
}
242+
}
199243
}
200244

201245
MakeSelection(newSlot, false);
@@ -1502,6 +1546,25 @@ internal void SetIsEditing(bool value)
15021546

15031547
IsEditing = value;
15041548
UpdateCornerButtonState();
1549+
1550+
if (value && SelectionUnit is TableViewSelectionUnit.Row or TableViewSelectionUnit.CellOrRow)
1551+
{
1552+
if (CurrentCellSlot.HasValue)
1553+
{
1554+
_editingHighlightRowIndex = CurrentCellSlot.Value.Row;
1555+
if (ContainerFromIndex(CurrentCellSlot.Value.Row) is TableViewRow row)
1556+
{
1557+
_editingHighlightRow = row;
1558+
row.ApplyEditingHighlight(true);
1559+
}
1560+
}
1561+
}
1562+
else if (!value)
1563+
{
1564+
_editingHighlightRow?.ApplyEditingHighlight(false);
1565+
_editingHighlightRow = null;
1566+
_editingHighlightRowIndex = -1;
1567+
}
15051568
}
15061569

15071570
/// <summary>

src/TableViewCell.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ protected override void OnPointerEntered(PointerRoutedEventArgs e)
190190

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

204205
if ((TableView?.SelectionMode is not ListViewSelectionMode.None
205206
&& TableView?.SelectionUnit is not TableViewSelectionUnit.Row)
206-
|| !TableView.IsReadOnly)
207+
|| !TableView.IsReadOnly
208+
|| (TableView?.SelectionUnit is TableViewSelectionUnit.Row or TableViewSelectionUnit.CellOrRow && !IsReadOnly))
207209
{
208210
VisualStates.GoToState(this, false, VisualStates.StateNormal);
209211
}
@@ -229,6 +231,16 @@ protected override async void OnTapped(TappedRoutedEventArgs e)
229231
MakeSelection();
230232
e.Handled = true;
231233
}
234+
else if (TableView?.SelectionUnit is TableViewSelectionUnit.CellOrRow
235+
&& !IsReadOnly
236+
&& TableView is not null
237+
&& !TableView.IsEditing
238+
&& Column?.UseSingleElement is not true)
239+
{
240+
// Second tap on an already-selected cell in CellOrRow mode — start editing
241+
// (like File Explorer's tap-pause-tap to rename).
242+
e.Handled = await BeginCellEditing(e);
243+
}
232244
}
233245

234246
/// <inheritdoc/>

src/TableViewRow.cs

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public partial class TableViewRow : ListViewItem
3737
private ListViewItemPresenter? _itemPresenter;
3838
private Border? _selectionBackground;
3939
private bool _ensureCells = true;
40+
private bool _hasEditingHighlight;
41+
private bool _isBeginningEdit;
4042
private Brush? _cellPresenterBackground;
4143
private Brush? _cellPresenterForeground;
4244

@@ -177,7 +179,7 @@ protected override void OnPointerReleased(PointerRoutedEventArgs e)
177179
}
178180

179181
/// <inheritdoc/>
180-
protected override void OnTapped(TappedRoutedEventArgs e)
182+
protected override async void OnTapped(TappedRoutedEventArgs e)
181183
{
182184
base.OnTapped(e);
183185

@@ -186,15 +188,48 @@ protected override void OnTapped(TappedRoutedEventArgs e)
186188
TableView.CurrentRowIndex = Index;
187189
TableView.LastSelectionUnit = TableViewSelectionUnit.Row;
188190
}
191+
192+
// When SelectionUnit is Row and the row is already selected, forward the
193+
// tap to the target cell so editing can be initiated with a second tap
194+
// (like File Explorer's tap-pause-tap to rename).
195+
if (TableView?.SelectionUnit is TableViewSelectionUnit.Row
196+
&& IsSelected
197+
&& e.OriginalSource is DependencyObject source
198+
&& source.FindAscendant<TableViewCell>() is { IsReadOnly: false } cell
199+
&& !TableView.IsEditing
200+
&& !_isBeginningEdit
201+
&& cell.Column?.UseSingleElement is not true)
202+
{
203+
_isBeginningEdit = true;
204+
TableView.MakeSelection(cell.Slot, false);
205+
e.Handled = await cell.BeginCellEditing(e);
206+
_isBeginningEdit = false;
207+
}
189208
}
190209

191210
/// <inheritdoc/>
192-
protected override void OnDoubleTapped(DoubleTappedRoutedEventArgs e)
211+
protected override async void OnDoubleTapped(DoubleTappedRoutedEventArgs e)
193212
{
194213
var eventArgs = new TableViewRowDoubleTappedEventArgs(Index, this, Content);
195214
TableView?.OnRowDoubleTapped(eventArgs);
196215
e.Handled = eventArgs.Handled;
197216

217+
if (e.Handled) { base.OnDoubleTapped(e); return; }
218+
219+
if (TableView?.SelectionUnit is TableViewSelectionUnit.Row
220+
&& e.OriginalSource is DependencyObject source
221+
&& source.FindAscendant<TableViewCell>() is { IsReadOnly: false } cell
222+
&& !TableView.IsEditing
223+
&& !_isBeginningEdit
224+
&& cell.Column?.UseSingleElement is not true)
225+
{
226+
_isBeginningEdit = true;
227+
TableView.MakeSelection(cell.Slot, false);
228+
e.Handled = await cell.BeginCellEditing(e);
229+
_isBeginningEdit = false;
230+
return;
231+
}
232+
198233
base.OnDoubleTapped(e);
199234
}
200235

@@ -584,7 +619,7 @@ private async void EnsureSelectionIndicatorPosition(double detailsHeight, Border
584619
/// </summary>
585620
internal void EnsureAlternateColors()
586621
{
587-
if (TableView is null || RowPresenter is null) return;
622+
if (TableView is null || RowPresenter is null || _hasEditingHighlight) return;
588623

589624
RowPresenter.Background =
590625
Index % 2 == 1 && TableView.AlternateRowBackground is not null ? TableView.AlternateRowBackground : _cellPresenterBackground;
@@ -603,6 +638,43 @@ internal void UpdateSelectCheckMarkOpacity()
603638
}
604639
}
605640

641+
/// <summary>
642+
/// Highlights or unhighlights the row to indicate that a cell is being edited.
643+
/// </summary>
644+
internal void ApplyEditingHighlight(bool isEditing)
645+
{
646+
_hasEditingHighlight = isEditing;
647+
if (isEditing)
648+
{
649+
#if WINDOWS
650+
if (RowPresenter is not null && _itemPresenter?.PointerOverBackground is { } pointerOverBrush)
651+
{
652+
RowPresenter.Background = pointerOverBrush;
653+
}
654+
#else
655+
if (_selectionBackground is not null)
656+
{
657+
_selectionBackground.Opacity = 1;
658+
}
659+
#endif
660+
}
661+
else
662+
{
663+
#if WINDOWS
664+
if (RowPresenter is not null)
665+
{
666+
RowPresenter.Background = _cellPresenterBackground;
667+
}
668+
#else
669+
if (_selectionBackground is not null)
670+
{
671+
_selectionBackground.Opacity = IsSelected ? 1 : 0;
672+
}
673+
#endif
674+
EnsureAlternateColors();
675+
}
676+
}
677+
606678
/// <summary>
607679
/// Gets the height of the horizontal gridlines.
608680
/// </summary>

0 commit comments

Comments
 (0)