Add SliderExtensions (mouse-wheel attached properties)#851
Conversation
Adds SliderExtensions.IsMouseWheelEnabled and SliderExtensions.MouseWheelChange attached properties to the Extensions component, allowing Slider controls to respond to mouse-wheel input. Includes a sample (SliderWheelSample) and docs page. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds an opt-in SliderExtensions API to enable mouse-wheel interaction for WinUI Slider controls, plus a gallery sample and documentation to demonstrate usage within the Extensions component.
Changes:
- Introduces
SliderExtensions.IsMouseWheelEnabledandSliderExtensions.MouseWheelChangeattached properties to adjustSlider.Valueon wheel input. - Adds a new interactive sample page (
SliderWheelSample) demonstrating the attached properties. - Adds a new docs page describing the new API and linking the sample.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| components/Extensions/src/Slider/SliderExtensions.cs | New attached properties and wheel handler implementation for Slider. |
| components/Extensions/samples/SliderExtensions/SliderWheelSample.xaml.cs | Sample page code-behind wiring the toolkit sample metadata. |
| components/Extensions/samples/SliderExtensions/SliderWheelSample.xaml | Sample UI demonstrating wheel-over-slider behavior. |
| components/Extensions/samples/SliderExtensions.md | Documentation for SliderExtensions and its attached properties. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public static readonly DependencyProperty IsMouseWheelEnabledProperty = DependencyProperty.RegisterAttached( | ||
| nameof(GetIsMouseWheelEnabled)[3..], | ||
| typeof(bool), | ||
| typeof(SliderExtensions), | ||
| new PropertyMetadata(false, OnIsMouseWheelEnabledChanged)); | ||
|
|
||
| /// <summary> | ||
| /// Attached <see cref="DependencyProperty"/> for the value added to or subtracted | ||
| /// from <see cref="RangeBase.Value"/> per mouse-wheel notch. Defaults to | ||
| /// <see cref="double.NaN"/>, which means "use the slider's own | ||
| /// <see cref="RangeBase.SmallChange"/>". | ||
| /// </summary> | ||
| public static readonly DependencyProperty MouseWheelChangeProperty = DependencyProperty.RegisterAttached( | ||
| nameof(GetMouseWheelChange)[3..], | ||
| typeof(double), | ||
| typeof(SliderExtensions), | ||
| new PropertyMetadata(double.NaN)); |
There was a problem hiding this comment.
Yeah. This is kind of clever, but weird.
I think it makes the code less readable, so I'm against it
| } | ||
|
|
||
| double step = GetMouseWheelChange(slider); | ||
| if (double.IsNaN(step) || step <= 0) |
|
@copilot fix the xaml styling. |
Avid29
left a comment
There was a problem hiding this comment.
Besides the suggestions, I wonder how we'd expand this to work with the RangeSelector? Though perhaps that's a separate issue.
| public static readonly DependencyProperty IsMouseWheelEnabledProperty = DependencyProperty.RegisterAttached( | ||
| nameof(GetIsMouseWheelEnabled)[3..], | ||
| typeof(bool), | ||
| typeof(SliderExtensions), | ||
| new PropertyMetadata(false, OnIsMouseWheelEnabledChanged)); | ||
|
|
||
| /// <summary> | ||
| /// Attached <see cref="DependencyProperty"/> for the value added to or subtracted | ||
| /// from <see cref="RangeBase.Value"/> per mouse-wheel notch. Defaults to | ||
| /// <see cref="double.NaN"/>, which means "use the slider's own | ||
| /// <see cref="RangeBase.SmallChange"/>". | ||
| /// </summary> | ||
| public static readonly DependencyProperty MouseWheelChangeProperty = DependencyProperty.RegisterAttached( | ||
| nameof(GetMouseWheelChange)[3..], | ||
| typeof(double), | ||
| typeof(SliderExtensions), | ||
| new PropertyMetadata(double.NaN)); |
There was a problem hiding this comment.
Yeah. This is kind of clever, but weird.
I think it makes the code less readable, so I'm against it
| /// an enclosing <see cref="ScrollViewer"/> does not also scroll. | ||
| /// </summary> | ||
| public static readonly DependencyProperty IsMouseWheelEnabledProperty = DependencyProperty.RegisterAttached( | ||
| nameof(GetIsMouseWheelEnabled)[3..], |
There was a problem hiding this comment.
| nameof(GetIsMouseWheelEnabled)[3..], | |
| "IsMouseWheelEnabled", |
| /// <see cref="RangeBase.SmallChange"/>". | ||
| /// </summary> | ||
| public static readonly DependencyProperty MouseWheelChangeProperty = DependencyProperty.RegisterAttached( | ||
| nameof(GetMouseWheelChange)[3..], |
There was a problem hiding this comment.
| nameof(GetMouseWheelChange)[3..], | |
| "MouseWheelChange", |
| step = slider.SmallChange; | ||
| } | ||
|
|
||
| double notches = delta / 120.0; |
There was a problem hiding this comment.
| double notches = delta / 120.0; | |
| double notches = delta / 120d; // Windows WHEEL_DELTA constant |
Let's get a comment for this magic constant
Summary
Adds a new
SliderExtensionsstatic class to the Extensions component, enabling mouse-wheel input onSlidercontrols via two attached properties:SliderExtensions.IsMouseWheelEnabledboolfalsetrue, wheel events on the slider adjustValue.SliderExtensions.MouseWheelChangedoubleNaN(use slider's ownSmallChange)Value.Why
By default
Sliderignores the mouse wheel. This is a small, common, opt-in convenience that's easy to add without subclassing — particularly useful when sliders are placed inside aScrollViewerand you want wheel-over-slider to adjust the value rather than scroll the surrounding container (the handler marks the wheel event handled).Behavior
notches = MouseWheelDelta / 120and appliesnotches * step, clamped to[Minimum, Maximum].MouseWheelChangedefaults toNaN, in which case the slider's ownSmallChangeis used as the per-notch delta — same source of truth used by arrow keys, no second knob unless the author wants one.e.Handled = trueso an enclosingScrollViewerdoesn't also scroll.PointerWheelChangedbased on theIsMouseWheelEnabledvalue, so toggling it off cleanly removes the handler.Origin
This was first written for the PowerToys PowerDisplay module, where wheel-scrollable brightness/contrast/volume sliders are very desirable. The implementation is intentionally dependency-free so it can live in the Toolkit and replace the local copy.
Files
components/Extensions/src/Slider/SliderExtensions.cs— the extension.components/Extensions/samples/SliderExtensions/SliderWheelSample.xaml(.cs)— interactive sample with toggleable bool/numeric options.components/Extensions/samples/SliderExtensions.md— docs page.Validation
CommunityToolkit.WinUI.Extensionsfornet8.0-windows10.0.19041.0andnet9.0-windows10.0.19041.0— clean.Extensions.Wasdkgallery head: wheel-over-slider adjusts the value, togglingIsMouseWheelEnabledoff restores default behavior,MouseWheelChangenumeric option changes the per-notch step.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com