Search code examples
c#wpfcombobox

C# - WPF - Prevent Scrolling this Element, but allow Scrolling Parent


Background

I need to prevent a ComboBox from scrolling (using the scroll wheel) when it is focused.

However, I need its parent UIElement to still scroll.

I got the following code from this question, which prevents any scrolling, including that of the parent UIElement.

Code

<ComboBox comboBox:NoScrollingBehaviour.Enabled="True"
          IsEditable="True" 
          IsTextSearchEnabled="True" 
          IsTextSearchCaseSensitive="False" 
          StaysOpenOnEdit="True"
          Text="{Binding MyTextProperty}"
          ItemsSource="{Binding DataContext.MySourceProperty, ElementName=MyElementName}"/>
using System.Windows;
using System.Windows.Input;

namespace AE.UI.ComboBox
{
    public static class NoScrollingBehaviour
    {
        public static readonly DependencyProperty EnabledProperty =
            DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(NoScrollingBehaviour),
                                                new UIPropertyMetadata(false, OnEnabledChanged));

        public static bool GetEnabled(System.Windows.Controls.ComboBox comboBox) => (bool)comboBox.GetValue(EnabledProperty);

        public static void SetEnabled(System.Windows.Controls.ComboBox comboBox, bool value) => comboBox.SetValue(EnabledProperty, value);

        private static void OnEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var comboBox = (System.Windows.Controls.ComboBox)d;
            if (GetEnabled(comboBox))
                comboBox.PreviewMouseWheel += OnPreviewMouseWheel;
        }

        private static void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            var comboBox = (System.Windows.Controls.ComboBox)sender;
            if (!GetEnabled(comboBox))
                return;
            
            if (comboBox.IsDropDownOpen)
                return;

            e.Handled = true;
        }
    }
}

Question

How do I allow the parent UIElement to scroll, while still preventing the ComboBox from scrolling?


Solution

  • I was able to propagate this up using the following code

    private static void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
            {
                var comboBox = (System.Windows.Controls.ComboBox)sender;
                if (!GetEnabled(comboBox))
                    return;
    
                if (comboBox.IsDropDownOpen)
                    return;
    
                e.Handled = true;
                
                var parentScrollViewer = comboBox.GetVisualParent<ScrollViewer>();
                parentScrollViewer?.ScrollToVerticalOffset(parentScrollViewer.VerticalOffset - e.Delta * 0.4);
            }
    
     public static T GetVisualParent<T>(this DependencyObject child) where T : Visual
            {
                while ((child != null) && !(child is T))
                {
                    child = VisualTreeHelper.GetParent(child);
                }
                return child as T;
            }
    

    Note: You may need to scale the e.Delta (I required a scaling of 0.4, but I am uncertain if this is the same everywhere)