Tabular: make TableView consumable with no app-side workarounds - #11641
Open
Hitesh Kumar (hiteshkrmsft) wants to merge 33 commits into
Open
Tabular: make TableView consumable with no app-side workarounds#11641Hitesh Kumar (hiteshkrmsft) wants to merge 33 commits into
Hitesh Kumar (hiteshkrmsft) wants to merge 33 commits into
Conversation
Hitesh Kumar (hiteshkrmsft)
force-pushed
the
user/hik/tableview-remove-workarounds
branch
from
August 25, 2026 10:54
1ae133f to
9072497
Compare
MuxcActivationHandler fast-pathed every Microsoft.UI.Xaml.* activation to the MUX framework DLL, excluding only the Microsoft.UI.Xaml.Controls.Tabular.* subtree. Tabular also owns Microsoft.UI.Xaml.XamlTypeInfo.XamlControlsTabularXamlMetaDataProvider, which was therefore routed to MUX instead of resolving locally. Add an explicit kTabularOwnedNamespacesOutsideSubtree prefix set, matched on a trailing '.' so a prefix can only ever match a whole namespace component, and drop the resolved TODO. Base-framework routing is unchanged. Validated: Microsoft.UI.Xaml.Controls.vcxproj and Microsoft.UI.Xaml.Controls.Tabular.vcxproj both build with 0 errors / 0 warnings (amd64chk). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f7510cb3-5321-46fb-919b-6234e3262d94
Hitesh Kumar (hiteshkrmsft)
force-pushed
the
user/hik/tableview-remove-workarounds
branch
from
August 25, 2026 11:59
9072497 to
9188c44
Compare
Amends the comment block on the previous commit down to what the code needs; no behaviour change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… resolution Removes the workarounds that consuming apps needed for TableView. An app outside the repo can now reference the package and render a TableView with no app-side shims: no projection regeneration, no ActivatableClass injection, no WinMDReference, no metadata-provider chaining. Public metadata Drop the IncludeTabularControlsInPublicMetadata gate so Tabular types reach the public merged winmd and the CsWinRT projection, and emit their activation registrations. Corrects nuspec comments that claimed Tabular was gated out. IDL de-duplication Tabular's IDL redeclared 8 Microsoft.UI.Xaml.CustomAttributes types; reference MUXC's unmerged winmd instead so the merge is clean. idl/dll split Tabular ran MIDL, mdmerge, XamlCompile and link in one project, so XamlCompile only ever saw pre-merge metadata. Split the metadata half into controls/idl-tabular, mirroring how MUXC separates controls/idl from controls/dev/dll. MergedWinMD consumes the unmerged winmd; the dll project runs XamlCompile against the merged public winmd. Theme resources Default styles resolved through the component's resource-map alias, ms-appx://TabularControlsAlias/. That authority does not exist in a consuming app: the app's build expands every reference PRI into its own resources.pri, which erases component root map names, so the lookup threw and took class activation down with it. Use an authority-less ms-appx:/// URI whose path matches AppxPriInitialPath, exactly as MUXC does in XamlControlsResources.cpp. Verified end to end against an external packaged app: UIA reports ControlType.DataGrid with a populated header and rows where it previously reported an empty rectangle and no descendants. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Hitesh Kumar (hiteshkrmsft)
force-pushed
the
user/hik/tableview-remove-workarounds
branch
2 times, most recently
from
August 26, 2026 05:39
afe4e6d to
74a828a
Compare
controls\dev\dll-tabular\Microsoft.UI.Xaml.Common.props had drifted into a verbatim copy of controls\dev\dll\Microsoft.UI.Xaml.Common.props plus ten lines, so every MUXC build-setting change had to be mirrored by hand or the two binaries silently diverged. Import the MUXC sheet and keep only the Tabular delta: the MUXCONTROLS_TABULAR define and the conditional TABULAR_BINARY_EMITS_THEME_RESOURCES define. Imported by file-relative path rather than $(MUXCProjectRoot), because the Tabular vcxproj imports this sheet as its first import, before environment.props has defined that property. The MUXC sheet is safe to import from the sibling directory: its only non-property paths are GetPathOfFileAbove(environment.props), which resolves from the MUXC sheet's own location, and ScriptPath="..\..\tools\", which is project-relative and identical for both projects. Verified by rebuilding the Tabular DLL: same 4,209,664-byte output, zero errors. Also refreshes a TableView_themeresources.xaml comment that described theme-XBF emission as still disabled. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The solution listed only Microsoft.UI.Xaml.Controls.Tabular.vcxproj, so the metadata project split out alongside it, idl-tabular, was in no solution at all. Build order was unaffected -- MergedWinMD carries a build-order-only ProjectReference to the idl project, and the Tabular DLL carries one to MergedWinMD -- but the project was invisible to Visual Studio and to anything that enumerates the solution. Verified by building the solution: 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tabular built from its own MUXControlsTabular.sln with a second pipeline step, because the MUXC -> Tabular dependency was sequenced by step order rather than by the project graph: EnsureMuxcComponentWinMDBuilt only *asserts* that MUXC's component winmd is already on disk, and Tabular's references reached MUXC's idl project through MergedWinMD but never the controls DLL that emits that winmd. Declare the missing edge instead. The Tabular DLL now carries a build-order-only ProjectReference to controls\dev\dll\Microsoft.UI.Xaml.Controls.vcxproj, mirroring the one it already has to MergedWinMD. MUXC never references Tabular, so there is no cycle. With the graph complete, both Tabular projects join MUXControls.sln and the separate solution and its pipeline step are deleted as redundant. Verified by building the Tabular target through MUXControls.sln from the solution graph alone: 0 errors. A parallel build on a memory-constrained machine can hit C1076/C3859 while MUXC and Tabular compile together; -m:1 completes cleanly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
README.md and AGENTS.md both told readers TableView "is not yet available through the WindowsAppSDK NuGet package" and that the sample's workarounds "all disappear once the control ships in-box". Both statements are now false: the API reaches the public winmd, activation registrations are emitted, and the control's theme resources ship and resolve, so a packaged app can reference the package and use TableView with no workarounds. The sample's workarounds are not evidence of a product gap. The sample builds unpackaged and self-contained against raw build outputs so the control can be iterated on without a package round-trip, and that configuration is what requires them. Deleting them while the sample still links build outputs breaks it; they retire when the sample is re-pointed at the package. Docs only -- no build or behaviour change. The sample itself is unchanged, and was not rebuilt: this enlistment has no pack.cmd and no nuget.exe, so the mock aggregator package the sample restores against cannot be produced here. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The header comment said this project "intentionally shares controls\idl\" with the MUXC idl project and that the resulting MSB8028 was benign. That is not what the project does: it sets its own IntDir, and both IntDir and IntermediateOutputPath evaluate to BuildOutput\obj\<flavor>\controls\idl-tabular\. The MSB8028 that prompted the comment came from stale output -- .tlog and FileListAbsolute.txt files left in controls\idl\ by builds predating this project. Deleting the Tabular-named entries there clears the warning with no code change; both idl projects then build with 0 warnings. Comment only; no build or behaviour change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Hitesh Kumar (hiteshkrmsft)
force-pushed
the
user/hik/tableview-remove-workarounds
branch
from
August 26, 2026 07:06
40060dc to
0db68ca
Compare
…hinery The sample hand-wired around the product: it linked raw build output, regenerated its own CsWinRT projection, injected a XAML metadata provider into the app provider's OtherProviders, seeded an internal template part through a never-loaded DataTemplate, compiled the control's theme resources from the control source tree, merged activatable-class registrations into its own app manifest, and staged the Tabular DLL next to the EXE. All of it existed because Tabular's type information was withheld from the public winmd and its theme resources did not resolve. Both of those are fixed, so the sample can be an ordinary consumer. Restructured on the ChartApp samples, which are the existing pattern for a separately-built control set: reference Microsoft.WindowsAppSDK.WinUI, and nothing else. Deleted: TabularMetadataProviderLoader.cs provider injection shim _SplitTypeSeed.xaml XamlTypeInfo seed for TableViewRow Build/MergeIxpAppManifest.ps1 activatable-class manifest merge Removed from the project file: the mock aggregator version override, the CsWinRT projection regeneration and its two targets, the theme-resource Page/Content items sourced from controls\dev, the Tabular DLL staging, and the IXP manifest augmentation block with its three targets. 232 lines to 60. App.xaml.cs drops the provider registration, the deferred resource merges and the diagnostic logging, leaving the standard OnLaunched. App.xaml merges the control's own TabularControlsResources instead of two dictionaries copied from the control source. Verified: builds clean (0 errors) and runs. Every page renders, and the Playground shows text, template, date-picker and image columns bound to live data. The app's own PRI is 3.5 MB, so MUXC's and Tabular's resources both fold into the app resource map as designed. No projection regeneration, no manifest injection, no theme resources compiled from the repo, and no reference into the control tree. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
README.md and AGENTS.md documented a build that no longer exists: staging the control DLL, regenerating the projection, compiling theme resources from the control source, self-contained unpackaged layout, and the IXP manifest merge. Rewritten for what the sample now is -- an ordinary consumer of Microsoft.WindowsAppSDK.WinUI, modelled on the ChartApp samples. Documents the two things a reader actually needs: merge TabularControlsResources alongside XamlControlsResources, and expect CS8305/WMC1501 because TableView is MUX_PREVIEW. AGENTS.md keeps a short record of what the removed machinery was and lists the three product invariants to check if anyone ever needs it back, so the history is not lost with the code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three review findings against this branch. OSS-safety: Microsoft.UI.Xaml.Controls.Tabular.vcxproj carried "See OSClient PR 16542065" in a comment. This repo mirrors publicly and comments mirror verbatim, so an internal PR number must not ship. Rewritten to keep the technical substance with no internal marker. Correctness: the same comment, and one in BuildMacros.h, asserted that resources resolve through the component's resource-map alias rather than ms-appx:///, because ms-appx:/// "would bind to the hosting process's package". The control now does exactly the opposite, deliberately: a consuming app's build expands every referenced PRI into its own resources.pri and drops component root-map names, so binding to the hosting process's package is what works. Both comments now describe shipped behaviour and point at TabularControlsResources.cpp. Dead code: MUXTABULARALIAS_STR had zero references once the URIs stopped using it -- one occurrence in the tree, its own definition. Removed. The XamlResourceMapName and ProjectPriIndexName properties keep the alias string, since those still name the root map of the standalone .pri. Build verified: 0 warnings, 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…l bug ID XamlResourceMapName and ProjectPriIndexName named the standalone PRI's root map "TabularControlsAlias". Nothing consumes that name: the control resolves its default style through an authority-less ms-appx:/// URI, and a consuming app's build re-indexes the component PRI into its own resources.pri, discarding component root map names entirely. Verified rather than assumed. With both properties removed the emitted theme XBFs are byte-identical (all five SHA-256 hashes match) and the only PRI difference is the root map name, which falls back to the target name; the resource paths the runtime URI depends on are unchanged. MUXC sets neither property and its root map is "Microsoft.UI.Xaml", so defaulting is also the WinUI-consistent shape. AppxPriInitialPath and AppxPrependPriInitialPath stay -- those are load-bearing, since they place the XBFs at the path the default-style URI resolves. Comment added saying so, because this is the pair that actually matters. Also drops an internal bug ID from a pch.h comment. This repo mirrors publicly and comments mirror verbatim; the technical note is kept, the identifier is not. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Four source files aliased every Tabular type, each with a comment saying the alias existed to "disambiguate the real split-binary type from the stale mock projection". That mock projection is gone, so the claim was tested rather than trusted: replacing an alias with a plain namespace import compiles with no CS0104. Microsoft.UI.Xaml.Controls and Microsoft.UI.Xaml.Controls.Tabular now coexist in the same file with no ambiguity, so there is no collision to disambiguate. Aliases replaced with a namespace import and the comments deleted. App.xaml no longer merges TabularControlsResources. Verified by removing it: all eight column headers and the rows still render, because the control resolves its own theme resources. The earlier belief that consumers must merge it came from changing two things at once -- adding the dictionary and declaring columns in the same step. It was the columns that mattered. Both docs corrected; they had been instructing readers to merge a dictionary they do not need. Also drops "Real" from a Playground label that distinguished the control from a mock that no longer exists. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…aged
Four minimal apps that consume TableView the way a customer would, modelled on the
ChartApp samples, which are the existing pattern in this repo for a separately-built
control set. Each references Microsoft.WindowsAppSDK.WinUI and nothing else, and
App.xaml merges only XamlControlsResources: no projection regeneration, no manifest
injection, no theme resources compiled from the control source, no staged DLL, no
type seed, and no project reference into the control.
Each app instantiates TableView twice on purpose. One comes from markup, exercising
XamlTypeInfo and XAML-driven activation; the other is built in code-behind,
exercising direct WinRT activation. Those paths fail independently, and the defect
this matrix guards against was an activation and default-style failure, so both are
covered.
The C# apps bind with a classic {Binding} against a plain class. The C++ apps use
TableViewTemplateColumn with x:Bind against a Person runtimeclass. That asymmetry is
a C++/WinRT rule, not a control limitation: classic {Binding} resolves properties by
reflection in .NET, but C++/WinRT needs an IXamlType, which the XAML compiler emits
only for types it sees in markup, or an explicit ICustomPropertyProvider. A data type
built purely from code has neither, so {Binding} silently resolves nothing and cells
render empty while headers, rows, grid lines and theming all look correct. The README
records this so the next person does not read it as a control bug.
Preview-API warnings are left visible here, unlike TableViewSampleApp which
suppresses CS8305 because it fires 324 times across the full API surface. The README
states that split so it reads as a choice.
All four build clean and run; the C# pair and the C++ packaged app were confirmed
rendering bound data in both their tables.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Hitesh Kumar (hiteshkrmsft)
marked this pull request as ready for review
August 26, 2026 12:46
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
The comments added with the idl/dll split and the theme fix had grown into prose -- rationale, history and reasoning that belonged in the PR, not the tree. Cut to the facts a maintainer needs at the point of change, with the reasoning left to the commits that introduced it. No behaviour change; build verified 0 warnings, 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The inline explanation duplicated the README. Left the fact and the pointer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CI failed every packaged app with APPX0703: "Manifest references file Microsoft.UI.Xaml.Controls.Tabular.dll which is not part of the payload." Publishing Tabular's winmd made eng\winrtclassregistration.targets emit its activatable-class registrations into consuming app manifests, but nothing added the DLL those registrations name to the payload. eng\consumebinaries.targets lists Microsoft.UI.Xaml.Controls.dll in @(XamlProduct) and had no Tabular counterpart, so every packaged app advertised a binary it did not carry. Charts, the closest precedent, keeps both halves together and gates each on the same Exists() condition -- a metadata entry with ImplementationDll in productmetadata.props and a binary entry in WinUIDetailsBinaries -- so its registrations and payload cannot desync. Tabular had only the metadata half. Verified by building a packaged sample that CI failed on: 0 errors, and both Microsoft.UI.Xaml.Controls.dll and Microsoft.UI.Xaml.Controls.Tabular.dll are now in the app payload. The existing Error condition on @(XamlProduct) also means a missing Tabular binary now fails with a clear message instead of an APPX0703 at packaging. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tabular.ProjectImports.targets feeds MIDL a path to MUXC's unmerged winmd from dev\dll for the Microsoft.UI.Xaml.CustomAttributes types this IDL stopped redeclaring, but the idl project had no ordering edge to the project that produces it. Step order in the pipeline used to guarantee it; retiring the second solution removed that guarantee, leaving a clean-build race. An x86 build surfaced it as 52 x MIDL2009 "undefined symbol Microsoft.UI.Xaml.CustomAttributes.MUXPropertyDefaultValue". Build-order-only reference, mirroring the one the Tabular DLL project already has. MUXC's DLL project references only Lightup, so this is not a cycle. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The markup-instantiated table had its ItemsSource assigned from code-behind, which left the markup path only partly exercised: the control came from XAML but its data did not. All four apps now bind it with x:Bind, so the markup table is driven entirely from markup and the code-behind table stays the pure code path. C# exposes a People property; C++ exposes it through MainWindow.idl backed by an IObservableVector. In both, People is populated before InitializeComponent, since x:Bind evaluates there -- populate it after and the markup table silently binds to nothing while the code-built table still works. Verified on all four: each window reports 2 TableView elements and 6 TableViewRow elements, 3 from the markup table and 3 from the code table. A failed markup binding would show 3. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three comment nits from review. The two nuspec comments noting that an existing wildcard also picks up Tabular's winmd and dll/.pri restated what the pattern plainly does, and would need touching for every future component. Removed, including the second identical one in the WinUI nuspec that was not flagged but is the same noise. The file entries are unchanged. BuildMacros.h cited a bare "PRI277 collision", which explains nothing on its own. Reworded to state the collision in words; the error code stays in the Tabular vcxproj where the surrounding detail lives. Comment-only; build verified 0 warnings, 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Both tables rendered identically, so the sample did not actually show what it was demonstrating. Each now sits in its own captioned grid: "Table 1 - instantiated from MARKUP (XAML)" and "Table 2 - created in CODE-BEHIND". A nested Grid with Auto/* rows rather than a StackPanel, so the caption takes its natural height and the table still stretches; a StackPanel would give the table unbounded height. The status line said "markup TableView: True; code TableView: 1; items: 3" -- a null check and a child count rendered as text, which the captions now convey far better. Replaced with the one thing the captions do not say: that both tables are bound to the same items. All four build clean and run. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
One conflict, in controls\dev\TableView\TableView_themeresources.xaml, and it was comment-only: both sides describe the same duplicate-key hazard. Took main's tighter wording and corrected its tense -- it says "once theme-XBF emission is enabled", which is stale on this branch because emission is enabled here. Incoming: the TableView data-shaping (sort + filter) core port and the Inking file name-casing fix. The casing change renames Generated\InkToolBar* to InkToolbar*, which collided on a case-insensitive filesystem with locally regenerated codegen; the local Generated artifacts were discarded rather than merged, since they are build output and were never intended to be committed. Build verified after the merge: Tabular DLL, 0 warnings, 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
Author
|
/azp run |
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. |
Contributor
Author
|
/azp run WinUI-GitHub-PR (OneBranch) |
|
No pipelines are associated with this pull request. |
Contributor
Author
|
/azp run |
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. |
Hitesh Kumar (hiteshkrmsft)
force-pushed
the
user/hik/tableview-remove-workarounds
branch
from
August 27, 2026 15:02
702972a to
ab0e263
Compare
Contributor
Author
|
/azp run |
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. |
Hitesh Kumar (hiteshkrmsft)
force-pushed
the
user/hik/tableview-remove-workarounds
branch
from
August 27, 2026 17:16
ab0e263 to
0ae5141
Compare
Two defects kept TableView from being consumable, one at build time and one at
runtime.
Build: Microsoft.UI.Xaml-Product.sln failed in the Tabular project with
error WMC1006: Cannot resolve Assembly or Windows Metadata file
'Type universe cannot resolve assembly: winrtbase, Version=255.255.255.255'
followed by two MSB3030 errors for the generic.xaml and themeresources.xaml the
failed compile never produced. CompilePageRequiringCustomCompilation already
drops the pre-merge winmds from @(ReferencePath), which is what Pass1 consumes.
Pass2 does not read that item: for native projects the SDK's MarkupCompilePass2
has already snapshotted its references into @(XamlReferencesToCompile), by way
of @(WinMDReferenceToCompile), before this target runs. That snapshot still
carried MUXC's unmerged winmd, and unmerged winmds reference the midlrt
'winrtbase' placeholder that only mdmerge resolves, so Pass2 failed while Pass1
succeeded. Apply the same three filters to the snapshot.
Runtime: every sample crashed roughly eight seconds after launch with a
0xC000027B stowed exception, from a XamlParseException (0x802B000A) raised
during the first layout pass:
Cannot find a Resource with the Name/Key SortIndicatorForeground
TableView's column-header style resolves SortIndicatorForeground through
SortIndicator's default style, and SortIndicator ships in the Tabular DLL rather
than in MUXC - controls/Tabular.ProjectImports.targets is the only importer of
SortIndicator.vcxitems. The key is therefore defined in Tabular's
themeresources.xaml and absent from MUXC's, so merging XamlControlsResources
alone leaves it unresolvable. Merge TabularControlsResources alongside it. That
dictionary is Tabular's own theme resources and the exact analogue of
XamlControlsResources for MUXC, so this is ordinary consumption of the control
set rather than an app-side workaround.
Verified against a locally built package: all five apps launch and render, with
TableView reporting ControlType.DataGrid and non-empty bounds from UIA in the
C# and C++, packaged and unpackaged shapes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Hitesh Kumar (hiteshkrmsft)
force-pushed
the
user/hik/tableview-remove-workarounds
branch
from
August 27, 2026 19:45
0ae5141 to
d0b8e71
Compare
Contributor
Author
|
/azp run |
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. |
WMC1006 ('cannot resolve assembly: winrtbase') and the two MSB3030 errors for
the generic.xaml and themeresources.xaml that never got produced are a build
ordering failure, not a reference-filtering one. The previous attempt filtered
@(XamlReferencesToCompile) and had no effect; revert it.
In build 156031576, mdmerge wrote MergedWinMD's public output at 21:22:36 while
Tabular's XamlCompile Pass2 ran at 21:16:38, six minutes earlier. Pass2 resolves
against $(MUXCXamlConsumeWinmd), which is that output, so it was absent. Pass1
survives because only Pass2 is given LocalAssembly - this project's own unmerged
winmd - and only Pass2 therefore has to resolve that assembly's midlrt
'winrtbase' placeholder.
The ordering-only ProjectReference to MergedWinMD does not prevent this.
packaging\Microsoft.WinUI.csproj lists MergedWinMD and this project as sibling
ProjectReferences with DisableTransitiveProjectReferences set, so MSBuild
schedules them concurrently and Tabular never blocks on it.
MUXC does not hit this because its Pass2 references
$(IntermediateOutputPath)Merged\Microsoft.winmd - an artefact its own project
produces, which cannot race. Tabular's equivalent reference crosses a project
boundary.
Build MergedWinMD on demand when its output is missing, then fail with a clear
message rather than letting the compiler fall through to WMC1006. Where another
node is already building it with these properties, MSBuild blocks here instead
of racing.
Validated locally: a clean build.cmd /c build succeeds, and deleting the merged
winmd and rebuilding this project alone with BuildProjectReferences=false - so
nothing else can produce it - restores it through this target and completes
Pass2 with no WMC1006 or MSB3030.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This change is large enough already. The four-app consumer matrix under Samples\TableViewApp (C# and C++, packaged and unpackaged) is additive and reviews better on its own, so it moves to a follow-up pull request that lands after this one. TableViewSampleApp stays. Its docs no longer point at the matrix, and both the README and AGENTS.md are corrected on one point they got wrong: consuming TableView does require merging TabularControlsResources alongside XamlControlsResources. SortIndicator ships in the Tabular DLL rather than in MUXC, so SortIndicatorForeground - which TableView's column-header style resolves - is absent from MUXC's dictionary, and omitting the merge throws XamlParseException 0x802B000A on the first layout pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three conflicts, all the same shape: #11640 (ResizeGripper) extended the TableViewSampleApp workaround machinery that this branch deletes. - Build\MergeIxpAppManifest.ps1: main added the ResizeGripper activatable classes; this branch deletes the script because the package now emits registrations. Kept deleted. - TableViewSampleApp.csproj: main added -include Microsoft.UI.Private.Controls to CsWinRTFilters so the app-side projection could resolve <Style TargetType="privatecontrols:ResizeGripper"> while app-compiling the control's generic.xaml. This branch removes the projection regeneration and the app no longer compiles generic.xaml, so the filter has nothing to apply to. Kept this branch's version. - AGENTS.md: main documented those workarounds; this branch rewrote the file for a sample that has none. Kept this branch's version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pass2 failed with WMC1006 'cannot resolve assembly: winrtbase'. Reading the assembly-reference tables shows why: Tabular's metadata carries an AssemblyRef to microsoft.ui.xaml.controls.g, MUXC's unmerged winmd, which itself references the midlrt winrtbase placeholder that only mdmerge strips. Pass2 loads LocalAssembly, follows that reference and cannot resolve it. Pass1 is given no LocalAssembly, so it never walks the reference and succeeds. MUXC never hits this because its metadata has no cross-reference to another unmerged winmd. The cross-reference comes from the IDL de-duplication in Tabular.ProjectImports.targets, which points MIDL at MUXC's unmerged winmd so the shared CustomAttributes resolve there instead of being redeclared. Filter midlrt outputs by extension rather than by directory. The previous attempt matched on 'Unmerged' appearing in the path and missed controls\idl-tabular\Microsoft.UI.Xaml.Controls.g.winmd, a copy with no such segment. Apply the filters to @(XamlReferencesToCompile) as well, since that is the snapshot Pass2 reads rather than @(ReferencePath). Filtering also removes Tabular's own types, so pass this project's mdmerge output alongside, mirroring controls/dev/dll/Microsoft.UI.Xaml.Common.targets:489 where MUXC's Pass2 references its own Merged\Microsoft.winmd. It is guarded by Exists() and is produced before this target runs. Also drops the previous attempt's guard, which never executed: it hooked MarkupCompilePass2, a target skipped for this project. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WMC1006 fails only in BuildMUX Debug_x64, the one job that builds Microsoft.UI.Xaml-Product.sln, and only with MUXFinalRelease=true. The other five jobs build MUXControls.sln and never reach this path, so their passing said nothing. Product.sln has no Tabular project of its own: the control arrives solely through packaging\Microsoft.WinUI.csproj's ProjectReference, added so the package could not ship Tabular registrations without the Tabular binary. Every Tabular type is [MUX_PREVIEW] or [MUX_INTERNAL], both of which expand to feature(Feature_Experimental). eng\muxfinalrelease.props leaves MUX_PRERELEASE undefined for a final release, which makes that feature AlwaysDisabled, so mdmerge -createPublicMetadata cuts the public winmd from 93 types to 6 - only TabularControlsResources and the metadata provider survive. The project then still compiles generic.xaml, whose TargetTypes no longer exist, and XamlCompile fails; the two MSB3030 errors are the themes that compile never produced. Condition the ProjectReference on MUXFinalRelease != true. The reason it was added does not apply there: registrations derive from the same stripped metadata, so a final-release package emits no Tabular registrations and there is nothing for APPX0703 to report. Also reverts the previous attempt, which supplied $(StrippedMetadataWinmdPath) to XamlCompile. That file is the 6-type winmd above, so it was actively wrong in the flavour that fails. Measured in a clean local build with MUXFinalRelease=true - the configuration CI uses and the one no earlier attempt was ever tested against. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Excluding Tabular from the MUXFinalRelease package left the flavour
inconsistent: the binary was no longer built, but 16 activatable-class
registrations naming Microsoft.UI.Xaml.Controls.Tabular.dll were still emitted.
That is the APPX0703 mismatch the packaging ProjectReference was added to
prevent, just inverted.
The registrations derive from MuxControlsWinMD, which points at the unmerged
winmd from the idl project - 93 types, unaffected by feature gating - not at the
6-type stripped winmd that mdmerge -createPublicMetadata produces once
Feature_Experimental is AlwaysDisabled. So they survived the packaging change.
Condition the MuxControlsWinMD entry on MUXFinalRelease != true so metadata,
registrations and binary are dropped together and the flavour stays
self-consistent.
Verified by clean builds in both flavours:
MUXFinalRelease=true - builds clean, Tabular registrations 0, no Tabular.dll,
public winmd 3083 types with 0 Tabular
MUXFinalRelease=false - builds clean, Tabular.dll present, registrations 16,
public winmd 3379 types with 70 Tabular
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Both conditions hold only while every Tabular type is preview. Once TableView leaves [MUX_PREVIEW] its types survive final-release stripping, and the conditions would silently keep the control out of the release package - no build error, no test failure, just a missing control. Say so at both sites so the next person changing that attribute sees it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Hitesh Kumar (hiteshkrmsft)
force-pushed
the
user/hik/tableview-remove-workarounds
branch
from
August 28, 2026 15:24
331db51 to
a051f95
Compare
Five files lost their BOM during editing - MergedWinMD\Directory.Build.props, controls\MUXControls.sln, TabularControlsResources.cpp, BuildMacros.h and packaging\Microsoft.WinUI.csproj. That is unintended diff noise, and .sln files in particular are BOM-sensitive for Visual Studio tooling. Also shortens the two MUXFinalRelease comments; the detail belongs in the commit message rather than the build files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
Author
|
/azp run |
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. |
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.
Makes TableView usable from an app outside this repo. The control shipped as unreachable payload:
Microsoft.UI.Xaml.Controls.Tabular.dllwas packaged, but its type information was withheld from the public winmd and its theme-XBF emission was off. OnlySamples/TableViewSampleAppcould use it, and only by hand-wiring around the product.Acceptance bar: build a package, consume it from an app outside the repo, and the table must render with zero workarounds on the app side. That is verified below.
Product changes
Public metadata and activation. Dropping the
IncludeTabularControlsInPublicMetadatagate publishes Tabular's types into the merged public winmd and the CsWinRT projection. It fixes activation at the same time:eng\winrtclassregistration.targetsderives<ActivatableClass>entries from the same itemmdmergeconsumes, so withholding the winmd had also suppressed every registration.IDL de-duplication. Tabular's IDL redeclared 8
Microsoft.UI.Xaml.CustomAttributestypes; it now references MUXC's unmerged winmd instead, so the merge is clean (8 -> 0).idl/dll split. Tabular ran MIDL, mdmerge, XamlCompile and link in a single project, so XamlCompile could only ever see pre-merge metadata. The metadata half moved to
controls/idl-tabular, mirroring how MUXC separatescontrols/idlfromcontrols/dev/dll.Theme resources. Default styles resolved through the component's resource-map alias,
ms-appx://TabularControlsAlias/. That authority does not exist in a consuming app: the app's build expands every referenced PRI into its ownresources.pri, which erases component root-map names, so the lookup threw and took class activation down with it. The URI is now authority-lessms-appx:///with a path matchingAppxPriInitialPath, exactly as MUXC does. The alias properties are removed as vestigial - verified byte-identical theme XBFs, and MUXC sets neither.Build topology. Tabular's property sheet had drifted into a verbatim copy of MUXC's plus ten lines; it now imports MUXC's and keeps only the delta. Tabular also no longer needs its own solution: it declares a build-order-only
ProjectReferenceto MUXC's controls DLL, soMUXControls.slnsequences MUXC -> Tabular from the project graph, andMUXControlsTabular.slnis deleted. The matching pipeline step was removed separately in ADO PR 16623412, which has already merged.Excluded from
MUXFinalRelease. Every Tabular type is[MUX_PREVIEW]or[MUX_INTERNAL], both of which expand tofeature(Feature_Experimental).eng\muxfinalrelease.propsleavesMUX_PRERELEASEundefined for a final release, making that featureAlwaysDisabled, somdmerge -createPublicMetadatacuts Tabular's public winmd from 93 types to 6. Building the control there would still XamlCompile ageneric.xamlwhoseTargetTypes no longer exist, which isWMC1006. Metadata, registrations and binary are therefore dropped together in that flavour, keeping it self-consistent - no types, no registrations, no DLL, and so nothing forAPPX0703to report.MUXFinalReleaseis the Official pipeline's "check for Preview/Stable to remove experimental APIs, uncheck for Experimental" switch, defaulting tofalse. Experimental releases are unaffected - Tabular ships there exactly as before. Preview/Stable builds set it, and removing an experimental API from those is the intended behaviour. The PR pipeline also forces it in itsBuild_MUXFinalReleaseleg, which is where this surfaced. The condition is written to be revisited whenTableViewleaves[MUX_PREVIEW], since Tabular becomes genuinely shippable in Preview/Stable at that point.Samples
TableViewSampleAppis now an ordinary consumer. It previously regenerated its own CsWinRT projection, injected a XAML metadata provider, seeded an internal template part intoXamlTypeInfo, compiled the control's theme resources out of the control source tree, merged activatable-class registrations into its own manifest, and staged the control DLL next to the EXE. All of it is deleted - 568 lines - and the project file is 232 lines shorter. It referencesMicrosoft.WindowsAppSDK.WinUIand nothing else, and mergesXamlControlsResourcesplusTabularControlsResources.TabularControlsResourcesis required, not optional:TableView's column-header style resolvesSortIndicatorForeground, which lives in Tabular's theme resources becauseSortIndicatorships in the Tabular DLL rather than in MUXC. Without the merge the app throwsXamlParseException 0x802B000Aon its first layout pass, surfacing as a0xC000027Bstowed exception seconds after launch.The four-app consumer matrix (C#/C++ x packaged/unpackaged) moved to #11691 to keep this change reviewable. It is verified against this branch below.
Public winmd surface
MUXFinalRelease=trueMicrosoft.UI.Xaml.Controls.Tabular.*Microsoft.UI.Private.Controls.*XamlControlsTabular*provider typesThe PR adds exactly 73 types, all Tabular: 70 control types plus 3
XamlTypeInfoprovider types. Nothing unrelated is added and nothing is removed, so there is no API-surface change for existing consumers.Microsoft.UI.Private.Controls.*stays 0 in all three columns -SortIndicatorandResizeGripperare framework-internal, registered for activation but never published. The final-release column is 223 types below the pre-PR baseline because every[MUX_PREVIEW]type across MUXC is stripped there; that is the same mechanism, not this change.Validation
Clean builds in both flavours, from a wiped
BuildOutput:MUXFinalRelease=falseMUXFinalRelease=trueTabular.dllbuiltAll five sample apps built against the resulting package and run, asserted through UIA rather than by a green build:
TableViewSampleAppTableViewAppCsUnpackagedTableViewAppCppUnpackagedTableViewAppCsPackagedTableViewAppCppPackagedThe consumer apps instantiate
TableViewtwice on purpose - once from markup, once from code-behind - because those are separate activation paths and either can fail alone. Each app was audited for the workarounds this change removes: no projection regeneration, no manifest injection, no metadata-provider chaining, no type seed, no staged DLL, no references into raw build output.Note for CI: a parallel build on a memory-constrained machine can hit
C1076/C3859while MUXC and Tabular compile together;-m:1completes cleanly.