30

スクロールビューアーを持つユーザーコントロールがあり、その中にテキストボックス、ラジオボタン、リストボックスなどの子コントロールがたくさんあります。マウスホイールを使用して、マウスがリストボックス内に着地するまで親スクロールビューアーをスクロールできます。その後、マウスホイールイベントがリストボックスに移動し始めます。

リストボックスからこれらのイベントを親コントロールに送り返す方法はありますか? この質問が示唆するように、親コントロール内からリストボックスを削除することは解決策ではありません。

私が試してみました

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

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

ありがとう

4

8 に答える 8

40

あなたが参照した答えはまさにあなたの問題の原因です.ScrollViewer内のListBox(ScrollViewerなどで構成されています)はMouseWheelイベントをキャッチして処理し、バブリングを防ぎます。したがって、ScrollViewerはイベントを認識しません発生したことがあります。

次の非常に単純な ControlTemplate を ListBox に使用してデモを行います (ScrollViewer が含まれていないため、MouseWheel イベントがキャッチされないことに注意してください)。

<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 に入ったときにマウスをキャプチャするオプションがあるため、マウスが離されるまですべてのマウス イベントを受け取り続けます。応答が必要です...次の 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 を無視するのではなく、イベントを「転送」してリストボックスの外に戻します。確認してください。」

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

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

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

  • リストビュー (a)

    • リストビュー (b)

      • リストビュー (c)

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

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

オリジナルが機能しない場合の変更されたサイモン・フォックスのソリューション:

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 に答える