SciChart WPF 2D Charts > ChartModifier API > Custom Modifiers > Custom Modifiers – ChartModifierBase API
Custom Modifiers – ChartModifierBase API

The ChartModifierBase API is by far the most powerful API in the SciChart library.

Using this API you can create behaviours which you can attach to a chart to perform custom Zooming, Panning, Annotation & Markers, Legend output and much much more. Any time you want to do something in C# code to alter the behaviour of a SciChartSurface you should be thinking about creating a custom modifier to do it.

The ChartModifierBase type

The ChartModifierBase provides an abstract base class for all of the ChartModifiers within SciChart. All of our built-in modifiers such as ZoomPanModifierRolloverModifierTooltipModifierYAxisDragModifier inherit ChartModifierBase.

You can create your own ChartModifiers by inheriting this class. For example, you can declare a modifier like this:

The ChartModifierBase type
Copy Code
public class DoubleClickToZoomExtentsModifier : ChartModifierBase
{
    public override void OnModifierDoubleClick(ModifierMouseArgs e)
    {
        base.OnModifierDoubleClick(e);

        base.ParentSurface.AnimateZoomExtents(TimeSpan.FromMilliseconds(500));
    }
}

And apply it like this:

The ChartModifierBase type
Copy Code
<s:SciChartSurface>
    <s:SciChartSurface.ChartModifier>
       <local:DoubleClickToZoomExtentsModifier/>
    </s:SciChartSurface.ChartModifier>
</s:SciChartSurface>

... and you've just created your first custom Zoom Extents behaviour using the ChartModifier API!

The Most Important Methods / Properties in the ChartModifierBase class

ChartModifierBase Properties

The API Documentation will tell you everything that ChartModifierBase can do, but for your convenience we've listed the most important methods and properties below:

Property Description
IsEnabled Gets or sets a value indicating whether this ChartModifier is enabled
IsMouseLeftButtonDown Gets if the Left mouse button is currently down
IsMouseMiddleButtonDown Gets if the Middle mouse button is currently down
IsMouseRightButtonDown Gets of the right mouse button is currently down
ModifierSurface Gets the IChartModifierSurface instance on the parent SciChartSurface, which acts as a canvas to place UIElements
ParentSurface Overridden. Gets or sets the parent SciChartSurface, to perform operations on
ReceiveHandledEvents Gets or sets a value, indicates whether current modifier handles events which have already been marked as handled
XAxisXAxes Returns the XAxes on the parent Abt.Controls.SciChart.Visuals.SciChartSurface
YAxisYAxes Returns the YAxes on the parent Abt.Controls.SciChart.Visuals.SciChartSurface

ChartModifierBase Methods

Methods Description
GetPointRelativeTo

Transforms the input point relative to the IHitTestable element. Can be used to transform points relative to the

SciChartSurface.ModifierSurface, or SciChartSurface.XAxis for instance.

IsPointWithinBounds

Gets whether the mouse point is within the bounds of the hit-testable element. Assumes the mouse-point has not

been translated yet (performs translation)

OnAttached Called when the element is attached to the Chart Surface
OnDetached Called immediately before the element is detached from the Chart Surface
OnMasterMouseLeave Called when the mouse leaves the Master of current MouseEventGroup
OnModifierDoubleClick Called when a Mouse DoubleClick occurs on the parent SciChartSurface,
OnModifierMouseDown Called when a Mouse Button is pressed on the parent SciChartSurface,
OnModifierMouseMove Called when the Mouse is moved on the parent SciChartSurface,
OnModifierMouseUp Called when a Mouse Button is released on the parent SciChartSurface,
OnModifierMouseWheel Called when the Mouse Wheel is scrolled on the parent SciChartSurface,
OnModifierTouchDown Called when a Multi-Touch Down interaction occurs on the parent SciChartSurface,
OnModifierTouchMove Called when a Multi-Touch Move interaction occurs on the parent SciChartSurface,
OnModifierTouchUp Called when a Multi-Touch Up interaction occurs on the parent SciChartSurface,
OnParentSurfaceRendered Called when the parentSciChartSurface,is rendered
OnParentSurfaceResized Called when the parent SciChartSurface, is resized
OnParentSurfaceMouseEnter Called when the mouse enters the parent SciChartSurface,
OnParentSurfaceMouseLeave Called when the mouse leaves the parent SciChartSurface,
OnSelectedSeriesChanged Called when the parent surface SelectedSeries collection changes
OnXAxesCollectionChanged

Overridden. Called with the SciChartSurface.XAxes AxisCollection changes. Overridden in derived classes to get notification

of this event

OnYAxesCollectionChanged

Overridden. Called with the SciChartSurface.YAxes AxisCollection changes. Overridden in derived classes to get notification

 of this event

 

So you can see there are a lot of powerful API functions on the ChartModifierBase class!

Using this one base-class you can:

  • Be notified about Mouse Clicks, Mouse Down, Mouse Move, or when the mouse enters or leaves the chart
  • Be notified when the SciChartSurface is rendered or resized, or when the XAxis or YAxis collection changed
  • Access the ParentSurface and therefore access the XAxis, YAxis, RenderableSeries, DataSeries and Annotations on the chart
  • Access the ModifierSurface - a dedicated Canvas over the chart for drawing UIElements such as Tooltips, Lines, Zoom Rectangles - anything really!

All of our Chart Modifiers are built with this API, the possibilities are really endless!

Overriding Mouse Events

To get a notification of mouse-event on the parent SciChartSurface, simply override OnModifierMouseDown, OnModifierMouseMove, OnModifierMouseUp:

Overriding Mouse Events
Copy Code
public class TestChartModifier : ChartModifierBase
{
   /// <summary>
   /// Called when a Mouse Button is pressed on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the mouse button operation</param>
   public override void OnModifierMouseDown(ModifierMouseArgs e)
   {
      base.OnModifierMouseDown(e);
   }

   /// <summary>
   /// Called when the Mouse is moved on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the mouse move operation</param>
   public override void OnModifierMouseMove(ModifierMouseArgs e)
   {
      base.OnModifierMouseMove(e);
   }

   /// <summary>
   /// Called when a Mouse Button is released on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the mouse button operation</param>
   public override void OnModifierMouseUp(ModifierMouseArgs e)
   {
      base.OnModifierMouseUp(e);
   }

   /// <summary>
   /// Called when the Mouse Wheel is scrolled on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the mouse wheel operation</param>
   public override void OnModifierMouseWheel(ModifierMouseArgs e)
   {
      base.OnModifierMouseWheel(e);
   }

   /// <summary>
   /// Called when a Mouse DoubleClick occurs on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the mouse button operation</param>
   public override void OnModifierDoubleClick(ModifierMouseArgs e)
   {
      base.OnModifierDoubleClick(e);
   }
}

When your chart modifier is attached to a SciChartSurface, mouse events on the chart will propagate to the TestChartModifier instance:

Overriding Mouse Events
Copy Code
<s:SciChartSurface>
   <s:SciChartSurface.ChartModifier>
      <s:TestChartModifier/>
   </s:SciChartSurface.ChartModifier>
</s:SciChartSurface>

 

ModifierMouseArgs Properties

The ModifierMouseArgs passed in to mouse-callbacks can tell you:

  • The X-Y coordinate of the mouse when the event took place
  • The Mouse-buttons currently pressed.
  • Whether the mouse-event occurred on this SciChartSurface (isMaster=true) or not (isMaster=false)
  • The modifier key (e.g. CTRL, SHIFT) currently pressed

ModifierMouseArgs Properties
Copy Code
public class TestChartModifier : ChartModifierBase
{
   /// <summary>
   /// Called when a Mouse Button is pressed on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the mouse button operation</param>
   public override void OnModifierMouseDown(ModifierMouseArgs e)
   {
      base.OnModifierMouseDown(e);

      Point mousePoint = e.MousePoint;
      MouseButtons mouseButtons = e.MouseButtons;
      bool isMaster = e.IsMaster;
      MouseModifier modifierKey = e.Modifier;
   }
}

Transforming Coordinates to the Viewport

Mouse coordinates are measured according to the top-left of the SciChartSurface. To effectively use these mouse-coordinates, they may need to be translated to the central viewport.

Transforming Coordinates to the Viewport
Copy Code
public class TestChartModifier : ChartModifierBase
{
   /// <summary>
   /// Called when a Mouse Button is pressed on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the mouse button operation</param>
   public override void OnModifierMouseDown(ModifierMouseArgs e)
   {
       base.OnModifierMouseDown(e);

       Point xy = e.MousePoint;
        
       var scs = base.ParentSurface;
        
       // gets the bounds of the ModifierSurface with respect to the RootGrid
       // which hosts the SciChartSurface
        var modifierRect = ModifierSurface.GetBoundsRelativeTo(RootGrid);

        // Translates the mouse point (from root grid coords) to ModifierSurface coords
        var x1y1 = base.GetPointRelativeTo(xy, base.ModifierSurface);

        // you can now use this coordinate to convert to data values
        double dataValue = scs.XAxis.GetCurrentCoordinateCalculator()
                             .GetDataValue(x1y1.X);

   }
}

Given a SciChartSurface bounds and a mouse coordinate (x,y), you may need to transform to viewport coordinates (x’,y’)

Determining if Mouse Event occurred over an Axis or main chart surface

To determine if a mouse-point is over an element, we can use code similar to the following. This is useful if you want to start a modifier action only if the mouse-down occurred on an axis, or on the main chart.

Determining if Mouse Event occurred over an Axis or main chart surface
Copy Code
public class HitTestingModifier : ChartModifierBase
{
    public override void OnModifierMouseDown(ModifierMouseArgs e)
    {
        bool isOnChart = IsPointWithinBounds(e.MousePoint, ModifierSurface);
        bool isOnYAxis = IsPointWithinBounds(e.MousePoint, YAxis);
        bool isOnXAxis = IsPointWithinBounds(e.MousePoint, XAxis);

        MessageBox.Show(string.Format("Clicked YAxis? {0}\nClicked XAxis? {1}\n" +
            “Clicked Chart? {2}",
             isOnYAxis, isOnXAxis, isOnChart));

        base.OnModifierMouseDown(e);
    }

    public bool IsPointWithinBounds(Point point, IHitTestable element)
    {
        var x1y1 = ParentSurface.RootGrid.TranslatePoint(point, element);

        var fe = (element as FrameworkElement);
        bool inBounds = (x1y1.X <= fe.ActualWidth && x1y1.X >= 0)
                            && (x1y1.Y <= fe.ActualHeight && x1y1.Y >= 0);

        return inBounds;
    }
}

Determining if the Mouse is over a RenderableSeries

To determine if a mouse-event occurred over a RenderableSeries, you can combine the ChartModifier API with the Hit-Test API, described in RenderableSeries Hit-Test API

Determining if the Mouse is over a RenderableSeries
Copy Code
public class TestChartModifier : ChartModifierBase
{
   /// <summary>
   /// Called when a Mouse Button is pressed on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the mouse button operation</param>
   public override void OnModifierMouseDown(ModifierMouseArgs e)
   {
      base.OnModifierMouseDown(e);

      double hitTestRadius = 5d;
      bool interpolate = true;

      foreach (var rSeries in base.ParentSurface.RenderableSeries)
      {
         var hitResult = rSeries.HitTest(e.MousePoint, hitTestRadius, interpolate);

         if (hitResult.IsHit)
         {
            // Mouse is over series!
         }
      }
   }      
}

Determining Drag on the chart

Determining Drag on the chart is as simple as registering MouseDown, followed by MouseMove and MouseUp on the chart. To do this, we suggest using some Boolean flags to track the state of the current mouse. All the modifier callbacks are called on the UI thread so you can be assured of the thread safety of the API.

Overriding Touch Events

SciChart’s ChartModifierBase API also has a number of callbacks for processing touch events. These us the TouchPoint class in WPF4’s multi-touch API.

Overriding Touch Events
Copy Code
public class TestChartModifier : ChartModifierBase
{
   /// <summary>
   /// Called when a Multi-Touch Down occurs on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the manipulation operation</param>
   public override void OnModifierTouchDown(ModifierTouchManipulationArgs e)
   {
      base.OnModifierTouchDown(e);

      IEnumerable<TouchPoint> touchPoints = e.Manipulators;           
   }

   /// <summary>
   /// Called when a Multi-Touch Move occurs on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the manipulation operation</param>
   public override void OnModifierTouchMove(ModifierTouchManipulationArgs e)
   {
      base.OnModifierTouchMove(e);
   }

   /// <summary>
   /// Called when a Multi-Touch Up occurs on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the manipulation operation</param>
   public override void OnModifierTouchUp(ModifierTouchManipulationArgs e)
   {
      base.OnModifierTouchUp(e);
   }
}

Similar notes apply to e.IsMaster and also translating TouchPoint.Position to the main central viewport as in  Transforming Coordinates to the Viewport

Overriding Key Events

SciChart also allows handling of Key events in the ChartModifier API. To get a notification of a key event, simply override OnModifierKeyDown, OnModifierKeyUp

Overriding Key Events
Copy Code
public class TestChartModifier : ChartModifierBase
{
   /// <summary>
   /// Called when the KeyDown event is fired for the Master of the current <see cref="MouseEventGroup" />
   /// </summary>
   /// <param name="e">Arguments detailing the key event</param>
   public override void OnModifierKeyDown(ModifierKeyArgs e)
   {
      base.OnModifierKeyDown(e);

      Key key = e.Key;
      MouseModifier modifierKey = e.Modifier;
      bool isMaster = e.IsMaster;
   }

   /// <summary>
   /// Called when the KeyUp event is fired for the Master of the current <see cref="MouseEventGroup" />
   /// </summary>
   /// <param name="e">Arguments detailing the key event</param>
   public override void OnModifierKeyUp(ModifierKeyArgs e)
   {
      base.OnModifierKeyUp(e);
   }
}

Drawing Shapes over the Chart with the ModifierSurface

The ChartModifierBase API was primarily designed to facilitate behavior-like classes to give zoom and pan behaviours to the chart. This quickly evolved into tooltips and now any behavior which requires access to the underlying SciChartSurface visual or logical elements could be placed in a ChartModifier.

To draw shapes over the chart at the time of using a ChartModifier, you can use the SciChartSurface.ModifierSurface property.

The ModifierSurface is simply a canvas which is overlaid on top of the SciChartSurface. This is a great place to put tooltips or temporary UI such as zoom-box rectangles as you interact with the chart. The following code demonstrates how to add a Shape to the ModifierSurface on MouseDown for instance:

Drawing Shapes over the Chart with the ModifierSurface
Copy Code
public class TestChartModifier : ChartModifierBase
{
   /// <summary>
   /// Called when a Mouse Button is pressed on the parent <see cref="SciChartSurface" />
   /// </summary>
   /// <param name="e">Arguments detailing the mouse button operation</param>
   public override void OnModifierMouseDown(ModifierMouseArgs e)
   {
       base.OnModifierMouseDown(e);

       Point xy = e.MousePoint;
        
       var rect = new Rectangle() { Fill = new SolidColorBrush(Color.FromArgb(0x33, 0xFF, 0xFF, 0xFF), Width = 10, Height = 10 };
          base.ParentSurface.ModifierSurface.Children.Add(rect);
         
          Canvas.SetLeft(rect, 100);
          Canvas.SetTop(rect, 200);
   }
}

Custom ChartModifier Tutorials

A number of custom chartmodifier examples and tutorials have been prepared at https://support.scichart.com/index.php?/Knowledgebase/List/Index/32