在 wpf 中的 itemscontrol 上禁用鼠标滚轮

发布于 2024-10-08 22:02:57 字数 478 浏览 8 评论 0原文

我有一个用户控件,它有一个滚动查看器,然后是一堆子控件,如文本框、单选按钮和列表框等。我可以使用鼠标滚轮滚动父滚动查看器,直到鼠标落在列表框中,然后鼠标滚轮事件开始进入列表框。

有什么方法可以让列表框将这些事件发送回父控件吗?像这个问题一样从父控件内部删除列表框(鼠标滚轮在 ScrollViewer 的子控件上方时不起作用)不是解决方案。

我已经尝试过,

private void ListBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
    e.Handled = true;
}

但这也不起作用。

谢谢

I have a usercontrol that has a scrollviewer, then a bunch of child controls like text boxes, radio buttons, and listboxes, etc inside of it. I can use the mouse wheel to scroll the parent scrollviewer until my mouse lands inside a listbox then, the mouse wheel events start going to the listbox.

Is there any way to have the listbox send those events back up to the parent control? Removing the listbox from within side the parent control like this question suggests (Mouse wheel not working when over ScrollViewer's child controls) isnt a solution.

I have tried

private void ListBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
    e.Handled = true;
}

but that didnt work either.

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

浅忆流年 2024-10-15 22:02:57

这可以通过附加行为来完成。

所以我想出了以下 IgnoreMouseWheelBehavior。从技术上讲,它并不是忽略 MouseWheel,而是将事件“转发”回 ListBox 之外。检查一下。

/// <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);

  }

}

以下是在 XAML 中使用它的方法。

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

其中 i 命名空间是:

 xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

请注意,您将需要 Microsoft.Xaml.Behaviors.Wpf nuget 包来使用它。

This can be accomplished via attached behaviors.

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:

 xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

Note that you will need the Microsoft.Xaml.Behaviors.Wpf nuget package to use this.

旧人 2024-10-15 22:02:57

您引用的答案正是导致问题的原因,ScrollViewer 内的 ListBox(由 ScrollViewer 等组成)捕获 MouseWheel 事件并处理它,防止其冒泡,因此 ScrollViewer 不知道该事件曾经发生过。

使用以下极其简单的 ControlTemplate 为您的 ListBox 进行演示(请注意,其中没有 ScrollViewer,因此不会捕获 MouseWheel 事件)ScrollViewer 仍将随着鼠标在 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>

您确实可以选择在鼠标进入 ScrollViewer 时捕获鼠标,因此它会继续接收所有鼠标事件,直到释放鼠标为止,但是此选项将要求您将任何进一步的鼠标事件委托给 ScrollViewer 中包含的控件,如果您想要响应...以下 MouseEnter MouseLeave 事件处理程序就足够了。

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

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

然而,我提供的两种解决方法都不是真正优选的,我建议重新考虑您实际上想要做什么。如果您解释您在问题中想要实现的目标,我相信您会得到更多建议......

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...

吃素的狼 2024-10-15 22:02:57

我按照 Amanduh 的方法解决了在滚动查看器中但在 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);
    }
}

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);
    }
}
物价感观 2024-10-15 22:02:57

正如 Simon 所说,标准 ListBox 模板中的 ScrollViewer 正在捕获事件。要绕过它,您可以提供自己的模板。

<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>

NoWheelScrollViewer 的实现非常简单。

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

然后,每当您希望列表框不处理鼠标滚轮时。

<ListBox Template="{StaticResource NoWheelScrollListBoxTemplate}">

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}">
浴红衣 2024-10-15 22:02:57

对我有用的一个简单解决方案是覆盖内部控制模板以删除滚动查看器(无论需要哪个),如下

所示
我有一个像这样的结构

  • ListView (a)

    • ListView(二)

      • 列表视图(c)

我想将 (b) 的鼠标滚轮滚动到 (a),但希望保持 (c) 的鼠标滚轮滚动可用。
我只是像这样重写了 (b) 的模板。这使我能够将除(c)之外的(b)的内容冒泡到(a)。另外,我仍然可以滚动(c)的内容。如果我想删除(c),那么我必须重复相同的步骤。

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

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>
病毒体 2024-10-15 22:02:57

我试图将 Simon Fox 的答案改编为 DataGrid。我发现模板隐藏了我的标题,并且我从未通过在 C# 中执行此操作获得 mouseLeave 事件。这最终对我有用:

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

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

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();
    }
番薯 2024-10-15 22:02:57

如果原始解决方案不起作用,则修改西蒙福克斯的解决方案:

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;
    }
}

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;
    }
}
夜血缘 2024-10-15 22:02:57

您必须从 ScrollViewer 监听 PreviewMouseWheel(它可以工作),但不能从列表框监听。

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文