Skip to content

Conversation

@nicolaihenriksen
Copy link
Contributor

@nicolaihenriksen nicolaihenriksen commented Dec 10, 2025

Fixes #3976

This is a work in progress attempting to align the TabControl (scrolling) style with the MD spec.

The PR contains the following changes:

  • Introduces TabControlHeaderScrollBehavior in order to react the tab/scroll events.
  • Introduces custom StackPanel (currently named BringIntoViewHijackingStackPanel to signal intent) in order to hijack and re-raise the RequestBringIntoView event.
    • It sits in between the ScrollViewer and the contained TabItems. It "hijacks" the FrameworkElement.RequestBringIntoViewEvent by marking it as handled, and subsequently issue a new FrameworkElement.BringIntoView(Rect) call with a Rect matching the original event, but offset left/right based on scroll direction.
  • Adds a "padding" to the left of the first tab, and to the right of the last tab as per the MD spec.
    • This padding is only applied if the width of the tabs overflow the available width.
  • Adds smooth/animated scrolling behavior when a partially visible tab needs to be brought into view.
    • This is done by animating a custom AP which in turn calls ScrollViewer.ScrollToHorizontalOffset() - thanks @Keboo for that idea!
    • A slight hack is used to prevent users to click on anything while the (very short) animation is running.
  • Adds TabAssist.TabScrollDuration AP to allow consumers to control the animation duration. TimeSpan.Zero effectively disables the animation.
    • Default value set in Style to allow easy override at the call site.
  • Adds TabAssist.TabScrollOffset AP to allow consumers to modify the offset (i.e. "padding" from above).
    • Default value set in Style to allow easy override at the call site.
  • 2 new UI tests which currently don't assert anything, but allow for easy testing of the feature.

I have done some preliminary testing and I think it works pretty well. However I don't have a mouse with a mouse-wheel-tilt feature, so that part lacks testing (I could imagine that may cause some issues). I have tried to avoid conflicts with it by setting a private variable (_desiredScrollStart) when the scrolling is initiated from a tab change. I hope that is enough, but only testing will show.

Things to look at before PR is ready:

  • Remove debugging Debug.WriteLine statements.
  • Ensure "mouse-wheel tilt" feature behaves nicely with this feature.
  • Consider if BringIntoViewHijackingStackPanel is really the name we want for this panel.
  • Ensure FlowDirection=RightToLeft works as expected.
  • Write proper assertions in UI tests (or delete the tests if we cannot find proper assertions)
  • Consider if the slight hack preventing user input while animating can be done in a better way.
  • Other things?

Preview of the feature

ScrollingTabsFix

Adds a behavior to handle tab selection changes intended to adjust scrolling for better user experience.
Introduces a custom panel to hijack the BringIntoView event, allowing for offsetting the Rect being scrolled to based on the tab selection direction.
There is no need for the padding if all tabs can fit inside the visible region.
The intention is to eventually add some assertions, but we'll need to figure out what is relevant to assert on.
Without this hack, if the user double-clicks on a tab that is partially out of view, it will not scroll fully into view (with the desired additional "padding"). It seems to stop the animation prematurely and leave the TabControl headers in a undesirable state. This minor hack prevents this behavior.
Defaulted to True in a style setter allowing for easy override at the call site.
Default value (40, although spec says 52) is set in style to allow easy override at the call site.
Setting a duration of TimeSpan.Zero, effectively disables the animated scrolling.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TabControl does not conform to MD specs

2 participants