Repository navigation
Map the experimental WinUI TableView and Chart into Reactor.Advanced - #1357
Draft
Alexandre Zollinger Chohfi (azchohfi) wants to merge 14 commits into
Draft
Alexandre Zollinger Chohfi (azchohfi) wants to merge 14 commits into
Alexandre Zollinger Chohfi (azchohfi) wants to merge 14 commits into
Conversation
The WinUI TableView (Microsoft.UI.Xaml.Controls.Tabular) and Chart (Microsoft.UI.Xaml.Controls.Charts) controls ship only in Microsoft.WindowsAppSDK.WinUI 2.3.10-experimental, which the 2.5.4-experimental metapackage pairs with (its nuspec names WinUI 2.3.10-experimental and pins Runtime [2.5.4-experimental]). Consequences of the bump: - CS8305 is demoted to a warning (WarningsNotAsErrors): the XAML compiler's generated XamlTypeInfo references the SDK's experimental metadata providers. - Disambiguate the IElementFactory cref (the SDK adds Microsoft.UI.Xaml.IElementFactory), which otherwise fails Release with CS0419. - Literal SDK pins in recipes/docs/CLI template/perf script follow the central floor, as WinAppSDKReferenceGuardTests requires. - Motion constants re-verified: the four WinUI transition sources are byte-identical at winui3/release/2.5.4-experimental (blob SHAs unchanged; the tag diff touches 300 other files). - The spec 063 D1 tripwire fired: at Foundation 2.3.13 LocalSettings writes the machine-local hive (measured). The guard is now hive-agnostic and the spec records the measurement; D1 itself is unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
Adds first-class Reactor elements for the experimental platform controls:
TableView (Microsoft.UI.Reactor.Advanced.Tabular)
- TableView(items, columns...) MVU-style: pass immutable record snapshots; the
handler diffs each snapshot into a collection it owns (.KeyRows for identity),
so the table never resets and selection/scroll/edits survive.
- TextColumn<T>(header, read, onEdit): the native text column (accessible
TextBlock cell, TextBox editor, UIA value pattern) fed by a pathless binding
through a C# converter. No reflection or INPC; edits arrive as a callback and
nothing is written into the row.
- TemplateColumn<T>(header, row => Element): Reactor-rendered cells hosted in a
code-built DataTemplate, re-rendered on recycle and unmounted with the table.
- BoundColumn(header, path) for classic {Binding} models.
- Sorting (typed SortBy), FilterRows/GroupRows (TableViewSource), group header
and empty content, frozen columns, density, grid lines, selection.
Chart (Microsoft.UI.Reactor.Advanced.Charts)
- Chart(LineSeries/BarSeries/AreaSeries...) with category, date-time and
linear axes, legend, markers and data labels.
- Axes are deduplicated by record equality per slot and layout (the native
chart rejects sharing across horizontal bars and other series), value
collections are shared per instance as XAML does, and everything updates in
place.
Both handlers merge TabularControlsResources / XamlChartsResources on first use
and explain the self-contained requirement for unpackaged apps instead of
surfacing an opaque COM error.
Includes selftest fixtures, unit tests, samples (TableView.Sample,
WinUIChart.Sample), and skill/README/AOT docs.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
Contributor
📦 Build metricsArtifact sizes for Packages (compressed .nupkg)
Assemblies in Microsoft.UI.Reactor
Assemblies in Microsoft.UI.Reactor.Advanced
Assemblies in Microsoft.UI.Reactor.Devtools
✅ smaller / |
Contributor
🧪 Merged coverageCoverage for
✅ higher / |
- Bind snapshot rows through a named partial collection (TableRows). Under
NativeAOT CsWinRT cannot project an anonymous ObservableCollection<object?>
assigned to the object-typed ItemsSource ("items must implement a supported
collection interface"), which failed every TableView mount in the AOT host.
- Split the edit-through-automation check into its own fixture and AOT-skip it:
in-process under NativeAOT CreatePeerForElement returns no peer for a
TableViewRow, so the fixture cannot reach the cell peer. Every other
TableView fixture, including the immutable-snapshot one, runs under AOT.
The fixture now uses the platform's own row/cell peers rather than a
hand-constructed one.
- Doc snippet gate: a command-line WarningsNotAsErrors is a global property that
replaces the props value, so it must exempt CS8305 too.
- Bootstrap: the reactor template floats Microsoft.WindowsAppSDK at "*", which
never resolves a prerelease; when the repo pin is a prerelease, pin the
scaffold to it (NU1107 otherwise).
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
- The reactor template's own post-creation restore fails under a prerelease pin (its Version="*" float resolves stable), making `dotnet new` exit non-zero before the pin step ran. Tolerate that exit only when the repo pin is a prerelease and the project was produced; the explicit restore after pinning remains the real check. - LT_OnMountUnmountBalanced failed intermittently in CI (0/25 locally) with no diagnostics; its checks now report the observed mount/unmount counts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
Get-PinnedWindowsAppSdkVersion reads with [IO.File], which resolves a relative path against the process directory, not the pwsh location pushed into TestProjects. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
The native control can release (and re-context) realized shells after the table is gone; a late DataContextChanged must not re-enter the reconciler on the unmounted table's behalf. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
Per-control .OnUnmount actions lived in a ConditionalWeakTable keyed by the FrameworkElement wrapper. Nothing keeps a plain control's RCW alive, so after a GC between mount and unmount the unmount walk sees a fresh wrapper over the same native control, the lookup misses, and the callback silently never runs. Move the action onto the native element's ReactorState, as was already done for the echo counter, validation bindings and managed resource keys for the same reason. This is what LT_OnMountUnmountBalanced hit intermittently in CI (exactly the last batch's 10 unmounts missing); GC timing in shard 2 shifted with this branch's added fixtures. LT_OnUnmountSurvivesWrapperCollection forces the GC and is deterministic: 0/10 unmounts before the fix, 10/10 after. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
…tity hash PDM_Stack/WrapGrid KeyedSwap compared RuntimeHelpers.GetHashCode of the Border's managed wrapper. A GC between the two reads collects and re-projects the wrapper, changing its identity hash with no change to the native tree, so the check failed spuriously (twice in the AOT shard). Hold the original wrappers and compare by reference, as the sibling fixtures in this file already do; a live wrapper keeps CsWinRT returning that same instance for the native Border. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
Resolves the ReactorState conflict by keeping both new fields: this branch's OnUnmountAction and main's ComponentDiagnosticId. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
- Compare layout doubles/floats within a tolerance instead of ==. - Make the TableView unmount fixture's effect counters instance-owned, handed to cells through props, instead of static fields written per run. - !table.IsReadOnly instead of == false. - AddDays(7.0 * i) in the chart sample. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
…brid) The 8 scalar TableView properties (SelectionMode, Density, GridLinesVisibility, HeadersVisibility, CanUserSortColumns, CanUserResizeColumns, RowBackground, AlternatingRowBackground) now come from [GenerateReactorDescriptor] with ClearValueOnUnset; the generated static constructor registers the descriptor. The rows, columns, templates, effective IsReadOnly, controlled selection and all events stay hand-written in the Customize hook (TableViewSync). Per-control state for TableView and Chart moves from a ConditionalWeakTable keyed by the managed wrapper to an attached DependencyProperty on the native control, so a fresh CsWinRT wrapper for the same control still finds it. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
…mental-tableview-charts # Conflicts: # src/Reactor/Core/Reconciler.cs
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7feb5bd2-d8b3-4135-8599-8bef352004c4
…mental-tableview-charts # Conflicts: # src/Reactor/Core/Reconciler.cs
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Moves the repo to the Windows App SDK
2.5.4-experimentalchannel (WinUI2.3.10-experimental), which ships two new control families, and maps both intoReactor.Advanced:Microsoft.UI.Reactor.Advanced.Tabular, overMicrosoft.UI.Xaml.Controls.Tabular.TableView), designed MVU-first:TableView(items, columns...)takes immutable record snapshots. It diffs each new snapshot into a collection it owns (.KeyRows<T>(...)for identity), so the table never resets: selection, scroll and active edits survive state changes.TextColumn<T>(header, read, onEdit)is the platform's own text column (accessible TextBlock cell, TextBox editor, UIA value pattern), fed by a pathless binding through a C# converter. No reflection, noINotifyPropertyChanged, NativeAOT-safe. Edits arrive as a callback (p with { Name = text }), and nothing is written into the row.TemplateColumn<T>(header, row => Element): Reactor-rendered cells in a code-builtDataTemplate.BoundColumn(header, path)keeps classic{Binding}available for INPC models.SortBy,FilterRows/GroupRows(viaTableViewSource), group-header/empty content, frozen columns, density, grid lines, selection.[GenerateReactorDescriptor]withClearValueOnUnset. The generated static constructor registers the control. Rows, columns, templates, the effectiveIsReadOnly, controlled selection and the events are hand-written in theCustomizehook. The events stay hand-written because a control holds a single event-payload box, so generated and custom events can't be mixed. Chart stays a hand-coded handler: the generator would cover only 2 of its properties.Microsoft.UI.Reactor.Advanced.Charts, overMicrosoft.UI.Xaml.Controls.Charts.Chart):Chart(LineSeries/BarSeries/AreaSeries...)with category / date-time / linear axes, legend, markers and data labels. Everything updates in place. Shared axes andSamplesare reconciled according to the native rules found while building this:Samples.samples/TableView.Sample,samples/WinUIChart.Sample.This supersedes #621, which hand-built a projection and checked in native binaries to reach a pre-release TableView. The control now ships in the SDK, so #621 can be closed.
Fixes found while getting CI green
.OnUnmountcould silently never fire.ConditionalWeakTablekeyed by the control's managed wrapper. If a GC collects that wrapper between mount and unmount, the unmount walk sees a fresh wrapper over the same native control and misses the entry.ReactorState, like the other per-element state that was already moved for this reason.LT_OnUnmountSurvivesWrapperCollectionforces the GC. 0/10 unmounts fired before the fix, 10/10 after. This was the intermittentLT_OnMountUnmountBalancedfailure.ConditionalWeakTablekeyed by the managed wrapper has the same wrapper-collection hazard as above.partialcollection (TableRows). Without it, CsWinRT can't project the collection and every TableView mount failed under AOT.WarningsNotAsErrorsoverrides the props file.Version="*"never resolves a prerelease).Linked issue / spec
Supersedes #621. Updates spec 063 §3.2 (the D1 tripwire fired; see below).
Test plan
tests/Reactor.Tests/ExperimentalControlsTests.cscover value normalization, axis inference and identity, the snapshot diff (RowDiff), the text-column commit rule and the column helpers.TableViewFixtures(7) andChartFixtures(3).onEdit.dotnet test tests/Reactor.Tests: passed, 14,756 succeeded, 0 failed (after mergingmain).TableView_TextColumnEditViaAutomation, skipped under AOT with a documented reason: in-process,CreatePeerForElementreturns no peer for a row.AnchorReset/Issue487/ItemsView_Parkingfailures, which fail identically on unmodifiedmainon this machine.dotnet pack src/Reactor.Advanced: the nuspec gains no generator or abstractions dependency.mainmerge. This includes Build solution, AOT / packaged / regular selftests, coverage, doc gate and bootstrap (packaged scaffold build and launch).Risk / breaking changes
WindowsAppSDKVersionis2.5.4-experimental, and CS8305 is demoted to a warning. Merge only if we're fine shipping on the experimental channel. Otherwise keep this as the integration branch until the APIs go stable.ms-appx:///, which a framework-dependent unpackaged app can't resolve. Reactor throws an explanatoryInvalidOperationException. Samples setWindowsAppSDKSelfContained=true..OnUnmountnow reliably fires even when the control's wrapper was collected. Previously such callbacks were silently skipped.LocalSettingsnow writes the machine-local hive. The guard is now hive-agnostic. D1/D2 are not revisited here.Reactor.Advanced.