Fixes #4869. Add ListView<T> with typed Value, SelectedItem, Index#4870
Fixes #4869. Add ListView<T> with typed Value, SelectedItem, Index#4870tig merged 12 commits intogui-cs:v2_developfrom
Conversation
…perties - ListView<T> extends ListView and implements IValue<T> - Value (T?) is the primary property returning the selected object - SelectedItem (new T?) is a convenience alias for Value, mirroring how ListView.SelectedItem aliases ListView.Value - Index (int?) provides read/write access to the underlying int? index - SetSource(ObservableCollection<T>?) wires the typed source - Event bridge translates base int? ValueChanging/ValueChanged events to typed T? events; cancellation propagates back correctly - 22 unit tests in UnitTestsParallelizable Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Shows: - ListView<T>.Value returning typed Country object (not int index) - SelectedItem as a convenience alias for Value - Index property for int? index access - Typed ValueChanging<Country?>/ValueChanged<Country?> events - Cancellation via ValueChanging.Handled Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ListWrapper<T>.AspectGetter (Func<T,string>?) lets callers provide a custom display-text converter instead of relying on ToString(). Setting the property recalculates MaxItemLength immediately. ListView<T>.AspectGetter exposes the same delegate and wires it to the underlying ListWrapper<T> automatically — works whether set before or after SetSource(). GenericListView UICatalog scenario updated to use: AspectGetter = c => c.Name Tests added: - ListWrapperTests.cs (4 new tests for ListWrapper<T> directly) - ListViewTTests.cs (2 new integration tests via ListView<T>) Total: 28 passing tests across both files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
71150f2 to
70b5b93
Compare
Replaces inheritdoc with explicit documentation clarifying that GetValue() returns the typed T? object (not the int? index that base ListView.GetValue() returns). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Do you think there would be any value in refactoring DropDownList to be based on this? |
Yes I do, although I'm not sure how it would behave in Editable mode. It would be pretty handy if it behaved like Prompt{T} for editing complex types. |
There was a problem hiding this comment.
Pull request overview
Adds a strongly-typed ListView<T> API on top of the existing ListView/ListWrapper<T> infrastructure, enabling selection/value access as the selected object (not just an index) and enabling a simple AspectGetter for display text conversion.
Changes:
- Introduces
Terminal.Gui.Views.ListView<T>with typedValue/SelectedItem,Index, typedValueChanging/ValueChanged, andAspectGetter. - Extends
ListWrapper<T>withAspectGetterand integrates it into rendering and max-item-length calculation. - Adds a new UICatalog scenario plus new unit tests covering the generic view and wrapper behavior.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| Tests/UnitTestsParallelizable/Views/ListWrapperTests.cs | Adds tests for ListWrapper<T>.AspectGetter behavior and MaxItemLength recalculation. |
| Tests/UnitTestsParallelizable/Views/ListViewTTests.cs | Adds tests for ListView<T> typed value semantics, typed events, cancellation, and AspectGetter forwarding. |
| Terminal.Gui/Views/ListView/ListWrapper.cs | Implements AspectGetter and uses it for rendering and width calculation. |
| Terminal.Gui/Views/ListView/ListViewT.cs | Adds the new generic ListView<T> type and event/value translation from index-based selection. |
| Examples/UICatalog/Scenarios/GenericListView.cs | Adds a scenario demonstrating typed selection, typed events, cancellation, and AspectGetter. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@tig @BDisp @tznind I have some questions about how to move forward with Problems
Options
|
|
I vote 1. AspectGetter is more elegant but given LV already has a way of doing custom rendering we can just leverage that. An AspectGetter approach could be added later. |
|
I also vote for option 1 because |
- Remove AspectGetter and ColorGetter from ListWrapper<T> and ListView<T> - Delete IAspectGetter<T> and IColorGetter<T> interfaces (no longer needed) - Fix GetMaxLengthItem(): use .GetColumns() instead of .Length for correct Unicode width measurement; rename pattern variable u -> s - Update GenericListView scenario to demonstrate the same functionality using built-in mechanisms (per BDisp's suggestion): - Country.ToString() override for display text (replaces AspectGetter) - ListView.RowRender event for per-row coloring (replaces ColorGetter) - Remove AspectGetter/ColorGetter tests from ListViewTTests.cs - Remove ListWrapperTests.cs (all tests were AspectGetter/ColorGetter) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Render() and GetMaxLengthItem() cosmetic refactors (object? vs T typedItem, switch statement vs switch expression) are reverted so ListWrapper<T> is minimal-diff from upstream. The GetMaxLengthItem() bug fix is retained: use .GetColumns() instead of .Length for correct Unicode width measurement, and rename pattern variable u -> s (per PR review feedback). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Document that setting Value to an object not in the collection is a no-op (selection unchanged), and explain why this differs from the base ListView.SelectedItem setter which throws ArgumentException for out-of-range indexes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Pretty sure the failing test is not from me: |

Fixes
Proposed Changes
ListView<T>New generic view extending
ListViewand implementingIValue<T>. The key difference fromListView:Valuereturns the actual selected object of typeTrather than the selected index.Valuenew T?SelectedItemnew T?Value, mirrorsListView.SelectedItemIndexint?(get/set)AspectGetterFunc<T,string>?SetSource(ObservableCollection<T>?)ValueChangingevent EventHandler<ValueChangingEventArgs<T?>>ValueChangedevent EventHandler<ValueChangedEventArgs<T?>>SelectedItemfollows the existing doc convention: a convenience property that is an alias for Value.Indexis the way to get/set the integer index when usingListView<T>.Typed
ValueChanging/ValueChangedevents are produced via an event-bridge that subscribes to the baseint?events and translates them toT?. Cancellation in the typed event correctly propagates back throughintArgs.Handled.ListWrapper<T>.AspectGettercsharp public Func<T, string>? AspectGetter { get; set; }When set,
Render()andGetMaxLengthItem()use the delegate instead ofToString().MaxItemLengthis recalculated immediately when the property is set. Fully backward-compatible —nullby default.ListView<T>.AspectGetterforwards the delegate to the underlyingListWrapper<T>, whether set before or afterSetSource().UICatalog scenario
New
GenericListViewscenario demonstrating:ListView<Country>backed byObservableCollection<Country>ValueChangedargs (provesValueis the object, not an index)Indexlabel showing the int? alongside the typed valueValueChanging.HandledAspectGetter = c => c.Namefor clean displayTests
28 new passing tests, 0 failures:
ListViewTTests.cs— 24 tests coveringValue,SelectedItem,Index,ValueChanging/ValueChanged, cancellation,AspectGetterintegrationListWrapperTests.cs(new file) — 4 tests directly onListWrapper<T>.AspectGetter(null fallback, delegate used, MaxItemLength updated, clearing recalculates)Pull Request checklist
Fixes #issue. Terse description.dotnet testrun before commit — all 28 new tests pass, 0 failures///XML docs on all public members)GenericListViewscenario included)