отключить колесико мыши по управлению элементами в wpf - PullRequest
26 голосов
/ 03 февраля 2010

У меня есть usercontrol, у которого есть scrollviewer, затем набор дочерних элементов управления, таких как текстовые поля, переключатели, списки и т.д. внутри него. Я могу использовать колесо мыши для прокрутки родительского средства просмотра прокрутки, пока моя мышь не окажется внутри списка, а затем события колеса мыши начнут поступать в список. Есть ли способ, чтобы список отправлял эти события обратно в родительский элемент управления? удаление списка изнутри стороны родительского элемента управления, как следует из этого вопроса ( Колесо мыши не работает, если над дочерними элементами управления ScrollViewer ) не является решением.

Я пытался

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

но это тоже не сработало.

Спасибо

Ответы [ 8 ]

34 голосов
/ 03 февраля 2010

Ответ, на который вы ссылались, это именно то, что вызывает вашу проблему, ListBox (который состоит, среди прочего, из ScrollViewer) внутри вашего 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();
}

Однако ни один из предложенных мной обходных путей не является предпочтительным, и я бы посоветовал переосмыслить то, что вы на самом деле пытаетесь сделать. Если вы объясните, что вы пытаетесь достичь в своем вопросе, я уверен, что вы получите еще несколько предложений ...

28 голосов
/ 10 августа 2011

Этого можно достичь с помощью прикрепленного поведения.

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

Edit: Вот связанное решение:

«Поэтому вместо этого я придумал следующий 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 из Blend:

 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
12 голосов
/ 08 февраля 2012

Я следовал подходу Амандуха для решения той же проблемы, что и у меня, с несколькими сетями данных в средстве просмотра прокрутки, но в 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);
    }
}
9 голосов
/ 03 февраля 2010

Как сказал Саймон, это ScrollViewer в стандартном шаблоне ListBox, который ловит событие.Чтобы обойти его, вы можете предоставить свой собственный шаблон.

<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}">
2 голосов
/ 08 апреля 2016

Простое решение, которое сработало для меня, - переопределить шаблон внутреннего элемента управления, чтобы удалить средство просмотра прокрутки (в зависимости от того, что требуется), как это

Например У меня есть такая структура

  • ListView (a)

    • ListView (b)

      • ListView (c)

Я хотел выделить прокрутку колесика мыши от (b) до (a), но хотел, чтобы прокрутка колесика мыши была доступна (c). Я просто переопределил шаблон (б), как это. Это позволило мне всплеснуть содержимое (b), кроме (c) - (a). Кроме того, я все еще могу прокручивать содержимое (с). Если я хочу удалить даже для (с), то я должен повторить тот же шаг.

<ListView.Template>
  <ControlTemplate>
     <ItemsPresenter />
  </ControlTemplate>
</ListView.Template>
1 голос
/ 20 августа 2014

Я пытался адаптировать ответ Саймона Фокса для DataGrid. Я обнаружил, что шаблон скрыл мои заголовки, и я так и не получил событие mouseLeave, выполнив его в C #. Это в конечном итоге то, что сработало для меня:

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

    private void DataGrid_MouseWheel(object sender, MouseWheelEventArgs e)
    {
        ((DataGrid)sender).ReleaseMouseCapture();
    }
0 голосов
/ 19 февраля 2015

Вы должны прослушивать PreviewMouseWheel из ScrollViewer (это работает), но не из списка.

0 голосов
/ 24 декабря 2014

Модифицированное решение Саймона Фокса, если оригинал не работает:

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