30

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

有没有办法让列表框将这些事件发送回父控件?像this question这样从父控件内部删除列表框(鼠标滚轮在ScrollViewer的子控件上方时不起作用)不是解决方案。

我试过了

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

但这也没有用。

谢谢

4

8 回答 8

40

您引用的答案正是导致您的问题的原因,您的 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();
}

但是,我提供的两种解决方法都不是真正的首选,我建议重新考虑您实际尝试做的事情。如果您在问题中解释您要达到的目标,我相信您会得到更多建议......

于 2010-02-03T02:10:57.607 回答
38

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

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

编辑:这是链接的解决方案:

“因此,我提出了以下 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/expression/2010/interactivity"
于 2011-08-09T21:40:06.210 回答
15

我按照 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);
    }
}
于 2012-02-08T06:59:08.537 回答
9

正如 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}">
于 2010-02-03T09:21:05.933 回答
2

我试图将 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();
    }
于 2014-08-20T18:13:03.100 回答
2

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

例如我有这样的结构

  • 列表视图(一)

    • 列表视图 (b)

      • 列表视图 (c)

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

<ListView.Template>
  <ControlTemplate>
     <ItemsPresenter />
  </ControlTemplate>
</ListView.Template>
于 2016-04-07T23:20:53.243 回答
1

如果原始版本不起作用,则修改后的 Simon Fox 解决方案:

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;
    }
}
于 2014-12-24T16:27:00.920 回答
1

您必须从 ScrollViewer 收听 PreviewMouseWheel(它有效),但不能从列表框中收听。

于 2015-02-19T14:28:29.387 回答