We often get asked how to add a zoom slider to an Axis. In this tutorial, we will create a Custom Chart Modifier to provide zoom slider functionality to a chart.
ScaleFactorModifier
Create a new class called ScaleFactorModifier using the following code.
using System; using System.Windows; using SciChart.Charting.ChartModifiers; using SciChart.Charting.Visuals.Axes; using SciChart.Charting.Visuals.Events; namespace SciChartTutorials.Example { /// <summary> /// Provides ability to bind zoom scale factor of a chart to a slider. /// </summary> public class ScaleFactorModifier : ChartModifierBase { /// <summary> /// Prevents recursive calls of <see cref="SetZoomScaleFactor"/>. /// </summary> private bool _isUpdating; /// <summary> /// Defines the ZoomScaleFactor dependency property. /// </summary> public static readonly DependencyProperty ZoomScaleFactorProperty = DependencyProperty.Register("ZoomScaleFactor", typeof(double), typeof(ScaleFactorModifier), new PropertyMetadata(default(double), OnZoomScaleFactorChanged)); /// <summary> /// Gets or sets zoom depth of the chart. It is calculated as Log10 of ratio between VisibleRange length and MaximumRange length /// (so, value of 0 means 100% zoom, value of -1 means 10% zoom etc.). /// </summary> public double ZoomScaleFactor { get { return (double)GetValue(ZoomScaleFactorProperty); } set { SetValue(ZoomScaleFactorProperty, value); } } /// <summary> /// Axis on which the zoom scale factor should be applied. /// </summary> public IAxis Axis { get { return ParentSurface.XAxis; } } private static void OnZoomScaleFactorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var @this = (ScaleFactorModifier)d; if (@this._isUpdating) return; @this.SetZoomScaleFactor(Math.Pow(10, (double)e.NewValue)); } public override void OnAttached() { base.OnAttached(); Axis.VisibleRangeChanged += OnVisibleRangeChanged; // First call to initialize the ZoomScaleFactor property. OnVisibleRangeChanged(this, null); } public override void OnDetached() { base.OnDetached(); Axis.VisibleRangeChanged -= OnVisibleRangeChanged; } private void OnVisibleRangeChanged(object sender, VisibleRangeChangedEventArgs e) { _isUpdating = true; ZoomScaleFactor = Math.Log10(GetZoomScaleFactor()); _isUpdating = false; } /// <summary> /// Gets linear zoom scale factor (i. e. without logarithm). /// </summary> /// <returns>The linear zoom scale factor.</returns> private double GetZoomScaleFactor() { return Axis.VisibleRange.AsDoubleRange().Diff / Axis.GetMaximumRange().AsDoubleRange().Diff; } /// <summary> /// Sets linear zoom scale factor (i. e. without logarithm). /// </summary> /// <param name="value">The new value.</param> private void SetZoomScaleFactor(double value) { var oldValue = GetZoomScaleFactor(); var diff = value / (2 * oldValue) - 0.5; Axis.ZoomBy(diff, diff); } } }
Now, if you add this modifier to a chart, you’ll be able to bind some dependency property to its ZoomScaleFactor property (or vice versa). Let’s take a look at an example.
<UserControl x:Class="SciChart.Examples.Examples.CreateSimpleChart.BandSeriesChartExampleView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:s="http://schemas.abtsoftware.co.uk/scichart" xmlns:example="clr-namespace:SciChartTutorials.Example" Loaded="BandSeriesChartExampleView_OnLoaded" d:DesignHeight="300" d:DesignWidth="300" mc:Ignorable="d"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <s:SciChartSurface Name="sciChart" s:ThemeManager.Theme="BlackSteel"> <s:SciChartSurface.ChartModifier> <example:ScaleFactorModifier x:Name="scaleFactorModifier" /> </s:SciChartSurface.ChartModifier> <s:SciChartSurface.RenderableSeries> <s:FastBandRenderableSeries Fill="#33279B27" FillY1="#33FF1919" StrokeY1="#FF279B27" Stroke="#FFFF1919" /> </s:SciChartSurface.RenderableSeries> <s:SciChartSurface.XAxis> <s:NumericAxis x:Name="xAxis" /> </s:SciChartSurface.XAxis> <s:SciChartSurface.YAxis> <s:NumericAxis x:Name="yAxis" DrawMajorBands="True"> <s:NumericAxis.GrowBy> <s:DoubleRange Max="0.1" Min="0.1" /> </s:NumericAxis.GrowBy> </s:NumericAxis> </s:SciChartSurface.YAxis> </s:SciChartSurface> <Slider Grid.Row="1" Maximum="0.1" Minimum="-2" Value="{Binding Path=ZoomScaleFactor, ElementName=scaleFactorModifier, Mode=TwoWay}" /> </Grid> </UserControl>
Now, if you move the slider, the chart will zoom in and out, keeping the center of the Visible Range at the same point. And when you zoom by other means (RubberBandXyZoomModifier, PinchZoomModifier etc.), the slider will also change its value.
Warnings
Performance
Due to calculating GetMaximumRange() on each update, this modifier can drop the performance on large amounts of data.
Non-linear axis
You’ll need to change the code a bit if you want to use this modifier on the logarithmic Axis. Essentially, you need to convert ranges to real lengths when you calculate GetZoomScaleFactor().
Clip mode
You will probably want to apply clip mode after zooming so that if your Visible Range stacks to one edge of the Chart series, the zoom slider will expand the chart only in the opposite direction. For that reason, create a method
private void ApplyClipMode() { var max = Axis.GetMaximumRange(); if (!max.IsValueWithinRange(Axis.VisibleRange.Min)) Axis.VisibleRange.Min = max.Min; if (!max.IsValueWithinRange(Axis.VisibleRange.Max)) Axis.VisibleRange.Max = max.Max; }
and call it at the end of OnZoomScaleFactorChanged() method.
Further Reading
To learn more about SciChart APIs, please see the following documentation articles:
Also, there is a set of articles on Custom Chart Modifiers creation available in the Knowledgebase at this link.
Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article