Search code examples
c#wpfoxyplot

Oxyplot's mouse events are not caught while model invalidated


Short: OxyPlot mouse event handlers (e.g. mySeries.MouseDown) are not being caught for a split second after I call myModel.InvalidatePlot(true/false) to update my model.

Detailed: I'm using the OxyPlot library in a sort of unconventional way. I am imitating the playing of a video in the background by rapidly updating an image annotation that takes up the entire graph. This allows me to use mouse events to plot on the image. My issue is that the video/image annotation playing and mouse events work separate, but when mouse events occur during the 'frame updates' some of them are missed. My belief is that when the plot is invalidated myModel.InvalidatePlot(true/false), that the Mouse Events are not picked up until the model is updated.


Solution

  • I found that the InvalidatePlot was causing an issue where the Mouse Button Events, such as OnMouseDown(MouseButtonEventArgs e), were never being captured by the PlotView. In order to work around this, I used another WPF object to catch the mouse events and force the call to my PlotView's mouse handling methods:

    1) I added a WPF Grid on another ZIndex level. And attached functions to the Mouse Events (MouseDown, MouseWheel, etc):

    <Grid x:Name="Grid_MouseCatcher"
            HorizontalAlignment="Stretch"
            VerticalAlignment="Stretch"
            Panel.ZIndex="2"
            MouseDown="Grid_MouseCatcher_MouseDown"
            MouseEnter="Grid_MouseCatcher_MouseEnter"
            MouseLeave="Grid_MouseCatcher_MouseLeave"
            MouseMove="Grid_MouseCatcher_MouseMove"
            MouseUp="Grid_MouseCatcher_MouseUp"
            MouseWheel="Grid_MouseCatcher_MouseWheel"
            Opacity="0.0"
            PreviewMouseMove="SetFocusToOxyPlot" />
    <oxy:PlotView x:Name="PlotView"
            HorizontalContentAlignment="Stretch"
            VerticalContentAlignment="Stretch"
            Panel.ZIndex="1"
            Model="{Binding PlotModel}" />
    

    2) Each event handled by the grid would call public methods that I created in the OxyPlot sourcecode:

     /// <summary>
     /// Sends mouse events from the overlaying grid to the PlotView. Forcing an Action.
     /// This was included due to an OxyPlot bug where the OnMouse... methods were not being called 
     /// when the plot was invalidated via InvalidatePlot(...)
     /// </summary>
     /// <param name="sender"></param>
     /// <param name="e"></param>
     private void Grid_MouseCatcher_MouseDown(object sender, MouseButtonEventArgs e)
     {
         Debug.WriteLine("MouseCatcher - Down: " + e.GetPosition(Grid_MouseCatcher).X + " " + e.GetPosition(Grid_MouseCatcher).Y);
         PlotView.ForceMouseDown(e);
     }
    

    3) In the source code file OxyPlot.Wpf\PlotBase.Events.cs I created functions (such as ForceMouseDown(MouseButtonEventArgs e)) which EXPLICITLY calls the protected mouse events (OnMouseDown(MouseButtonEventArgs e))

    /// <summary>
    /// Forces a call to Mouse handler. 
    /// This was needed because the events were getting lost when the model was updating
    /// </summary>
    /// <param name="e"></param>
    /// <returns></returns>
    public void ForceMouseDown(MouseButtonEventArgs e){
        this.OnMouseDown(e);
    }
    
    
    /// <summary>
    /// Invoked when an unhandled MouseDown attached event reaches an element in its route that is derived from this class. 
    /// Implement this method to add class handling for this event.
    /// </summary>
    /// <param name="e">The <see cref="T:System.Windows.Input.MouseButtonEventArgs" /> that contains the event data. 
    /// This event data reports details about the mouse button that was pressed and the handled state.</param>
    protected override void OnMouseDown(MouseButtonEventArgs e)
    {
        base.OnMouseDown(e);
        if (e.Handled)
        {
            return;
        }
    
        this.Focus();
        this.CaptureMouse();
    
        // store the mouse down point, check it when mouse button is released to determine if the context menu should be shown
        this.mouseDownPoint = e.GetPosition(this).ToScreenPoint();
    
        e.Handled = this.ActualController.HandleMouseDown(this, e.ToMouseDownEventArgs(this));
    }
    

    By fixing the issue this way I can still add Mouse Event Handlers to my Plotview the way that OxyPlot recommends, because their OnMouse#### events are still being called, they are just now called explicitly by the overlaying grid.