Synchronized Axes (Range Sync Groups)
When a dashboard contains multiple SciChartSurface controls that display related data — for example, a distance-domain chart and a time-domain chart showing the same telemetry — it is expected that zooming or panning on one chart updates the others. SciChart provides a purely MVVM range-synchronization mechanism that requires no code-behind and no event wiring between charts.
How It Works
Every IAxisViewModel exposes a RangeSyncGroupId property (string). Axes that share the same non-null group ID are automatically linked: when any axis in the group changes its VisibleRange (via user interaction, programmatic update, or animation), the new range is propagated to every other axis in the same group. Propagation is managed internally by MvvmPropertyManager and requires no manual subscription.
Basic usage — synchronize the X axes of two charts:
| Basic X axes synchronization |
Copy Code |
|---|---|
// ViewModel var distXAxis = new NumericAxisViewModel { Id = "DistX", AxisTitle = "Distance (m)", RangeSyncGroupId = "XDomain" // same group ID }; var timeXAxis = new NumericAxisViewModel { Id = "TimeX", AxisTitle = "Time (s)", RangeSyncGroupId = "XDomain" // same group ID }; | |
With this setup, zooming in on the distance chart automatically zooms the time chart to the same numeric range, and vice versa.
Range Transforms (Different Units)
When synchronized axes represent different physical units (e.g., distance in meters vs. time in seconds, or temperature in Celsius vs. Fahrenheit), a one-to-one range copy is incorrect. To handle this, assign an IRangeSyncTransform to any axis whose units differ from the canonical group unit. Axes without a transform are treated as the identity — their range IS the group range.
The synchronization flow is:
senderRange → sender.ToGroupRange() → [canonical group range] → target.FromGroupRange() → targetRange
The IRangeSyncTransform interface:
| Creating IRangeSyncTransform interface |
Copy Code |
|---|---|
public interface IRangeSyncTransform { /// Converts this axis's VisibleRange to the canonical group range. IRange ToGroupRange(IRange axisRange); /// Converts the canonical group range to this axis's VisibleRange. IRange FromGroupRange(IRange groupRange); } | |
For example, if distance is the canonical unit, the time axis would have a transform that converts time ranges to distance and vice versa. For instance, consider InterpolatingRangeSyncTransform, which performs piecewise-linear interpolation between two parallel arrays of paired values:
| Using InterpolatingRangeSyncTransform |
Copy Code |
|---|---|
// timeValues[i] corresponds to distanceValues[i] double[] timeValues = ...; double[] distanceValues = ...; var timeXAxis = new NumericAxisViewModel { Id = "TimeX", AxisTitle = "Time (s)", RangeSyncGroupId = "XDomain", // Convert between time (this axis) and distance (canonical group unit) RangeSyncTransform = new InterpolatingRangeSyncTransform(timeValues, distanceValues) }; | |
Now when the user zooms the distance chart to [1000 m, 2000 m], the time axis automatically computes the corresponding time window via interpolation.
Behavioral Notes
- Multiple groups: each axis belongs to at most one group. Y axes can have their own groups independent of X axes (e.g., RangeSyncGroupId = "Speed_Y" on speed axes across both charts).
- Cross-type safety: axes of different types (e.g., NumericAxis vs. DateTimeAxis) in the same group are skipped unless an IRangeSyncTransform is provided.
- Null or empty string disables synchronization for that axis.
ZoomExtents Behavior with Synchronized Axes
ZoomExtents resets each axis to the full data range of its series. In a multi-chart dashboard with synchronized axes, this creates a conflict: each chart has different data, so each chart computes different extents. If synchronization were active during ZoomExtents, the charts would fight — chart A zooms to its extents, pushes that range to chart B, chart B overrides it with its own extents, pushes back to A, and so on.
Default Behavior — Sync Suppressed During ZoomExtents
To prevent this, SciChart automatically suppresses range synchronization for the duration of a ZoomExtents operation. Each chart computes and applies its own extents independently. After ZoomExtents completes, synchronization resumes. This is the correct behavior when each chart should show its own full data range.
With default settings, no special configuration is needed — ZoomExtents just works, and each chart lands at its own natural extents without interfering with peers.
Opt-In: RangeSyncOnZoomExtents
Sometimes the opposite behavior is desired: when ZoomExtents is triggered on one chart, all synchronized charts should zoom out together, with ranges transformed through IRangeSyncTransform as usual. For this, set RangeSyncOnZoomExtents = true on the axis that should act as the authoritative source for its sync group after ZoomExtents.
| Adding RangeSyncOnZoomExtents |
Copy Code |
|---|---|
// The distance X axis is the "authority" -- its ZoomExtents result // propagates to all peers in the "XDomain" group. var distXAxis = new NumericAxisViewModel { Id = "DistX", AxisTitle = "Distance (m)", RangeSyncGroupId = "XDomain", RangeSyncOnZoomExtents = true // authoritative source }; // The time X axis receives the propagated range, transformed via its // RangeSyncTransform. Its own ZoomExtents result does NOT propagate // (RangeSyncOnZoomExtents defaults to false). var timeXAxis = new NumericAxisViewModel { Id = "TimeX", AxisTitle = "Time (s)", RangeSyncGroupId = "XDomain", RangeSyncTransform = new InterpolatingRangeSyncTransform(timeValues, distValues) }; | |
Behavioral Notes
- Only one axis per sync group should have RangeSyncOnZoomExtents = true. If multiple axes in the same group are flagged, the last one in iteration order wins.
- The flag only affects ZoomExtents operations. Normal user interactions (zoom, pan, drag) always propagate through the sync group regardless of this setting.
- When using animated ZoomExtents with sync, the authoritative axis drives the animation for the entire group — peer axes do not run independent animations, they follow frame by frame.
Complete Example
Complete example of axes synchronization can be found in "Tutorial 10b: Synchronizing Axis Ranges with MVVM" in the public SciChart Sanbox repo on GitHub