Tabular: add opt-in column-header tooltips to TableView - #11764
Draft
Hitesh Kumar (hiteshkrmsft) wants to merge 1 commit into
Draft
Tabular: add opt-in column-header tooltips to TableView#11764Hitesh Kumar (hiteshkrmsft) wants to merge 1 commit into
Hitesh Kumar (hiteshkrmsft) wants to merge 1 commit into
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Adds `TableViewColumn.HeaderToolTip`, the header-side counterpart to the per-cell `CellToolTipBinding` that shipped in #11669. Every mature grid pairs the two; a header that ellipsizes or abbreviates has nowhere to put the full label today. A dependency property typed `Object`, not a `Binding`: a header is not bound against a row, so there is nothing to defer. The value is the tooltip's content, not a `ToolTip`, and the control owns the `ToolTip` and its placement (`PlacementMode.Mouse`) - the same contract as the cell path, including leaving a tooltip the app set alone. The tooltip is attached to the header cell root, so it covers the header's content, its padding and its sort affordance. Header cells are rebuilt wholesale rather than recycled, so there is no binding, no attached value and no refresh path: the content is read from the column when the header is built, and re-applied in place when the property changes. Re-applying rather than rebuilding keeps header focus and the sort affordance intact, and deliberately avoids `OnColumnHeaderChanged`, which recomputes the column's Auto width. Owned tooltips are closed before `RebuildHeaders` discards the band - clearing an open tooltip's element is the reentrant `CPopup::RemoveChild` shape. Accessibility: string content is reported through `TableViewColumnHeaderAutomationPeer::GetHelpTextCore`, composed with the column's sort state so neither is dropped. The header peer is virtual - owned by the TableView, not by the header element - so `AutomationProperties.HelpText` on the element would never reach a client; the peer publishes the text itself. As with cells, non-string content is mouse-only. Also corrects the functional spec, which claimed header tooltips could never open because the header band is `IsHitTestVisible="False"`. That attribute is on the row's selection indicator; the header band is hit-testable, which is what makes click-to-sort and resize work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Hitesh Kumar (hiteshkrmsft)
force-pushed
the
user/hik/tableview-header-tooltip
branch
from
September 2, 2026 10:51
2a7c77c to
00f2834
Compare
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.
Adds
TableViewColumn.HeaderToolTip, the header-side counterpart to the per-cellCellToolTipBindingthat shipped in #11669.A dependency property typed
Object, not aBinding: a header is not bound against a row, so there is nothing to defer.x:BindandStaticResourceboth still work against it.Design
The value is the tooltip's content, not a
ToolTip; the control owns theToolTipand its placement (PlacementMode.Mouse), and never touches a tooltip the app set. That is the merged cell contract, and the ownership record, the content rules and the teardown are reused rather than duplicated —SetOwnedToolTipgained an opt-out for publishingAutomationProperties.HelpText, and nothing else changed in the cell path.The tooltip is attached to the header cell root, so it covers the header content, its padding and its sort affordance.
Header cells are rebuilt wholesale rather than recycled, so there is no binding, no attached value and no refresh path: the content is read from the column when the header is built, and re-applied in place when the property changes. Re-applying rather than rebuilding keeps header focus and the sort affordance intact, and deliberately avoids
OnColumnHeaderChanged, which recomputes the column's Auto width — a tooltip cannot change it. Owned tooltips are closed beforeRebuildHeadersdiscards the band, since clearing an open tooltip's element is the reentrantCPopup::RemoveChildshape.Accessibility
String content is reported through
TableViewColumnHeaderAutomationPeer::GetHelpTextCore, composed with the column's sort state through a localized format string so neither is dropped. It is suppressed when it merely repeats the header's own name, so the same text is never announced twice — the header-side equivalent of the suppressionTableViewCellAutomationPeeralready does, and equally gated on the ownership record, so text the app set is never dropped.This is the one place the header genuinely differs from a cell. The header peer is virtual — owned by the
TableView, not by the header element — soAutomationProperties.HelpTexton that element would never reach a client, andGetHelpTextCorealready reported the sort state. The peer therefore publishes the tooltip text itself and merges the two, rather than the attach path settingHelpTextas it does for cells.Non-string content is mouse-only, matching the cell rule and
TabViewItem/NavigationViewItem.Spec correction
The functional spec claimed column-header tooltips were deferred because the header band is
IsHitTestVisible="False"and "a tooltip there could never open". That is wrong: the attribute is on the row's selection indicator, and the header band is hit-testable — which is what makes click-to-sort and resize work. Corrected in this PR.Not in scope
Tooltips are still not gated on truncation. The header
ContentPresenterhas noTextTrimming, so header text currently clips without an ellipsis while its cells ellipsize correctly. Making headers trim and then auto-showing a tooltip only when trimmed — for headers and cells — is the natural follow-up, and is worth its own review: it changes the default for every table, and it would also close the non-string accessibility gap, since a truncation-driven tooltip is text by construction. Group-header tooltips remain deferred until grouping is enabled.Validation
Built and verified locally:
Microsoft.UI.Xaml.Controls.dllandMicrosoft.UI.Xaml.Controls.Tabular.dllbuild and link clean (x64).Microsoft.UI.Xaml.winmdcarriesTableViewColumn.HeaderToolTipwithget_/put_accessors plusHeaderToolTipProperty, read out of the metadata tables.XamlMetadataProviderGenerated.hregisters the DP, soHeaderToolTip="…"resolves from markup.HeaderToolTipto C#, and theToolTipsPagesample — extended here with header tooltips (text on Name and City, non-string content on Bio, and a tooltip on Score that repeats its own header text to exercise the suppression) plus a button that reads the header peers' help text — compiles against it.The sample could not be run.
TableViewSampleAppcrashes at startup with0xC000027B(stowedE_UNEXPECTEDfromMicrosoft.UI.Xaml.dll) in this tree. That is pre-existing and not caused by this change: an A/B control built frommainthrough the identical package pipeline, differing only in this commit, crashes identically. So the runtime behaviour — tooltip open, placement, teardown on rebuild, in-place re-apply, and the peer's help-text composition — is left to CI and to review.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com