Search code examples
c#.netwpfscrollvieweruielement

Prevent ScrollViewer from handling Mouse Wheel Event


Is there a way to prevent a ScrollViewer from handling mouse scrolling?

The following code isn't sufficient, because the event shall be handled by a child element of the ScrollViewer instead:

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

EDIT - @Anders Keller Carstensen

I used to instantiate a PlotModel object directly, that's why I didn't see the AddHandler() method. I now changed my code, but the plot doesn't show anything:

public OxyPlot.Wpf.PlotView Plot { get; set; }

public MainWindow()
{
    InitializeComponent();
    DataContext = this;

    Plot = new OxyPlot.Wpf.PlotView();
    Plot.Model = new PlotModel();

    Plot.AddHandler(System.Windows.UIElement.PreviewMouseWheelEvent, new MouseWheelEventHandler(Plot_PreviewMouseWheel), true);

    // Create Line series
    var s1 = new LineSeries();
    s1.Points.Add(new DataPoint(2, 7));
    s1.Points.Add(new DataPoint(7, 9));
    s1.Points.Add(new DataPoint(9, 4));

    // add Series and Axis to plot model
    Plot.Model.Series.Add(s1);
    Plot.Model.InvalidatePlot(false);
}

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

private void Plot_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
    MessageBox.Show("Button_PreviewMouseWheel");
}

This is the corresponding XAML:

<ScrollViewer Margin="0,25,0,0" PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
    <oxy:PlotView Model="{Binding Plot}" Height="2000" Margin="0,0,15,0" />
</ScrollViewer>

I'm certain that Model="{Binding Plot}" is wrong, but I don't what to write instead.


Solution

  • EDIT - here is a better solution.

    You can set e.Handled = true in the ScrollViewer's PreviewMouseWheel event handler, and then still handle the event in your other control.

    For example, let's say I have a button inside a grid in the ScrollViewer's Content. In the window's constructor, I subscribe to the button's PreviewMouseWheel event like this:

    public ScrollViewerWindow() {
      InitializeComponent();
    
      btn.AddHandler(UIElement.PreviewMouseWheelEvent, 
                     new MouseWheelEventHandler(Button_PreviewMouseWheel), 
                     true // Handler will be called even though e.Handled = true
                    );
    }
    

    Then your event handles will look like this:

    private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e) {
      e.Handled = true;
    }
    
    private void Button_PreviewMouseWheel(object sender, MouseWheelEventArgs e) {
      MessageBox.Show("Button_PreviewMouseWheel");
    }
    

    This is an answer to your new question.

    Give the PlotView a name in the XAML, and bind the Model to a PlotModel property.

    <ScrollViewer Margin="0,25,0,0" PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
        <oxy:PlotView x:Name="pv" Model="{Binding PlotModel}" Height="2000" Margin="0,0,15,0" />
    </ScrollViewer>
    

    In your code-behind you should add the PlotModel property and initialize it:

    public PlotModel PlotModel { get; set; }
    
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    
        pv.AddHandler(System.Windows.UIElement.PreviewMouseWheelEvent, new MouseWheelEventHandler(Plot_PreviewMouseWheel), true);
    
        // Create Line series
        var s1 = new LineSeries();
        s1.Points.Add(new DataPoint(2, 7));
        s1.Points.Add(new DataPoint(7, 9));
        s1.Points.Add(new DataPoint(9, 4));
    
        // add Series and Axis to plot model
        PlotModel = new PlotModel();
        PlotModel.Series.Add(s1);
        PlotModel.InvalidatePlot(false);
    }
    
    private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        e.Handled = true;
    }
    
    private void Plot_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        MessageBox.Show("Plot_PreviewMouseWheel");
    }
    

    Old solution:

    Creating a sub-class of ScrollViewer that responds negative to all HitTest requests seems to solve your problem - however, it may have side-effects that you don't like. Check it out and see if it suits your needs.

    First, create the sub-class:

    public class MyScrollViewer : ScrollViewer {
      protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) {
        return null;
      }
    }
    

    Then, in the XAML, change <ScrollViewer> to <local:MyScrollViewer>.

    Make sure the local namespace is declared. It will look something like this:

    <Window ....
            xmlns:local="clr-namespace:WPFTest">