mirror of
https://github.com/microsoft/microsoft-ui-xaml.git
synced 2026-10-28 16:35:19 +08:00
## Fixes Brings the TableView context menu work (reviewed in #12299, merged into `user/dipesh/feature/tableview-experimental`) into main. ## PR Type - [ ] Bugfix - [x] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description Adds context menu support to TableView for cells, column headers, rows, group headers and the table itself, from mouse, touch, keyboard (Shift+F10 / Menu key) and UI Automation. ### Current Behavior TableView has no per-column menus. A right-click only shows whatever `ContextFlyout` the framework finds on the hit element, keyboard and UIA requests have no defined target, and menus do not get the row's item as their DataContext. ### New Behavior - **New API (preview):** `TableViewColumn.CellContextFlyout` and `TableViewColumn.HeaderContextFlyout` (+ DP properties). - **Resolution, first match wins:** - Cell: a `ContextFlyout` inside the cell content → `CellContextFlyout` → the row's `ContextFlyout` → `TableView.ContextFlyout`. - Header: a `ContextFlyout` inside the `HeaderTemplate` → `HeaderContextFlyout` → `TableView.ContextFlyout`. - Rows and group headers get menus from implicit `TableViewRow` / `TableViewGroupHeader` styles; a group header's DataContext is its `TableViewGroupInfo`. - Menu items inherit the requested row's item as DataContext, so one shared menu works for every row. - Opening a menu focuses the target cell/row and never changes selection. Keyboard and UIA requests open at the focused cell. - No TableView menu opens while a cell is editing. Nested TableViews / items hosts and text controls' default flyouts are not picked up by the outer table. - Automation peers (row, cell, header, group header, table) implement `ShowContextMenu`; pooled/collapsed elements open nothing. - Editing: focus moving into a flyout the editor owns (attached flyout, button flyout, picker) keeps the edit open, including while focus rests on the flyout's `Popup`; focus in an unrelated popup commits. - Sample app: new **Context menus** page; README updated. - Tests: context menu API tests, interaction tests, editing focus tests, shared interaction test helpers. ## Customer Impact User-facing: apps can give TableView cells, headers and rows their own context menus with no code-behind wiring. Additive, preview API. ## Regression Potential - [ ] Low risk — isolated change, limited scope - [x] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change New public properties on `TableViewColumn`. Touches row/cell focus on context requests, editing focus-loss commit, and TableView automation peers. ## How Has This Been Tested? - [x] I have performed a self-review of my own code - [x] I have added tests to cover my changes - [x] Existing tests pass locally Builds (x64chk, 0 errors): - `.\initrun.ps1 .\build.cmd /q` — passed - `.\initrun.ps1 msb /q /m controls\test\MUXControlsTestApp\MUXControlsTestApp.csproj /p:Platform=x64 /p:Configuration=Debug` — passed - `.\initrun.ps1 msb /q /m controls\test\MUXControls.Test\MUXControls.Test.csproj /p:Platform=x64 /p:Configuration=Debug` — passed - `.\initrun.ps1 msb /q /restore Samples\TableViewSampleApp\TableViewSampleApp.csproj /p:Platform=x64 /p:WinUIVersion=3.0.0-dev` — passed TAEF on a test VM (115 tests: context menu API + interaction, editing API, hierarchy interaction), run in 9 batches: - `.\initrun.ps1 .\tools\run-tests-on-vm.ps1 -VMName winui-test2 -SkipPayload * -fromFile:batchN.txt` — 113/115 passed on the first run. The 2 failures (`VerifyFocusInEditorOwnedFlyoutKeepsEditOpen`, `PressAndHoldOnCellOpensCellMenu`) were fixed (product + test) and the rerun passed 4/4 including neighbouring tests. Runtime: sample app Context menus and Hierarchy pages navigated through UIA after the fixes; app stays up and tables render. ## Screenshots (if appropriate) N/A --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 75efebd4-6de1-4fe8-b075-fab226b722c6 Copilot-Session: 128a3e81-37b9-46df-b080-757f110ae417
63 lines
2.2 KiB
C++
63 lines
2.2 KiB
C++
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
|
|
|
#pragma once
|
|
|
|
#include "pch.h"
|
|
|
|
namespace TableViewDetails
|
|
{
|
|
enum class ContextMenuTargetKind
|
|
{
|
|
Body,
|
|
Header,
|
|
};
|
|
|
|
struct ContextMenuTarget
|
|
{
|
|
ContextMenuTargetKind Kind{ ContextMenuTargetKind::Body };
|
|
winrt::TableViewRow Row{ nullptr };
|
|
winrt::IInspectable Item{ nullptr };
|
|
winrt::TableViewColumn Column{ nullptr };
|
|
winrt::FrameworkElement Anchor{ nullptr };
|
|
winrt::Panel ScopeRoot{ nullptr };
|
|
};
|
|
|
|
struct ContextMenuCandidate
|
|
{
|
|
winrt::FlyoutBase Flyout{ nullptr };
|
|
winrt::FrameworkElement Anchor{ nullptr };
|
|
|
|
explicit operator bool() const noexcept { return static_cast<bool>(Flyout); }
|
|
};
|
|
|
|
// True when a header or row cell anchor is still the visible cell for column, directly under
|
|
// its cells host.
|
|
bool IsLiveCellAnchor(
|
|
winrt::FrameworkElement const& anchor,
|
|
winrt::Panel const& scopeRoot,
|
|
winrt::TableViewColumn const& column);
|
|
|
|
// True when a request's OriginalSource is scope or inside it without crossing a nested
|
|
// TableView or TableViewRow: a request from a nested table belongs to that table, even when it
|
|
// left it Unhandled.
|
|
bool IsOwnRequestSource(winrt::DependencyObject const& source, winrt::DependencyObject const& scope);
|
|
|
|
// Hooks ContextRequested on control's template root. The framework shows the control's own
|
|
// ContextFlyout before the control's handlers run, so suppression must sit below it.
|
|
template <typename Handler>
|
|
winrt::UIElement::ContextRequested_revoker HookTemplateRootContextRequested(
|
|
winrt::DependencyObject const& control,
|
|
Handler&& handler)
|
|
{
|
|
if (winrt::VisualTreeHelper::GetChildrenCount(control) > 0)
|
|
{
|
|
if (auto const templateRoot = winrt::VisualTreeHelper::GetChild(control, 0).try_as<winrt::UIElement>())
|
|
{
|
|
return templateRoot.ContextRequested(winrt::auto_revoke, std::forward<Handler>(handler));
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
}
|