Skip to content

Tabular: make TableView consumable with no app-side workarounds - #11641

Open
Hitesh Kumar (hiteshkrmsft) wants to merge 33 commits into
mainfrom
user/hik/tableview-remove-workarounds
Open

Tabular: make TableView consumable with no app-side workarounds#11641
Hitesh Kumar (hiteshkrmsft) wants to merge 33 commits into
mainfrom
user/hik/tableview-remove-workarounds

Conversation

@hiteshkrmsft

@hiteshkrmsft Hitesh Kumar (hiteshkrmsft) commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Makes TableView usable from an app outside this repo. The control shipped as unreachable payload: Microsoft.UI.Xaml.Controls.Tabular.dll was packaged, but its type information was withheld from the public winmd and its theme-XBF emission was off. Only Samples/TableViewSampleApp could 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 IncludeTabularControlsInPublicMetadata gate publishes Tabular's types into the merged public winmd and the CsWinRT projection. It fixes activation at the same time: eng\winrtclassregistration.targets derives <ActivatableClass> entries from the same item mdmerge consumes, so withholding the winmd had also suppressed every registration.

IDL de-duplication. Tabular's IDL redeclared 8 Microsoft.UI.Xaml.CustomAttributes types; 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 separates controls/idl from controls/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 own resources.pri, which erases component root-map names, so the lookup threw and took class activation down with it. The URI is now authority-less ms-appx:/// with a path matching AppxPriInitialPath, 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 ProjectReference to MUXC's controls DLL, so MUXControls.sln sequences MUXC -> Tabular from the project graph, and MUXControlsTabular.sln is 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 to feature(Feature_Experimental). eng\muxfinalrelease.props leaves MUX_PRERELEASE undefined for a final release, making that feature AlwaysDisabled, so mdmerge -createPublicMetadata cuts Tabular's public winmd from 93 types to 6. Building the control there would still XamlCompile a generic.xaml whose TargetTypes no longer exist, which is WMC1006. Metadata, registrations and binary are therefore dropped together in that flavour, keeping it self-consistent - no types, no registrations, no DLL, and so nothing for APPX0703 to report.

MUXFinalRelease is the Official pipeline's "check for Preview/Stable to remove experimental APIs, uncheck for Experimental" switch, defaulting to false. 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 its Build_MUXFinalRelease leg, which is where this surfaced. The condition is written to be revisited when TableView leaves [MUX_PREVIEW], since Tabular becomes genuinely shippable in Preview/Stable at that point.

Samples

TableViewSampleApp is now an ordinary consumer. It previously regenerated its own CsWinRT projection, injected a XAML metadata provider, seeded an internal template part into XamlTypeInfo, 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 references Microsoft.WindowsAppSDK.WinUI and nothing else, and merges XamlControlsResources plus TabularControlsResources.

TabularControlsResources is required, not optional: TableView's column-header style resolves SortIndicatorForeground, which lives in Tabular's theme resources because SortIndicator ships in the Tabular DLL rather than in MUXC. Without the merge the app throws XamlParseException 0x802B000A on its first layout pass, surfacing as a 0xC000027B stowed 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

Without this PR With this PR With this PR, MUXFinalRelease=true
Total types 3306 3379 (+73) 3083
Microsoft.UI.Xaml.Controls.Tabular.* 0 70 0
Microsoft.UI.Private.Controls.* 0 0 0
XamlControlsTabular* provider types 0 3 0
Non-Tabular additions - none -
Removals - none -

The PR adds exactly 73 types, all Tabular: 70 control types plus 3 XamlTypeInfo provider 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 - SortIndicator and ResizeGripper are 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=false MUXFinalRelease=true
Product build clean, 38m clean, 35m
Tabular.dll built yes no, by design
Tabular registrations 16 0
Public winmd Tabular types 70 0

All five sample apps built against the resulting package and run, asserted through UIA rather than by a green build:

App TableViews Bounds
TableViewSampleApp 1 237x1267
TableViewAppCsUnpackaged 2 608x527, 608x160
TableViewAppCppUnpackaged 2 608x527, 608x160
TableViewAppCsPackaged 2 608x527, 608x160
TableViewAppCppPackaged 2 608x527, 608x160

The consumer apps instantiate TableView twice 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/C3859 while MUXC and Tabular compile together; -m:1 completes cleanly.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the needs-triage Issue needs to be triaged by the area owners label Aug 25, 2026
@hiteshkrmsft
Hitesh Kumar (hiteshkrmsft) force-pushed the user/hik/tableview-remove-workarounds branch from 1ae133f to 9072497 Compare August 25, 2026 10:54
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
@hiteshkrmsft
Hitesh Kumar (hiteshkrmsft) force-pushed the user/hik/tableview-remove-workarounds branch from 9072497 to 9188c44 Compare August 25, 2026 11:59
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>
@hiteshkrmsft
Hitesh Kumar (hiteshkrmsft) force-pushed the user/hik/tableview-remove-workarounds branch 2 times, most recently from afe4e6d to 74a828a Compare August 26, 2026 05:39
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>
@hiteshkrmsft Hitesh Kumar (hiteshkrmsft) changed the title Tabular: remove two packaging/build workarounds from the TableView control Tabular: publish TableView through the package so apps outside the repo can use it Aug 26, 2026
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>
@hiteshkrmsft
Hitesh Kumar (hiteshkrmsft) force-pushed the user/hik/tableview-remove-workarounds branch from 40060dc to 0db68ca Compare August 26, 2026 07:06
…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>
@hiteshkrmsft Hitesh Kumar (hiteshkrmsft) changed the title Tabular: publish TableView through the package so apps outside the repo can use it Tabular: make TableView consumable with no app-side workarounds Aug 26, 2026
@hiteshkrmsft
Hitesh Kumar (hiteshkrmsft) marked this pull request as ready for review August 26, 2026 12:46
@hiteshkrmsft
Hitesh Kumar (hiteshkrmsft) requested a review from a team as a code owner August 26, 2026 12:46
@azure-pipelines

Copy link
Copy Markdown
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>
@hiteshkrmsft

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
1 pipeline(s) were filtered out due to trigger conditions.

@hiteshkrmsft

Copy link
Copy Markdown
Contributor Author

/azp run WinUI-GitHub-PR (OneBranch)

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@hiteshkrmsft

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
1 pipeline(s) were filtered out due to trigger conditions.

@hiteshkrmsft
Hitesh Kumar (hiteshkrmsft) force-pushed the user/hik/tableview-remove-workarounds branch from 702972a to ab0e263 Compare August 27, 2026 15:02
@hiteshkrmsft

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
1 pipeline(s) were filtered out due to trigger conditions.

@hiteshkrmsft
Hitesh Kumar (hiteshkrmsft) force-pushed the user/hik/tableview-remove-workarounds branch from ab0e263 to 0ae5141 Compare August 27, 2026 17:16
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>
@hiteshkrmsft
Hitesh Kumar (hiteshkrmsft) force-pushed the user/hik/tableview-remove-workarounds branch from 0ae5141 to d0b8e71 Compare August 27, 2026 19:45
@hiteshkrmsft

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
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>
@hiteshkrmsft
Hitesh Kumar (hiteshkrmsft) force-pushed the user/hik/tableview-remove-workarounds branch from 331db51 to a051f95 Compare August 28, 2026 15:24
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>
@hiteshkrmsft

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
1 pipeline(s) were filtered out due to trigger conditions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-triage Issue needs to be triaged by the area owners

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants