TableView: first-class Reactor control + a 36-page gallery mirroring TableViewSamples (POC)#621
Conversation
…ppSDK 2.0.1) Adds samples/apps/tableview-demo: a self-contained Reactor app that hosts the native C++/WinRT TableView -- built as a separate satellite binary (Microsoft.UI.Xaml.Controls.Advanced.dll) and projected to C# via CsWinRT against public WinAppSDK 2.0.1 -- live inside the Reactor mount path. Contents: - App.cs / Program.cs: Reactor Component rendering TableView(Data) - TableViewFacade.cs: the TableView(...) DSL facade via XamlHostElement - TableView.Projection/: CsWinRT projection of the native component winmd - Microsoft.UI.Xaml.Controls.Advanced.dll: the native satellite control (Content; POC artifact) - app.manifest: embedded WinRT activation mapping to the satellite DLL - selftest.ps1 + .github/workflows/tableview-demo.yml: headless self-test that asserts native activation inside the Reactor mount. On-demand CI, path-scoped to this folder so it never burdens the main Reactor build. Isolated from Reactor.slnx, so the main solution and its every-PR CI are unaffected. Consumes the split-binary control from microsoft-ui-xaml-lift PR #15930004. Verified: dotnet build green + selftest PASS (native TableView activated + 3 auto-columns + ItemsSource set). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces the raw XamlHostElement facade with a proper first-class Reactor
control, following the opt-in third-party-control pattern documented on
Microsoft.UI.Reactor.Advanced.Factories:
- Reactor.Controls.TableView/ (new library):
- TableViewElement : Element -- typed record (Items, Columns, SelectionMode,
SelectedIndex, OnSelectionChanged).
- TableViewHandler : IElementHandler<TableViewElement, TableView> -- mounts /
diffs / unmounts the native control; reconciles columns + ItemsSource +
selection as minimal writes on a pooled instance.
- Factories -- registers the handler via ControlRegistry.Register in a static
ctor (per-library trim unit) + TableView(...) DSL factories.
- Kept OUT of core src/Reactor (core must not depend on the POC control binary);
consumes the control purely through the public extensibility API.
- Demo App.cs now renders the typed TableView(Data); removed TableViewFacade.cs.
Also fixes a latent Central Package Management violation: the demo + projection
carried explicit Version= on PackageReference (NU1008 on a clean restore, which
would fail the on-demand CI). Switched to CPM form (central WinAppSDK version;
VersionOverride for CsWinRT).
Verified: dotnet build green (0 warn / 0 err) + selftest PASS (native TableView
activated + 3 columns + ItemsSource via the first-class TableViewHandler).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…I.Reactor.TableView) Makes Reactor.Controls.TableView packable so consumers reference the native control via a single PackageReference instead of a committed binary. The package bundles: - lib/: the first-class control + the CsWinRT projection (bundled via TargetsForTfmSpecificBuildOutput; not a separate package) - runtimes/win-x64/native/: the native Microsoft.UI.Xaml.Controls.Advanced.dll (NuGet auto-deploys it next to the consumer exe) - build/: a .targets + app.manifest that supply the WinRT activation manifest when the consumer has not declared one - deps: Microsoft.UI.Reactor + Microsoft.WindowsAppSDK pack-and-verify.ps1 packs core Reactor + this package to a local feed, scaffolds an external consumer that references ONLY the package, builds it, and runs it headlessly -- asserting the native control activates through the package. Verified: package structure correct; external consumer builds green; headless selftest PASS (native TableView activated + 3 columns + ItemsSource via the first-class handler, deployed entirely from the package). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The first-class control activated with data but rendered a blank body in a code-only Reactor app (no XAML-compiled metadata). Fixed with three pieces, all self-contained in the control library/package so it renders out of the box for both ProjectReference and PackageReference consumers: 1. Register the satellite XAML metadata provider via ReactorApp.RegisterControlAssembly so the WinUI XAML loader resolves controls:TableView (+ the template's primitive types) when the style closure is parsed -- a code-only app has no compiler-generated provider to auto-chain. 2. Embed + apply the control's default Style/theme closure (Styles/*.xaml): parsed into Application.Resources and assigned explicitly (implicit lookup misses a loose satellite control), so the template inflates. 3. Merge the control's localized SR_TableView* strings into the app PRI (TableViewStrings/ + post-build makepri); public WinAppSDK 2.0.1's PRI lacks them and native row/header realization throws without them. The package's build/.targets does the same merge for package consumers. Verified: 12-row grid renders headers + cells (RenderTargetBitmap, TVDEMO_SHOT); pack-and-verify.ps1 now also asserts the packaged consumer RENDERS (not just activates) -- RENDER cells=15 through a pure PackageReference. Demo render uses the typed explicit-columns API. README documents the mechanism. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tes) The blank-body bug taught us activation != rendering, but the CI selftest only checked activation. Adds a render gate: runs the demo with TVDEMO_SHOT=1, which walks the control's visual tree (composition-independent, survives headless agents) and asserts >=6 rendered cells -- guarding the regression where the satellite control activates but its Style/template never inflates (blank body). Verified: PASS -- activated AND rendered 42 cells. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…p standalone demo)
Per review feedback ("adding to the existing gallery should have been enough"),
host the native C++/WinRT TableView sample inside the existing Reactor.TestApp
gallery as a new "TableView" tab instead of a separate standalone app.
- Reactor.TestApp gains a TableView tab hosting the full 36-page TableViewSamples
gallery (TableViewSamples.Embedded) via XamlHostElement, rendering the projected
native split-binary Advanced TableView. Verified: Showcase = 16 rows + 9 column
headers via UIA + screenshot.
- Gated behind -p:IncludeTableView: default ON locally + the on-demand
tableview-gallery workflow; default OFF when GITHUB_ACTIONS=true so the shared
every-PR 'dotnet build Reactor.slnx' is unaffected. App.cs guards the tab with
#if INCLUDE_TABLEVIEW.
- Shared TableView.Projection relocated to samples/TableView.Projection/.
- Removed the standalone samples/apps/tableview-demo app + the first-class
Reactor.Controls.TableView library/NuGet (superseded by the in-gallery sample).
- Fixed a latent CPM NU1008 (WindowsAppSDK Version=) in the embedded gallery csproj.
- Replaced tableview-demo.yml with tableview-gallery.yml + a gallery selftest
(builds with the gallery ON, best-effort UIA render check).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dc9c6e4 to
103240d
Compare
…p (drop embedded gallery)
Final shape per review intent: "show tableview control in the reactor sample" +
"expose tableview control reactor consumers use" + "no separate demo sample app".
- Consumable first-class control as a library: samples/Reactor.Controls.TableView
(TableViewElement + TableViewHandler + ControlRegistry registration + embedded
Style/theme closure + code-only-host rendering enablement). This is what a Reactor
consumer references and uses:
using static Reactor.Controls.Factories;
TableView(items, columns)
- Reactor.TestApp's "TableView" tab consumes that control (Demos/TableViewControlDemo.cs)
exactly as a consumer would -- a directory of 12 people x 4 typed columns.
- Dropped the embedded TableViewSamples gallery (XamlHostElement host): it cannot
coexist with the control library in one process -- loading the control crashed the
gallery's SampleShell -> Frame.Navigate (WinRT/WinAppSDK activation conflict via the
shared projection, 0xC0000005). The first-class control is the cleaner Reactor-native demo.
- Fixed the library's Reactor P2P ref (removed GlobalPropertiesToRemove, which produced an
inconsistent Reactor build that crashed the WinAppSDK runtime under a self-contained host).
- Still gated behind -p:IncludeTableView (default ON local + on-demand tableview-control.yml;
OFF when GITHUB_ACTIONS=true) so the every-PR Reactor.slnx build is unaffected.
Verified: TestApp ON build 0 errors + UIA = 12 rows x 4 headers (Name/Age/City/Role), stable;
full 'dotnet build Reactor.slnx -c Release' with GITHUB_ACTIONS=true = 0 errors (CI-safe;
control + projection are not in the slnx).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a proof-of-concept “first-class” Reactor control wrapper for the native split-binary WinUI Microsoft.UI.Xaml.Controls.TableView (from Microsoft.UI.Xaml.Controls.Advanced.dll), plus a gated integration/demo in Reactor.TestApp and an on-demand GitHub Actions workflow to build/self-check it without impacting every-PR CI.
Changes:
- Introduces
Reactor.Controls.TableView(element + V1 handler + factories) and embeds the control’s style/theme closure so it can render in code-only Reactor hosts. - Adds a gated “TableView” tab to
Reactor.TestApp, including WinRT activation manifest + PRI string merge to satisfy the native control’s localized string lookup. - Adds a CsWinRT projection project and an on-demand workflow/script to build and (best-effort) validate UIA rendering.
Reviewed changes
Copilot reviewed 26 out of 28 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| samples/TableView.Projection/TableView.Projection.csproj | CsWinRT projection project for the satellite control winmd. |
| samples/Reactor.TestApp/TableViewStrings/Resources.resw | Localized strings required by native TableView (merged into PRI). |
| samples/Reactor.TestApp/TableViewStrings/merge-tableview-strings.ps1 | Post-build PRI merge script for TestApp. |
| samples/Reactor.TestApp/tableview-control-selftest.ps1 | On-demand build + best-effort UIA render self-test script. |
| samples/Reactor.TestApp/Reactor.TestApp.csproj | Gated IncludeTableView wiring, references, native DLL content, PRI merge target. |
| samples/Reactor.TestApp/GlobalUsings.TableView.cs | Alias to avoid SortDirection ambiguity with the projection. |
| samples/Reactor.TestApp/Demos/TableViewControlDemo.cs | New TestApp demo tab consuming the control as a library user would. |
| samples/Reactor.TestApp/app.manifest | WinRT activation manifest mapping runtime classes to Advanced.dll. |
| samples/Reactor.TestApp/App.cs | Adds conditional TableView tab + default tab selection when enabled. |
| samples/Reactor.Controls.TableView/TableViewStyles.cs | Registers metadata provider + merges embedded style/theme closure + applies style. |
| samples/Reactor.Controls.TableView/TableViewHandler.cs | V1 element handler (mount/update/unmount, columns, items, selection event). |
| samples/Reactor.Controls.TableView/TableViewElement.cs | New Reactor element record + column definition for TableView. |
| samples/Reactor.Controls.TableView/Styles/07_TableView.xaml | Embedded default style/template for TableView/TableViewRow. |
| samples/Reactor.Controls.TableView/Styles/06_EditableContentPresenter.xaml | Embedded template for a TableView primitive. |
| samples/Reactor.Controls.TableView/Styles/05_SortIndicator.xaml | Embedded template for sort indicator primitive. |
| samples/Reactor.Controls.TableView/Styles/04_MarqueeSelector_themeresources.xaml | Theme resources for marquee selection. |
| samples/Reactor.Controls.TableView/Styles/03_SortIndicator_themeresources.xaml | Theme resources for sort indicator + badge. |
| samples/Reactor.Controls.TableView/Styles/02_TableView_themeresources.xaml | Theme resources for TableView surface/metrics + keyed styles. |
| samples/Reactor.Controls.TableView/Styles/01_TabularSurfaces_themeresources.xaml | Shared tabular-surface theme tokens embedded with the control. |
| samples/Reactor.Controls.TableView/Reactor.Controls.TableView.csproj | Packable control library project + embeds styles + packs native/targets artifacts. |
| samples/Reactor.Controls.TableView/Factories.cs | Factories + handler registration via ControlRegistry. |
| samples/Reactor.Controls.TableView/build/Resources.resw | Packaged strings for consumers (merged during build via targets). |
| samples/Reactor.Controls.TableView/build/Microsoft.UI.Reactor.TableView.targets | Consumer build targets (manifest + PRI string merge). |
| samples/Reactor.Controls.TableView/build/merge-tableview-strings.ps1 | Packaged PRI merge script for consumers. |
| samples/Reactor.Controls.TableView/build/app.manifest | Packaged WinRT activation manifest for consumers. |
| .github/workflows/tableview-control.yml | On-demand workflow to build/self-test the gated TableView integration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private static bool s_init; | ||
| private static Style? s_tvStyle; | ||
| private static readonly object s_gate = new object(); | ||
|
|
||
| /// <summary>Diagnostic status of the last init attempt (surfaced by the demo selftest).</summary> | ||
| internal static string Status { get; private set; } = "(not initialized)"; | ||
|
|
||
| /// <summary>Registers the satellite XAML metadata provider. Idempotent; safe to call early.</summary> | ||
| internal static void RegisterMetadata() | ||
| { | ||
| try { ReactorApp.RegisterControlAssembly(new AdvancedXamlMetadataProvider()); } | ||
| catch (Exception ex) { System.Diagnostics.Debug.WriteLine("[TableViewStyles] register failed: " + ex); } | ||
| } |
| /// <summary>Layout height of the hosted control.</summary> | ||
| public double Height { get; init; } = 360; | ||
|
|
||
| /// <summary>Minimum layout width of the hosted control.</summary> | ||
| public double MinWidth { get; init; } = 520; | ||
|
|
| Reconciler.SetElementTag(tv, el); | ||
|
|
||
| tv.Height = el.Height; | ||
| tv.MinWidth = el.MinWidth; | ||
| if (el.SelectionMode is { } mode) |
| if (oldEl.Height != newEl.Height) | ||
| tv.Height = newEl.Height; | ||
| if (oldEl.MinWidth != newEl.MinWidth) | ||
| tv.MinWidth = newEl.MinWidth; |
| <PropertyGroup> | ||
| <IncludeTableView Condition="'$(IncludeTableView)' == '' and '$(GITHUB_ACTIONS)' == 'true'">false</IncludeTableView> | ||
| <IncludeTableView Condition="'$(IncludeTableView)' == ''">true</IncludeTableView> | ||
| <DefineConstants Condition="'$(IncludeTableView)' == 'true'">$(DefineConstants);INCLUDE_TABLEVIEW</DefineConstants> | ||
| </PropertyGroup> |
| <PropertyGroup> | ||
| <TargetFramework>net10.0-windows10.0.22621.0</TargetFramework> | ||
| <Platforms>x64;ARM64</Platforms> | ||
| <RootNamespace>Reactor.Controls</RootNamespace> |
…rendering for real
Integrate the complete 37-page native TableViewSamples gallery into the existing
Reactor.TestApp (a "TableView Gallery" tab hosting the compiled gallery via
XamlHostElement) ALONGSIDE the consumable first-class Reactor.Controls.TableView
control ("TableView" tab) - both rendering the native split-binary Advanced TableView
for real (cells + headers + colored pills/chips/stoplight tints), gated behind
IncludeTableView so shared CI stays byte-identical.
Three problems solved:
1. COEXISTENCE AV (0xC0000005 in Frame.Navigate): the separately XAML-compiled gallery
and the registered advanced projection provider each described the same native WinRT
type with a distinct native CCustomClassInfo. Fix: register the gallery's compiled
XAML metadata provider as the single winning authority at startup (App.cs).
2. BLANK BODY (headers + cells never materialized - empty grid): ROOT CAUSE is
WindowsAppSDKSelfContained. Framework-dependent activation resolves a WinAppSDK
runtime build whose ABI / ms-appx resource resolution the dev-framework-built
Advanced.dll's internal realization cannot use, so its inner-type templates
(TableViewColumnHeader / TableViewCell / row presenter) silently fail to load and no
headers/cells generate. Fix: the TestApp is now WindowsAppSDKSelfContained=true when
IncludeTableView=true (the local self-contained runtime restores full rendering).
Found by orchestrated investigation: the working standalone reference
(TableViewGallery.WinUI, which is self-contained) rendered 16 rows x 9 headers; the
only material diff was self-contained. Verified: control 12 rows x 4 headers with
cell text; gallery Showcase 16 rows x 9 headers with colored Department pills, Status
chips, stoplight Salary tints, hierarchy chevrons - identical to standalone.
3. ROW realization ordering: re-assert ItemsSource on Loaded (declarative XAML hosts get
template-first ordering for free; a code-only host data-binds before the template
applies). Enablement unified so the advanced theme closure merges into
Application.Resources exactly once (the gallery defers to Reactor.Controls.TableViewStyles).
CI-safe: the TableView projects are not in Reactor.slnx; the self-contained switch only
engages when IncludeTableView=true; dotnet build Reactor.slnx -c Release with
GITHUB_ACTIONS=true = 0 warnings / 0 errors (TableView tabs OFF, byte-identical to pristine).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ew gallery
Adds a pure-C# Reactor (MVU) TableView gallery that consumes the consumable
Reactor.Controls.TableView control directly -- the idiomatic first-class approach --
instead of hosting the compiled WinUI gallery via XamlHostElement interop. The
"TableView" tab now shows this first-class gallery; the "TableView Gallery" tab keeps
the XamlHostElement interop (full 37 native pages) for comparison.
Control surface expanded (Reactor.Controls.TableView):
- TableViewElement: SelectionMode/SelectionUnit, GridLinesVisibility, HeadersVisibility,
CanSort/Filter/Reorder/ResizeColumns, IsSelectionGutterVisible, FrozenColumnCount.
- TableColumn: a CellStyle {Text, Pill, Chip, Tint} + per-column Width.
- TableViewHandler maps these to the native props (CanUser*Columns) and sets per-column
FrozenEdge=Leading for FrozenColumnCount.
Template columns (TableViewCellVisuals.cs) render the gallery's signature visuals in a
code-only host: CellStyle.Pill/Chip/Tint build a native TableViewTemplateColumn.CellTemplate
via XamlReader.Load of a DataTemplate bound through a TableViewCellVisualConverter
(Department -> pill hue, IsActive -> Active/Inactive chip, Salary -> stoplight tint, using
the exact Showcase colors). The converter is registered through a code-created merged
dictionary (Application.Resources is a Source-set XamlControlsResources, so the indexer
throws "Local values are not allowed").
First-class gallery (Demos/TableViewFirstClassGallery.cs): a Reactor component with a page
selector -- Showcase, Selection, Sort + filter, Grid lines, Frozen columns, Headers -- each
TableView(People, columns) with { feature props }. Showcase renders 12 rows x 7 headers with
colored Department pills, Active/Inactive chips, stoplight Salary tints, and a frozen first
column, all in pure C#. Replaces the basic TableViewControlDemo (deleted).
CI-safe: self-contained + the TableView tabs still gate on IncludeTableView; dotnet build
Reactor.slnx -c Release with GITHUB_ACTIONS=true = 0 errors.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…chk+fre native binaries Make the first-class Reactor TableView gallery look like the real TableViewSamples gallery, and ship both build flavors of the native Advanced.dll so the sample can be run optimized (Release/fre) for performance or checked (Debug/chk) for debugging. Gallery (Demos/TableViewFirstClassGallery.cs, pure-C# Reactor MVU): - NavigationView shell (PaneTitle "TableView") with grouped sections (Quick start / Columns / Rows & cells / About), routed by SelectedTag. - Each page is a Component with SamplePresenter-style chrome: Heading + description + a "Try it" InfoBar + an interactive Options card (RadioButtons / ToggleSwitch / Slider) that reconfigures the live TableView, plus the table itself. - Pages: Home, Showcase, Selection, Sort + filter, Frozen columns, Headers, Grid lines, About. Showcase renders 12 rows x 7 cols with colored Department pills, Active/Inactive Status chips and stoplight Salary tints (template columns) + a frozen first column. - Demos/TableViewSampleData.cs: shared Person data + Vibrant/Text column sets. Native binary, per-configuration (both checked in, built from PR #15930004 tip c75da7b3ef, which already merges advanced-poc-v2 @149f6f270f): - native\fre\ = fre (free/optimized) Advanced.dll -> Release builds (performance). - native\chk\ = chk (checked/asserts) Advanced.dll -> Debug builds (debugging). - Reactor.TestApp.csproj selects native\$(fre|chk)\ by $(Configuration), falling back to chk if fre is absent. Named chk/fre to dodge the [Rr]elease/[Dd]ebug .gitignore rules. Verified: Release consumes amd64fre and renders for real (NavigationView + colored cells); CI-OFF `dotnet build Reactor.slnx -c Release` (GITHUB_ACTIONS=true) = 0/0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mples, and FIX sorting
Two changes driven by user feedback ("same as reference sample" + "sorting is not working").
FIX sorting (+ filtering) in the consumable control (Reactor.Controls.TableView):
- The native TableView uses a consumer-owned re-shape model: a header click only raises Sorted/
Filtered and updates the column SortMemberPath/SortDirection/Filter state -- the CONSUMER must
re-order/-filter the data itself. The handler did neither, so nothing sorted.
- TableViewHandler now sets SortMemberPath on every column and binds an ObservableCollection "view"
rebuilt from a master snapshot on Sorted/Filtered: intersect active column filters, then apply the
SortedColumns chain (by SortIndex, reflection on SortMemberPath, null-safe IComparable). Mirrors the
reference SortPage's Sorted/Filtered handler. Verified: clicking Salary sorts the rows descending.
Full reference parity for the first-class gallery:
- NavigationView shell now reproduces the real SampleShell exactly: 36 items across Home / Quick start
/ Columns / Rows & cells / Styling / Power user (+ Performance, About), with the reference labels and
Segoe Fluent glyph icons (Reactor's IconResolver renders raw glyph strings as FontIcons).
- Each page uses SamplePresenter-style chrome (reference Header + Description verbatim via TvMeta + a
"Try it" InfoBar + an Options panel + the live table). Interactive pages: Showcase, Selection, Cell
selection, Headers, Grid lines, Frozen leading/trailing (trailing via FrozenEdge setter), Column
resize, Dynamic columns, Conditional/Per-cell styling, Row colors (AlternatingRowBackground), RTL
(FlowDirection), Virtualization, Pagination, Data export, Performance. Built-in-gesture + native-only
pages render the configured table + an explanatory note.
- TableViewSampleData.ManyPeople(n) synthesizes large datasets for pagination/perf/virtualization.
Verified: all 36 pages navigate with no crash; sorting reorders rows (screenshot); CI-OFF
`dotnet build Reactor.slnx -c Release` (GITHUB_ACTIONS=true) = 0/0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…leViewSamples
User: "pages don't have the same options" + "work on every page to match, except home and
about. DO where what is right." Every page (except Home/About) now reproduces its reference
SamplePresenter.Options: the same section headers, the same controls, and a live Status / readout
block — driven through the consumable Reactor.Controls.TableView control.
Control: add OnControlReady (Action<TableView>, fired once on Loaded) so a page can capture the live
native control to drive imperative APIs (SortByColumn / SetSortColumn / ClearSort / ToggleSortDirection,
SelectAll / Select / DeselectAll, AutoSizeColumn, ScrollIntoView) and subscribe to Sorted / Filtered
for live readouts.
Gallery: uniform routing tag -> Component<Tv{Tag}Page>(); TvSample gains Section(header, caption, …)
and Readout(label, value); TvFx (brush + FreezeTrailing) and Home/About fold into the shell.
Pages (4 section files, ~34 components):
- Quick start — Showcase (mode/row-count/banding/gutter), Selection (mode combo + Select first/last/all/
clear + live count/index/changes), Cell selection (unit combo), Multi-column sort (programmatic presets
+ Sorted-fires + priority readout), Per-column filtering (Filtered-fires + visible count), Inline edit
(IsReadOnly toggle), Keyboard nav (shortcut table).
- Columns — Column resize (column combo + Min/Width/Max sliders with value + Status), reorder + autosize,
drag-reorder gesture, dynamic columns (per-column visibility), sticky headers, headers visibility,
frozen leading / trailing.
- Rows & cells — row reorder, grouped rows, hierarchy, row colors (banding), grid lines, row templates,
row details, mixed controls, marquee.
- Power user / Styling — conditional + per-cell styling, advanced filter, clipboard, persisted layout,
RTL, virtualization (source/size combos), pagination (real paged window), data export (CSV/TSV/JSON
preview), performance (timed sort/filter).
Where a behaviour is native-only (grouping, hierarchy, whole-row templates, row details, in-cell controls,
real clipboard/layout serialization), the page still renders the configured table + the matching option
controls + a TvSample.NativeNote, and computes the readouts it genuinely can.
Verified: 36/36 pages navigate with no crash; imperative options work (Select all -> 12 selected; Sort by
Salary -> rows reshape; Column resize sliders drive Width/Min/Max with live Status); CI-OFF
`dotnet build Reactor.slnx -c Release` (GITHUB_ACTIONS=true) = 0/0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…oc-v2 + mirror rounded corners Forward-ported the latest control branch user/hik/tableview/advanced-poc-v2 (commit f0ca535ec4, "rounded corners — outer container + row selection/keyboard focus") into the separate-binary PR #15930004 branch user/hik/tableview/advanced-binary-poc-v2 (merge 5aa1358b01, pushed), then rebuilt BOTH satellite flavors and refreshed the checked-in binaries: - native\chk\ amd64chk (9.64 MB) - native\fre\ amd64fre (1.73 MB) The consumable control applies its own Reactor-side default Style (Styles/07_TableView.xaml), which fully replaces the native ControlTemplate — so it is kept in sync with the native TableView.xaml: CornerRadius 8 on the outer container Border + CornerRadius 4 on the row Border (TemplateBinding), matching f0ca535ec4 so the rounded corners actually render in the Reactor host. Verified: Release sample consumes amd64fre and renders (NavigationView + 36 pages + options); build 0/0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ive updates (Showcase) Three reference-parity gaps the user called out: - Table height: the tables were a fixed 460px, leaving empty space. Added a Stretch flag to the control (VerticalAlignment/HorizontalAlignment=Stretch + MinHeight 320, no fixed Height) and made TvSample.Page fill the available height (table grows, options rail scrolls). Every page now stretches. - Hierarchy: exposed HierarchicalItems + HierarchicalChildrenPath on the control (binds the native HierarchicalItemsSource + HierarchicalChildrenPropertyName). Showcase now DEFAULTS to Hierarchical (matching the reference) with real expand chevrons; verified 4 roots expand to 8 rows. - Live updates: added an observable LivePerson model (Salary/IsActive raise PropertyChanged so bound cells + tint converters re-run IN PLACE, no re-bind), plus LivePeople(n) + HierarchyRoots(). Showcase gains a Live-updates rail (Off/1000/500/200 ms) driven by UseEffect + DispatcherTimer; verified the Salary tints recolor live (205 px changed across 1.5s). Showcase options now mirror the reference: Mode (Flat/Grouped/Hierarchical), Row count, Live updates, Appearance (vibrant + banding), Selection gutter, Status readouts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ge, Groups expand/collapse, more live updates Orchestrated pass (3 sub-agents, one per section file) filling the gaps the user flagged: - Stretch everywhere: removed all fixed `height:` args across Quick start / Columns / Rows / Power so every table fills the available height via TvSample.Page (content-height + MinHeight 320, matching the reference SamplePresenter example container). - Hierarchy page is now a REAL tree-grid (HierarchicalItems = HierarchyRoots(), children "Children"), with Expand all / Collapse all / Toggle selected actions + IsHierarchical / ChildrenPropertyName / Selected node readouts. Verified 4 roots expand to 8 rows with chevrons + indentation. - Groups page captures the control and calls the native ExpandAllGroups() / CollapseAllGroups(), with real group-count / total / per-group-count readouts. - Live updates added to the per-cell styling (CellStyling) + row-colors pages (observable LivePerson + UseEffect DispatcherTimer mutating Salary in place), matching the reference. Verified tints recolor. - Columns pages use native MoveColumn / ResetColumnOrder / AutoSizeAllColumns / column Visibility. Verified: 36/36 pages navigate no-crash; Hierarchy 4→8 on expand; CellStyling live updates recolor; CI-OFF `dotnet build Reactor.slnx -c Release` (GITHUB_ACTIONS=true) = 0/0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… by default Two follow-ups from user feedback: - "No scrolling option to go down": the NavigationView content host measures the page unconstrained, so the inner options ScrollView never activated and the rail was cut off. Reverted to a page-level ScrollView (like the reference SamplePresenter's ScrollViewer) so the whole page scrolls and every option (down to the Status readouts) is reachable. The table is content-height by default (grows with its rows, no dead space); the data-heavy Virtualization + Performance pages pass an explicit tableHeight so their tables stay fixed, virtualizing viewports (don't realize 50k/100k rows). - "Table should be expanded by default, first level": added ExpandFirstLevel to the control; after binding HierarchicalItemsSource it enqueues tv.ExpandItem(root) for each root (once the tree is realized). Set on the Showcase (default Hierarchical) + Hierarchy + GridLines-hierarchical tables. Verified the Showcase default now shows 23 rows (4 roots expanded to their children) instead of 4 collapsed. Verified: 36/36 pages no-crash (Virtualization stays bounded); page scrolls to all options; CI-OFF `dotnet build Reactor.slnx -c Release` (GITHUB_ACTIONS=true) = 0/0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
User: "I don't see sample code in each page." Added a collapsible "Source code (C#)" expander
(monospace, horizontally scrollable) under each page's example — mirroring the reference
SamplePresenter's source viewer. TvSample.Page gains a `sourceCode` parameter rendered via
TvSample.SourceCode().
Every feature page (34 — all except Home/About) now carries a ~12–25 line verbatim C# snippet of
its KEY Reactor usage: the TableView(items, columns) with { … } config plus the relevant options /
OnControlReady imperative / UseEffect timer code, accurate to what the page does. Authored via 3
section sub-agents (Columns/Rows/Power) + the orchestrator (Quick start). Sub-agents also tightened
each page's Options / Status readouts against the reference page.
Verified: build ON 0/0; 36/36 pages navigate no-crash; Source code expander present + renders the
snippet (screenshot); CI-OFF `dotnet build Reactor.slnx -c Release` (GITHUB_ACTIONS=true) = 0/0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@DDancingDeath please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
… + sizing) From the PR reviewer + a thorough self-review: Control correctness: - ItemsSource contract: the handler snapshotted the consumer's collection, so in-place add/remove/move on a bound ObservableCollection was invisible (e.g. RowReorder's "Move 0→3 (API)" did nothing). Now subscribe to the source's INotifyCollectionChanged and re-sync the master snapshot (re-applying the active sort/filter); unsubscribe on rebind + unmount. Verified Move 0→3 now reorders the rows. - Metadata provider registered EXACTLY once (cached + double-checked-lock guard) instead of a fresh provider on every Factories/EnsureInit call. Layout sizing honors framework modifiers: - Element Height is now nullable — written only when explicitly set (else .Height()/.MinHeight() modifiers or Stretch win). Removed the vestigial element-level MinWidth (use the .MinWidth() modifier). x64-only native path (the satellite DLL is win-x64 only): - IncludeTableView auto-off on non-x64 + a build-time _TableViewArchGuard error if forced on another arch. - Restricted the control package to <Platforms>x64</Platforms>. Perf: - Virtualization page caches its dataset by size (was re-materializing 10k–50k rows + rebinding every render, snapping scroll back). Showcase passes an explicit tableHeight so Flat+1000 stays virtualized. Verified: build ON 0/0; 36/36 pages no-crash; Move 0→3 reorders; CI-OFF slnx Release (GITHUB_ACTIONS=true) = 0/0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Thanks for the review. Addressed all of it in Inline comments
From a deeper self-review (also fixed in
This remains a draft / self-contained POC (gated OFF in shared CI, x64-only), so it doesn't touch any existing Reactor project's every-PR build. |
The package's runtimes/win-x64/native asset pointed at ..\Reactor.TestApp\Microsoft.UI.Xaml.Controls.Advanced.dll, which moved to native\fre\ (and native\chk\) when chk/fre flavors were split. dotnet pack therefore referenced a non-existent file. Repoint to the optimized fre (release) satellite so the package builds and ships the native control at runtimes/win-x64/native/. Verified: pack produces the nupkg with the 1.69 MB fre Advanced.dll at the correct path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The vendored native TableView (Advanced.dll) is an unshipped WinRT satellite, so framework-dependent activation against the installed public WinAppSDK runtime renders a blank table. The consuming app must set WindowsAppSDKSelfContained=true until the native control ships in public WinAppSDK. The consumer targets injected the manifest and merged PRI strings but were silent about this, so a consumer hit a mystifying empty grid. Add a non-fatal RTV0001 build warning (unpackaged win-x64 only) guiding the consumer to set self-contained. Affects PackageReference consumers only; the in-repo sample uses ProjectReference and already sets the flag. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The native split-binary TableView satellite was renamed upstream
(microsoft-ui-xaml-lift PR #15930004, branch tabular-binary-rename):
Microsoft.UI.Xaml.Controls.Advanced.{dll,g.winmd} ->
Microsoft.UI.Xaml.Controls.Tabular.{dll,g.winmd}. The WinRT namespace is
unchanged (Microsoft.UI.Xaml.Controls), so runtimeclass names stay the same;
only the binary/winmd file name plus two resource classes renamed
(AdvancedControlsResources -> TabularControlsResources,
XamlControlsAdvancedXamlMetaDataProvider -> XamlControlsTabularXamlMetaDataProvider).
Rebuilt both flavors of the renamed satellite from dll-tabular (chk 9.4 MB,
fre 1.69 MB - fre identical size to the old fre Advanced, confirming a
content-neutral rename) plus the Tabular.g.winmd, and updated every consumer:
- native\{chk,fre}\...Tabular.dll (was Advanced.dll)
- TableView.Projection: CsWinRTInputs winmd + CsWinRTIncludes resource/provider
class names -> Tabular
- app.manifest (both copies): <file name> + AdvancedControlsResources +
XamlControlsAdvancedXamlMetaDataProvider activatableClass -> Tabular
- TableViewStyles.cs: metadata-provider alias target + TabularControlsResources
- Reactor.TestApp.csproj / Reactor.Controls.TableView.csproj: DLL paths, pack
asset, Content link, x64 guard text -> Tabular
- doc comments + merge-string-script comment -> Tabular (string-merge root is the
binary-name-independent Microsoft.UI.Xaml/Resources subtree, so unaffected)
Verified: Debug-ON build 0 errors; Release-ON build 0 errors (fre 1.69 MB copied);
CI-OFF (Reactor.slnx, IncludeTableView off) 0/0; both flavors launch and render
(UIA: DataGrid=1, 7 column headers, 23 rows on Showcase).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
What this adds
A first-class Reactor (MVU) control for the native split-binary WinUI
Microsoft.UI.Xaml.Controls.TableView(shipped inMicrosoft.UI.Xaml.Controls.Advanced.dll), plus a 36-page gallery inReactor.TestAppthat mirrors the native TableViewSamples gallery — every page rendering for real (cells + headers + colored pills/chips/stoplight tints).1. The consumable control —
Reactor.Controls.TableViewAny Reactor consumer can drop in
TableView(items, columns)in pure C#. It is reconciled by a realTableViewHandler(mount/update/unmount on a pooled control), not a XAML island:CellStyle.Text/Pill/Chip/Tint) via a cell-template bridge + value converter.Sorted/Filtered; the handler re-orders/-filters the boundObservableCollectionfrom a master snapshot (columns get aSortMemberPathautomatically).OnSelectionChanged), frozen leading/trailing, grid lines, headers visibility, column resize/reorder, hierarchy (HierarchicalItems+HierarchicalChildrenPath+ExpandFirstLevel), row banding, RTL, and anOnControlReadyhook to drive imperative APIs (SortByColumn,SelectAll,AutoSizeColumn,ExpandAllGroups, …).PropertyChangedso bound tint cells re-run their converter without a re-bind).2. The gallery — first-class, mirrors the reference
Reactor.TestAppgets a TableView section: aNavigationViewshell with the reference's grouped sections (Quick start / Columns / Rows & cells / Styling / Power user / Performance / About) and all 36 pages. Each page has the reference SamplePresenter chrome: heading + description + a "Try it" InfoBar + an interactive Options panel (combos/toggles/radios/sliders/buttons + live Status readouts) + the live table + a collapsible "Source code (C#)" section showing the page's key Reactor usage.How it renders in a code-only host
The dev-framework-built satellite renders blank in a framework-dependent host. Fixes:
WindowsAppSDKSelfContained=truewhen the TableView path is on (the activation ABI must match) — the key fix that makes the body realize.Application.Resourcesexactly once (two copies access-violate the nextFrame.Navigate), assign the default Style explicitly, and install aDispatcherQueueSynchronizationContextso the control's realization continuations resume on the UI thread.Native binaries (both flavors, x64-only)
The satellite is checked in as both flavors and selected by sample
$(Configuration):native\fre\— fre (free/optimized) → Release builds, for performance.native\chk\— chk (checked/asserts) → Debug builds, for debugging.Built from the separate-binary control PR (
advanced-binary-poc-v2), which folds in the canonical Advanced control branch.CI safety
The TableView projects are not in
Reactor.slnx;IncludeTableViewdefaults OFF in shared CI (GITHUB_ACTIONS) and on non-x64, so every existing Reactor PR build stays byte-identical. An on-demand workflow (tableview-control.yml) builds + best-effort UIA-validates the integration.Review feedback addressed
Heightis nullable (written only when explicitly set, else.Height()/Stretchwin); the vestigial element-levelMinWidthwas removed.IncludeTableViewauto-off on non-x64 + a build-time guard; the control package restricted tox64(the native asset is win-x64 only).