Skip to content

Samples: add a TableView consumer matrix for C#/C++ x packaged/unpackaged - #11691

Draft
Hitesh Kumar (hiteshkrmsft) wants to merge 33 commits into
mainfrom
user/hik/tableview-consumer-samples
Draft

Samples: add a TableView consumer matrix for C#/C++ x packaged/unpackaged#11691
Hitesh Kumar (hiteshkrmsft) wants to merge 33 commits into
mainfrom
user/hik/tableview-consumer-samples

Conversation

@hiteshkrmsft

Copy link
Copy Markdown
Contributor

Adds a four-app TableView consumer matrix under Samples/TableViewApp — C# and C++, packaged and unpackaged — proving TableView works in every host shape with no app-side workarounds. Modelled on the ChartApp samples, the existing pattern in this repo for a separately-built control set.

Split out of #11641, which had grown large. This is purely additive.

Stacking

This targets main but depends on #11641 (it needs Tabular published through the package). Its branch is based on #11641's, so until that merges this PR's diff will also show #11641's commits. Once #11641 lands, the diff here reduces to the 80 sample files. Merge #11641 first.

What the apps prove

Each references Microsoft.WindowsAppSDK.WinUI and merges XamlControlsResources plus TabularControlsResources. No projection regeneration, no manifest injection, no theme resources compiled from the control source, no staged DLL, no type seed.

Each instantiates TableView twice on purpose: once from markup, exercising XamlTypeInfo, the metadata provider and XAML-driven activation; once from code-behind, exercising direct WinRT activation with no markup involved. Those are separate paths and either can fail alone.

Verification

Built against a locally packed component package; all four launch and render, with TableView reporting ControlType.DataGrid and non-empty bounds from UIA:

App TableViews Bounds
TableViewAppCsUnpackaged 2 1376x276
TableViewAppCppUnpackaged 2 1376x276
TableViewAppCsPackaged 2 1376x276, 1376x160
TableViewAppCppPackaged 2 1376x276, 1376x160

Note on the C#/C++ binding difference

The C# apps use TableViewTextColumn.Binding with a classic {Binding}; the C++ apps use TableViewTemplateColumn.CellTemplate with x:Bind. That is a C++/WinRT data-binding rule, not a control limitation: classic {Binding} resolves properties through reflection, which C++/WinRT does not have, so a data type constructed purely from code has no IXamlType and every cell renders empty while headers, rows and theming look correct.

Not wired into CI yet

Tracked by work item 63794082. The pipeline template that would build these is resolved from the internal repo's main, so it has to land there separately.

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
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>
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>
…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>
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>
The comment claimed this entry keeps packaging from failing with APPX0703. It does
not. @(XamlProduct) drives an existence check; the payload copy is done by
_UpdateMUXFilesToCopyForApp above, via a wildcard over Product\*.dll that already
covered Tabular. The entry is still worth keeping as a validation, but it is not a
payload fix and the comment should not say otherwise.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The WinUI component package could ship Tabular's activatable-class registrations
without shipping Tabular's DLL. Every consuming .wapproj then failed packaging with
APPX0703: "Manifest references file Microsoft.UI.Xaml.Controls.Tabular.dll which is
not part of the payload."

The packaging project had exactly one ordering edge, to MergedWinMD. MergedWinMD
depends on Tabular's *idl* project, which is enough to produce the merged winmd and
therefore the registrations -- those are architecture-independent and always name
Tabular.dll -- but nothing pulled in the DLL project that produces the binary. The
payload is per-architecture and came up empty for Tabular.

Reproduced by clearing the staging directory and building only the packaging project,
as the pack step does: Microsoft.UI.Xaml.Controls.dll and both .pri files were
restored, Microsoft.UI.Xaml.Controls.Tabular.dll was not. With this build-order-only
reference the same experiment stages the Tabular DLL.

Not a cycle: the Tabular DLL project depends on MergedWinMD and MUXC's controls DLL,
neither of which depends on packaging.

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>
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>
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>
@microsoft-github-policy-service microsoft-github-policy-service Bot added the needs-triage Issue needs to be triaged by the area owners label Aug 28, 2026
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>
…aged

Four minimal apps that consume TableView the way a customer would, proving the
control works in every host shape with no app-side workarounds. Modelled on the
ChartApp samples, the existing pattern in this repo for a separately-built
control set.

Each app references Microsoft.WindowsAppSDK.WinUI and merges XamlControlsResources
plus TabularControlsResources, and each instantiates TableView twice on purpose:
once from markup, exercising XamlTypeInfo, the metadata provider and XAML-driven
activation, and once from code-behind, exercising direct WinRT activation with no
markup involved. Those are separate paths and either can fail alone.

The C# apps bind with TableViewTextColumn.Binding and a classic {Binding}; the
C++ apps use TableViewTemplateColumn.CellTemplate with x:Bind. That difference is
a C++/WinRT data-binding rule rather than a control limitation: classic {Binding}
resolves through reflection, which C++/WinRT does not have, so a data type built
only from code has no IXamlType and every cell renders empty.

Verified against a locally built package: all four apps launch and render, with
TableView reporting ControlType.DataGrid and non-empty bounds from UIA.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@hiteshkrmsft
Hitesh Kumar (hiteshkrmsft) force-pushed the user/hik/tableview-consumer-samples branch from f15bf9c to dc5a4d5 Compare August 28, 2026 16:50
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.

1 participant