diff --git a/components/Extensions/samples/SliderExtensions.md b/components/Extensions/samples/SliderExtensions.md new file mode 100644 index 00000000..ff8b396e --- /dev/null +++ b/components/Extensions/samples/SliderExtensions.md @@ -0,0 +1,44 @@ +--- +title: SliderExtensions +author: niels9001 +description: SliderExtensions provides attached properties that let a Slider be adjusted by mouse-wheel input. +keywords: windows 10, windows 11, windows community toolkit, winui, Slider, extensions, mouse wheel +dev_langs: + - csharp +category: Extensions +subcategory: Controls +discussion-id: 0 +issue-id: 0 +icon: Assets/Extensions.png +--- + +The `SliderExtensions` static class provides attached properties for the [`Slider`](https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.slider) control. + +> **Platform APIs:** `SliderExtensions` + +## IsMouseWheelEnabled + +Setting `SliderExtensions.IsMouseWheelEnabled` to `true` opts a slider in to mouse-wheel input: scrolling the wheel while the pointer is over the slider increments or decrements `Value`. The wheel event is marked handled, so an enclosing [`ScrollViewer`](https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.scrollviewer) is not also scrolled. + +```xaml + + + +``` + +## MouseWheelChange + +`SliderExtensions.MouseWheelChange` is the amount added to or subtracted from `Value` per wheel notch. The naming mirrors the built-in [`Slider.SmallChange`](https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.primitives.rangebase.smallchange) / [`LargeChange`](https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.primitives.rangebase.largechange) properties. When unset (the default of `double.NaN`), the slider's own `SmallChange` is used — so a slider already configured for arrow-key input "just works" for the wheel too. + +```xaml + + + +``` + +> [!SAMPLE SliderWheelSample] diff --git a/components/Extensions/samples/SliderExtensions/SliderWheelSample.xaml b/components/Extensions/samples/SliderExtensions/SliderWheelSample.xaml new file mode 100644 index 00000000..d1727cd9 --- /dev/null +++ b/components/Extensions/samples/SliderExtensions/SliderWheelSample.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/components/Extensions/samples/SliderExtensions/SliderWheelSample.xaml.cs b/components/Extensions/samples/SliderExtensions/SliderWheelSample.xaml.cs new file mode 100644 index 00000000..6d4d0f60 --- /dev/null +++ b/components/Extensions/samples/SliderExtensions/SliderWheelSample.xaml.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace ExtensionsExperiment.Samples.SliderExtensions; + +/// +/// A sample showing how the SliderExtensions attached properties make a +/// respond to mouse-wheel input. +/// +[ToolkitSampleBoolOption("IsMouseWheelEnabled", true, Title = "Enable mouse-wheel scrolling")] +[ToolkitSampleNumericOption("MouseWheelChange", 5, 1, 25, Title = "Value change per wheel notch")] +[ToolkitSample(id: nameof(SliderWheelSample), "Slider Mouse Wheel", description: "An extension that lets a Slider be adjusted by mouse-wheel scrolling.")] +public sealed partial class SliderWheelSample : Page +{ + public SliderWheelSample() + { + this.InitializeComponent(); + } +} diff --git a/components/Extensions/src/Slider/SliderExtensions.cs b/components/Extensions/src/Slider/SliderExtensions.cs new file mode 100644 index 00000000..9e785dda --- /dev/null +++ b/components/Extensions/src/Slider/SliderExtensions.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace CommunityToolkit.WinUI; + +/// +/// Provides attached dependency properties for the control. +/// +public static class SliderExtensions +{ + /// + /// Attached that, when true, lets the + /// target respond to mouse-wheel input by adjusting its + /// . Each notch changes the value by + /// ; the wheel event is marked handled so + /// an enclosing does not also scroll. + /// + public static readonly DependencyProperty IsMouseWheelEnabledProperty = DependencyProperty.RegisterAttached( + nameof(GetIsMouseWheelEnabled)[3..], + typeof(bool), + typeof(SliderExtensions), + new PropertyMetadata(false, OnIsMouseWheelEnabledChanged)); + + /// + /// Attached for the value added to or subtracted + /// from per mouse-wheel notch. Defaults to + /// , which means "use the slider's own + /// ". + /// + public static readonly DependencyProperty MouseWheelChangeProperty = DependencyProperty.RegisterAttached( + nameof(GetMouseWheelChange)[3..], + typeof(double), + typeof(SliderExtensions), + new PropertyMetadata(double.NaN)); + + /// + /// Gets the value of . + /// + /// The to read the property value from. + /// true if mouse-wheel scrolling is enabled on the slider; otherwise false. + public static bool GetIsMouseWheelEnabled(Slider obj) => (bool)obj.GetValue(IsMouseWheelEnabledProperty); + + /// + /// Sets the value of . + /// + /// The to set the property on. + /// true to enable mouse-wheel scrolling on the slider; otherwise false. + public static void SetIsMouseWheelEnabled(Slider obj, bool value) => obj.SetValue(IsMouseWheelEnabledProperty, value); + + /// + /// Gets the value of . + /// + /// The to read the property value from. + /// The per-notch delta, or to inherit from . + public static double GetMouseWheelChange(Slider obj) => (double)obj.GetValue(MouseWheelChangeProperty); + + /// + /// Sets the value of . + /// + /// The to set the property on. + /// The per-notch delta to apply to . + public static void SetMouseWheelChange(Slider obj, double value) => obj.SetValue(MouseWheelChangeProperty, value); + + private static void OnIsMouseWheelEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not Slider slider) + { + return; + } + + slider.PointerWheelChanged -= OnPointerWheelChanged; + + if (e.NewValue is true) + { + slider.PointerWheelChanged += OnPointerWheelChanged; + } + } + + private static void OnPointerWheelChanged(object sender, PointerRoutedEventArgs e) + { + if (sender is not Slider slider) + { + return; + } + + int delta = e.GetCurrentPoint(slider).Properties.MouseWheelDelta; + if (delta == 0) + { + return; + } + + double step = GetMouseWheelChange(slider); + if (double.IsNaN(step) || step <= 0) + { + step = slider.SmallChange; + } + + double notches = delta / 120.0; + double newValue = Math.Clamp(slider.Value + (notches * step), slider.Minimum, slider.Maximum); + + if (newValue != slider.Value) + { + slider.Value = newValue; + } + + // Claim the gesture so an enclosing ScrollViewer does not also scroll. + e.Handled = true; + } +}