web-dev-qa-db-ja.com

wpfのitemscontrolでマウスホイールを無効にする

スクロールビューアを備えたユーザーコントロールがあり、その中にテキストボックス、ラジオボタン、リストボックスなどの子コントロールがたくさんあります。マウスホイールを使用して、マウスがリストボックス内に収まるまで親スクロールビューアをスクロールできます。その後、マウスホイールイベントがリストボックスに移動し始めます。リストボックスにそれらのイベントを親コントロールに送り返す方法はありますか?この質問のように親コントロールの内側からリストボックスを削除すると、( ScrollViewerの子コントロール上でマウスホイールが機能しない )は解決策ではありません。

私が試してみました

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

しかし、それもうまくいきませんでした。

ありがとう

28
odyth

あなたが参照した答えはまさにあなたの問題を引き起こしているものです、あなたのScrollViewer内のListBox(とりわけScrollViewerで構成されています)はMouseWheelイベントをキャッチしてそれを処理し、それがバブリングするのを防ぎます、そしてそれでScrollViewerはイベントを知りませんこれまでに発生しました。

リストボックスに次の非常に単純なControlTemplateを使用してデモンストレーションします(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に含まれるコントロールにさらにマウスイベントを委任する必要があります。応答が必要です...次のMouseEnterMouseLeaveイベントハンドラーで十分です。

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

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

ただし、私が提供した回避策はどちらも実際には好ましくありません。実際に何をしようとしているのかを再考することをお勧めします。あなたがあなたの質問であなたが達成しようとしていることを説明するならば、私はあなたがいくつかのより多くの提案を得ると確信しています...

35
Simon Fox

これは、アタッチされた動作を介して実行できます。

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

編集:リンクされたソリューションは次のとおりです。

「代わりに、次のIgnoreMouseWheelBehaviorを思いつきました。技術的には、MouseWheelを無視していませんが、イベントをリストボックスからバックアップして「転送」しています。確認してください。」

/// <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"
28
Amanduh

私は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);
    }
}
12
quarkonium

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}">
9

私のために働いた簡単な解決策は、このようにスクロールビューア(必要な方)を削除するために内部コントロールテンプレートをオーバーライドすることです

たとえば、私はこのような構造を持っています

  • ListView(a)

    • ListView(b)

      • ListView(c)

(b)のマウスホイールスクロールを(a)にバブルしたかったのですが、(c)のマウスホイールスクロールを使用できるようにしておきたいと思いました。このように(b)のテンプレートを上書きしただけです。これにより、(c)から(a)を除く(b)の内容をバブルすることができました。また、(c)の内容をスクロールすることもできます。 (c)でも削除したい場合は、同じ手順を繰り返す必要があります。

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

私はSimonFoxの答えを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();
    }
1
J-DawG

ScrollViewerからPreviewMouseWheelを聞く必要があります(動作します)が、リストボックスからは聞きません。

0
Smagin Alexey

オリジナルが機能しない場合の修正されたSimonFoxのソリューション:

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