TabViewItem: don't enter the drag visual state when dragging is disallowed - #11672
Open
Martin Zikmund (MartinZikmund) wants to merge 3 commits into
Open
TabViewItem: don't enter the drag visual state when dragging is disallowed#11672Martin Zikmund (MartinZikmund) wants to merge 3 commits into
Martin Zikmund (MartinZikmund) wants to merge 3 commits into
Conversation
…lowed BeginCheckingForDrag armed drag-checking on every pointer press, regardless of whether the parent TabView permits a drag gesture. With CanReorderTabs=false (and CanDragTabs defaulting to false), ListViewBase::GetIsDragEnabled() is false, so ListViewBaseItem never calls BeginCheckingForMouseDrag and never captures the pointer. The item therefore stops receiving pointer events once the pointer leaves its bounds and never sees the PointerReleased that would take it back out of DragDropVisualVisible, leaving the tab stuck in the drag visual state until it is unfocused and refocused. Only arm drag-checking when the framework will actually capture the pointer, mirroring ListViewBase::GetIsDragEnabled() plus the item's own CanDrag. CanTearOutTabs is deliberately not part of the condition: tear-out registers the tab strip as a Caption non-client region, so pointer events do not reach the item at all. Fixes microsoft#10749 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AEWdGmNFY5YbSAAf9xBmDi
Covers both directions of the drag/drop visual state:
- VerifyTabEntersDragVisualStateWhenDraggingIsEnabled: dragging a tab still
shows the drag visual when the TabView allows dragging.
- VerifyTabDoesNotEnterDragVisualStateWhenDraggingIsDisabled: with both
CanReorderTabs and CanDragTabs off, dragging a tab away and releasing the
pointer outside it neither enters DragDropVisualVisible nor leaves the tab
stuck in it.
TabViewPage gains CanDragTabs/CanReorderTabs checkboxes and a readout that
records every transition of the drag/drop visual state group on FirstTab. The
group is unnamed in the TabViewItem template, so it is located by the states it
contains. Recording the transitions (rather than only sampling the final state)
is what lets the disabled case assert that the visual state is never entered,
not merely that it was left correctly.
Verified against a local x64chk build: the second test fails without the
TabViewItem fix ("Visited states: 'DragDropVisualVisible'", with no return to
DragDropVisualNotVisible) and passes with it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AEWdGmNFY5YbSAAf9xBmDi
Copilot started reviewing on behalf of
Martin Zikmund (MartinZikmund)
August 27, 2026 12:14
View session
There was a problem hiding this comment.
Pull request overview
Fixes a TabViewItem visual-state bug where DragDropVisualVisible could be entered (and remain stuck) even when the owning TabView disallows dragging/reordering, by preventing drag-tracking from being armed in non-draggable scenarios and adding interaction tests to cover both the enabled and disabled cases.
Changes:
- Add
TabViewItem::IsDraggingAllowed()and gateBeginCheckingForDragso the drag visual state is never entered when dragging cannot occur. - Extend the TabView TestUI page with toggles for
CanDragTabs/CanReorderTabsand a UIA-readable readout/log of the first tab’s drag visual state. - Add two new interaction tests validating drag visual-state behavior when dragging is enabled vs. disabled.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| controls/dev/TabView/TestUI/TabViewPage.xaml.cs | Adds drag visual-state group discovery + state logging/readout for interaction tests. |
| controls/dev/TabView/TestUI/TabViewPage.xaml | Adds CanDragTabs/CanReorderTabs toggles and buttons/text for reading/resetting drag visual state. |
| controls/dev/TabView/TabViewItem.h | Declares IsDraggingAllowed() helper. |
| controls/dev/TabView/TabViewItem.cpp | Implements IsDraggingAllowed() and prevents drag tracking when dragging is disallowed. |
| controls/dev/TabView/InteractionTests/TabViewTests.cs | Adds regression and non-regression interaction tests for the drag visual state. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…tate groups Addresses PR review feedback: FindDragDropVisualStateGroup cast the first visual child with `as FrameworkElement` and passed the possibly-null result to VisualStateManager.GetVisualStateGroups, which would throw. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QeTCwZRu14zhNaPSPxU7vw
Martin Zikmund (MartinZikmund)
force-pushed
the
user/mzikmund/tabitem-drag
branch
from
August 28, 2026 08:21
2900fbe to
f18b913
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.
Fixes
Fixes #10749
PR Type
Description
A
TabViewItemwould enter (and get stuck in) theDragDropVisualVisiblevisual state even when the parentTabViewdisallowed dragging and reordering.TabViewItem::BeginCheckingForDragunconditionally armed drag tracking onPointerPressed.ListViewBaseItemonly captures the pointer for drag detection when the item is actually draggable, so when dragging is disallowed the item stops receiving pointer events as soon as the pointer leaves its bounds — thePointerReleased/PointerCaptureLostthat would take it back out of the drag visual state never arrives, and the state sticks until the item is unfocused and refocused.The fix adds
TabViewItem::IsDraggingAllowed(), which mirrors the conditions under whichListViewBaseItemcaptures the pointer (TabView.CanDragTabs,TabView.CanReorderTabs, or the item's ownUIElement.CanDrag).BeginCheckingForDragnow bails out early when dragging isn't allowed, so the drag visual state is never entered in the first place. When the item has no parentTabView, the previous behavior is preserved.Current Behavior
With
<TabView CanReorderTabs="False">, dragging a tab still shows the drag visual (DragDropVisualVisible) and the tab remains in that state after the pointer is released.New Behavior
With dragging and reordering disallowed, the tab never enters
DragDropVisualVisible. Behavior is unchanged when dragging or reordering is allowed, or when the item setsCanDrag="True"itself.Customer Impact
User-facing. Fixes an invalid, persistent visual state on
TabViewItemfor apps that turn off tab dragging/reordering.Regression Potential
The change is scoped to
TabViewItem's pointer-pressed drag tracking and only skips work in the case where the drag could not have been carried out anyway. All existing paths where dragging is allowed are untouched.How Has This Been Tested?
Two new interaction tests in
TabViewTests.cs:VerifyTabEntersDragVisualStateWhenDraggingIsEnabled— guards against regressing the normal case, asserting the drag visual still appears when dragging is allowed.VerifyTabDoesNotEnterDragVisualStateWhenDraggingIsDisabled— the regression test for this issue: unchecksCanDragTabs/CanReorderTabs, drags the tab off the tab strip, and asserts the tab neither visits nor is left inDragDropVisualVisible.The test page (
TabViewPage) gainedCanDragTabs/CanReorderTabscheckboxes and a small readout of the first tab's drag/drop visual state group (current state plus the log of states visited), which is what the tests assert against.