disable mouse wheel on itemscontrol in wpf

六月ゝ 毕业季﹏ 提交于 2019-11-26 16:26:09

The answer you have referenced is exactly what is causing your problem, the ListBox (which is composed of among other things a ScrollViewer) inside your ScrollViewer catches the MouseWheel event and handles it, preventing it from bubbling and thus the ScrollViewer has no idea the event ever occurred.

Use the following extremely simple ControlTemplate for your ListBox to demonstrate (note it does not have a ScrollViewer in it and so the MouseWheel event will not be caught) The ScrollViewer will still scroll with the mouse over the ListBox.

<UserControl.Resources>
     <ControlTemplate x:Key="NoScroll">
         <ItemsPresenter></ItemsPresenter>
     </ControlTemplate>
</UserControl.Resources>

<ScrollViewer>
    <SomeContainerControl>
        <.... what ever other controls are inside your ScrollViewer>
        <ListBox Template="{StaticResource NoScroll}"></ListBox>
    <SomeContainerControl>
</ScrollViewer>

You do have the option of capturing the mouse when it enters the ScrollViewer though so it continues to receive all mouse events until the mouse is released, however this option would require you to delgate any further mouse events to the controls contained within the ScrollViewer if you want a response...the following MouseEnter MouseLeave event handlers will be sufficient.

private void ScrollViewerMouseEnter(object sender, MouseEventArgs e)
{
    ((ScrollViewer)sender).CaptureMouse();
}

private void ScrollViewerMouseLeave(object sender, MouseEventArgs e)
{
    ((ScrollViewer)sender).ReleaseMouseCapture();
}

Neither of the workarounds I have provided are really preferred however and I would suggest rethinking what you are actually trying to do. If you explain what you are trying to achieve in your question I'm sure you will get some more suggestions...

Amanduh

This can be accomplished via attached behaviors.

http://josheinstein.com/blog/index.php/2010/08/wpf-nested-scrollviewer-listbox-scrolling/

Edit: Here is the linked solution:

"So instead I came up with the following IgnoreMouseWheelBehavior. Technically it’s not ignoring the MouseWheel, but it is “forwarding” the event back up and out of the ListBox. Check it."

/// <summary>
/// Captures and eats MouseWheel events so that a nested ListBox does not
/// prevent an outer scrollable control from scrolling.
/// </summary>
public sealed class IgnoreMouseWheelBehavior : Behavior<UIElement>
{

  protected override void OnAttached( )
  {
     base.OnAttached( );
      AssociatedObject.PreviewMouseWheel += AssociatedObject_PreviewMouseWheel ;
  }

  protected override void OnDetaching( )
  {
      AssociatedObject.PreviewMouseWheel -= AssociatedObject_PreviewMouseWheel;
      base.OnDetaching( );
  }

  void AssociatedObject_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
  {

      e.Handled = true;

      var e2 = new MouseWheelEventArgs(e.MouseDevice,e.Timestamp,e.Delta);
      e2.RoutedEvent = UIElement.MouseWheelEvent;

      AssociatedObject.RaiseEvent(e2);

  }

}

And here’s how you would use it in XAML.

<ScrollViewer Name="IScroll">
    <ListBox Name="IDont">
        <i:Interaction.Behaviors>
            <local:IgnoreMouseWheelBehavior />
        </i:Interaction.Behaviors>
    </ListBox>
</ScrollViewer>

Where the i namespace is from Blend:

 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

I followed Amanduh's approach to solve the same problem I had with multiple datagrids in a scrollviewer but in WPF:

public sealed class IgnoreMouseWheelBehavior 
{
    public static bool GetIgnoreMouseWheel(DataGrid gridItem)
    {
        return (bool)gridItem.GetValue(IgnoreMouseWheelProperty);
    }

    public static void SetIgnoreMouseWheel(DataGrid gridItem, bool value)
    {
        gridItem.SetValue(IgnoreMouseWheelProperty, value);
    }

    public static readonly DependencyProperty IgnoreMouseWheelProperty =
        DependencyProperty.RegisterAttached("IgnoreMouseWheel", typeof(bool),
        typeof(IgnoreMouseWheelBehavior), new UIPropertyMetadata(false, OnIgnoreMouseWheelChanged));

    static void OnIgnoreMouseWheelChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        var item = depObj as DataGrid;
        if (item == null)
            return;

        if (e.NewValue is bool == false)
            return;

        if ((bool)e.NewValue)
            item.PreviewMouseWheel += OnPreviewMouseWheel;
        else
            item.PreviewMouseWheel -= OnPreviewMouseWheel;
    }

    static void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        e.Handled = true;

        var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
                     {RoutedEvent = UIElement.MouseWheelEvent};

        var gv = sender as DataGrid;
        if (gv != null) gv.RaiseEvent(e2);
    }
}

As Simon said, it's the ScrollViewer in the standard ListBox template that's catching the event. To bypass it you can provide your own template.

<ControlTemplate x:Key="NoWheelScrollListBoxTemplate" TargetType="ListBox">
    <Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="1,1,1,1" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" Name="Bd" SnapsToDevicePixels="True">
        <!-- This is the new control -->
        <l:NoWheelScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False">
            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
        </l:NoWheelScrollViewer>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="UIElement.IsEnabled" Value="False">
            <Setter TargetName="Bd" Property="Panel.Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
        </Trigger>
        <Trigger Property="ItemsControl.IsGrouping" Value="True">
            <Setter Property="ScrollViewer.CanContentScroll" Value="False" />
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

And the implementation for NoWheelScrollViewer is pretty simple.

public class NoWheelScrollViewer : ScrollViewer
{
    protected override void OnMouseWheel(MouseWheelEventArgs e)
    {
        // Do nothing
    }
}

Then, whenever you want a listbox to not handle the mouse wheel.

<ListBox Template="{StaticResource NoWheelScrollListBoxTemplate}">

A simple solution which worked for me is to override the inner control template to remove the scroll viewer (whichever required) like this

For example I have a structure like this

  • ListView (a)

    • ListView (b)

      • ListView (c)

I wanted to bubble the mouse wheel scroll of (b) to (a), however wanted to keep the mouse wheel scroll of (c) available. I simply overridden the Template of (b) like this. This allowed me to bubble contents of (b) except (c) to (a). Also, I can still scroll the contents of (c). If i want to remove even for (c) then i have to repeat the same step.

<ListView.Template>
  <ControlTemplate>
     <ItemsPresenter />
  </ControlTemplate>
</ListView.Template>

I was trying to adapt Simon Fox's answer for a DataGrid. I found the the template hid my headers, and I never got the mouseLeave event by doing it in C#. This is ultimately what worked for me:

    private void DataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        ((DataGrid)sender).CaptureMouse();
    }

    private void DataGrid_MouseWheel(object sender, MouseWheelEventArgs e)
    {
        ((DataGrid)sender).ReleaseMouseCapture();
    }

A modified Simon Fox's solution if the original doesn't work:

public sealed class IgnoreMouseWheelBehavior : Behavior<UIElement>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.PreviewMouseWheel += AssociatedObject_PreviewMouseWheel;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.PreviewMouseWheel -= AssociatedObject_PreviewMouseWheel;
        base.OnDetaching();
    }

    static void AssociatedObject_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        if (!(sender is DependencyObject))
        {
            return;
        }

        DependencyObject parent = VisualTreeHelper.GetParent((DependencyObject) sender);
        if (!(parent is UIElement))
        {
            return;
        }

        ((UIElement) parent).RaiseEvent(
            new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta) { RoutedEvent = UIElement.MouseWheelEvent });
        e.Handled = true;
    }
}

You must listening PreviewMouseWheel from ScrollViewer (it works), but not from listbox.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!