Pre loader

1

Welcome to the SciChart Forums!

  • Please read our Question Asking Guidelines for how to format a good question
  • Some reputation is required to post answers. Get up-voted to avoid the spam filter!
  • We welcome community answers and upvotes. Every Q&A improves SciChart for everyone

WPF Forums | JavaScript Forums | Android Forums | iOS Forums

3 votes

Hi Marcel,

Keyboard input for ChartModifiers is a hotly requested feature. We haven’t implemented this yet, but you can achieve the same effect by a workaround.

Start by inheriting RolloverModifier and subscribing to the KeyDown event of the parent Window. E.g.

public class KeydownRolloverModifier : RolloverModifier
{
    private double positionFraction = 0.0;

    void KeydownRolloverModifier_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Right)
        {
            positionFraction += 0.01;
        }
        if (e.Key == Key.Left)
        {
            positionFraction -= 0.01;
        }

        // Constrain to 0.0, 1.0
        positionFraction = positionFraction > 1.0 ? 1.0 : positionFraction < 0.0 ? 0.0 : positionFraction;

        UpdateRollover(positionFraction);
    }

    public override void OnAttached()
    {
        base.OnAttached();
        var scichart = (ParentSurface as SciChartSurface);
        scichart.FindLogicalParent<Window>().PreviewKeyDown +=
            new KeyEventHandler(KeydownRolloverModifier_PreviewKeyDown);
    }

    public override void OnModifierMouseMove(ModifierMouseArgs e)
    {
        // Don't call base. This way the mouse does not change the cursor position
        // base.OnModifierMouseMove(e);
    }

    private void UpdateRollover(double fraction)
    {
        var point = new Point(ModifierSurface.Width * fraction, 0);
        point = ModifierSurface.TranslatePoint(point, RootGrid);
        base.OnModifierMouseMove(new ModifierMouseArgs(point, MouseButtons.None, MouseModifier.None, true));
    }
}

The code for FindLogicalParent is as follows:

public static class FrameworkElementExtensions
{
    public static T FindLogicalParent<T>(this FrameworkElement element) where T : FrameworkElement
    {
        var parent = (FrameworkElement)element.Parent;

        if (parent is T)
            return (T)parent;

        return parent.FindLogicalParent<T>();
    }
}

Using the above you should get a key right/left response by moving the rollover. It’ll move by a percentage each time, not to the next data-point. If you wish to achieve data-point by point moves, then maybe take a look at the HitTest API (BaseRenderableSeries.HitTest). This is used in the base RolloverModifier class to get the X,Y values of data-points.

Best regards,
Andrew

3 votes

Hi Deepak, Andrew

There is also another way of achieving this. SciChart allows you to build “mixed charts”, where you have two X axis with different orientation. I compiled a small sample for you, maybe you will find it a bit easier to implement. Please, find the code and screenshot attached.

Also, you can find the example of a histogram implementation in this post.

Hope this helps!

Best regards,
Yuriy

3 votes

It is possible to create a Heatmap and add some annotations on it currently. This will give the closest view possible with SciChart. Is it suitable?

Unfortunately, contour chart isn’t supported directly for now. You could post this request by contacting us as a feature request and we will consider implementing it in future.

Best regards,
Yuriy

3 votes

Hi there,

Keyboard input for ChartModifiers is a hotly requested feature. We haven’t implemented this yet, but you can achieve the same effect by a workaround.

Here is an extract from a related post titled keybindings for rollover functionality

Start by inheriting RolloverModifier and subscribing to the KeyDown event of the parent Window. E.g.

public class KeydownRolloverModifier : RolloverModifier
{
    private double positionFraction = 0.0;

    void KeydownRolloverModifier_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Right)
        {
            positionFraction += 0.01;
        }
        if (e.Key == Key.Left)
        {
            positionFraction -= 0.01;
        }

        // Constrain to 0.0, 1.0
        positionFraction = positionFraction > 1.0 ? 1.0 : positionFraction < 0.0 ? 0.0 : positionFraction;

        UpdateRollover(positionFraction);
    }

    public override void OnAttached()
    {
        base.OnAttached();
        var scichart = (ParentSurface as SciChartSurface);
        scichart.FindLogicalParent<Window>().PreviewKeyDown +=
            new KeyEventHandler(KeydownRolloverModifier_PreviewKeyDown);
    }

    public override void OnModifierMouseMove(ModifierMouseArgs e)
    {
        // Don't call base. This way the mouse does not change the cursor position
        // base.OnModifierMouseMove(e);
    }

    private void UpdateRollover(double fraction)
    {
        var point = new Point(ModifierSurface.Width * fraction, 0);
        point = ModifierSurface.TranslatePoint(point, RootGrid);
        base.OnModifierMouseMove(new ModifierMouseArgs(point, MouseButtons.None, MouseModifier.None, true));
    }
}

The code for FindLogicalParent is as follows:

public static class FrameworkElementExtensions
{
    public static T FindLogicalParent<T>(this FrameworkElement element) where T : FrameworkElement
    {
        var parent = (FrameworkElement)element.Parent;

        if (parent is T)
            return (T)parent;

        return parent.FindLogicalParent<T>();
    }
}

Using the above you should get a key right/left response by moving the rollover. It’ll move by a percentage each time, not to the next data-point. If you wish to achieve data-point by point moves, then maybe take a look at the HitTest API (BaseRenderableSeries.HitTest). This is used in the base RolloverModifier class to get the X,Y values of data-points.

Best regards,
Andrew

3 votes

Hi Simon,

As Nazar mentioned, it just works.

Please see the Video showing SciChart.iOS in UITableView that he created.

Best regards,
Andrew

3 votes

SciChart Expects Sorted X-Data

One of the underlying principles of SciChart is that data should be sorted in ascending order on the X-Axis. The reason for this is many of our users are rendering millions of points, and by far the most time is spent transforming data-points prior to render. Sorting in the X-Direction helps with this, as we can then use efficient binary search algorithms to find indices to data-points and can make several assumptions when resampling.

  • Versions 1.x and 2.x of Scichart will render and behave incorrectly
    (bad rendering, glitches) if data is unsorted.

  • Version 3.x will render correctly, but performance is greatly
    enhanced if data is sorted in X.

Must Data always be sorted?

Actually, no. As of SciChart 3.0 you can have unsorted data in an XyDataSeries. SciChart will automatically detect the distribution of your data (Sorted, Unsorted, Evenly Spaced, Unevely Spaced, Scatter, Line) and apply the correct algorithms, meaning it should render correctly in all circumstances.

However, you will still get massive performance improvements if your data is sorted in the X direction.

What should you do if data arrives out of order?

That doesn’t help you if your data is arriving out of order you can’t append it to the end of the data series. Then, we have the ability to Update, Remove, Insert data via the XyDataSeries class.

DataSeries API Methods include:

You can also directly manipulate the data if you wish to trigger a redraw yourself. Note this does not recalculate flags such as what is the distribution of your data so be careful what you do!

Hope this helps!

3 votes

Hello lovely SciChart people,

To add to the excellent answers above by Marcell and Matt, SciChart v3.0 now supports natively persistence of properties, including annotations.

Please see the article entitled Parts of chart serialization as well as the topic category XML Serialization for more details.

Best regards,
Andrew

3 votes

Hi Lisbeth,

AnnotationCreationModifier is really added as a guideline on how to user-add annotations. Since you have the source code (thanks for upgrading btw!) why not create your own custom modifier which instead of adding to the SciChartSurface.Annotations collection, it handles mouse events and adds new AnnotationViewModels?

E.g. your architecture becomes something like this (refer to attached image)

SciChartSurface

Draws charts, annotations as normal. Binds to ChartViewModel which contains series data

ChartViewModel

Has 1..N AnnotationViewModels which have properties such as Color, Text, Position or other custom properties. Maybe you have a number of annotation view models such as TextAnnotationViewModel, LineAnnotationviewModel if you are mixing types

CustomAnnotationRenderingModifier

Databinds to ChartViewModel.Annotations and consumes annotationViewModels. Adds or maintains the state of 1..N annotations on the parent SciChartSurface. Only concerned with the presentation / rendering. Use the modifier from our MVVM Annotations tutorial as a basis for this.

CustomAnnotationCreationModifier

This class listens to mouse events e.g. two clicks. As the user is placing the two clicks (use our code from AnnotationCreationModifier as a basis for this) it places the line annotation on the chart. Then on completion, it removes that and creates an AnnotationViewModel to represent it. Because of databinding magic and because of the rendering modifier this will get picked up and added to the chart surface.

And hey presto, you have user-creatable annotations plus state management, colours, custom properties in a viewmodel!

Hope this helps,
Andrew

3 votes

An update for you, in SciChart v3.0, the SeriesSelectionModifier has now been reconfigured to trigger on MouseUp. The RubberBandXyZoomModifier sets e.Handled on MouseUp when a successful zoom occurs.

So, putting RubberBandXyZoomModifier before the SeriesSelectionModifier in a ModifierGroup means handled zooms will not cause a series to be selected. his is now demonstrated in our Series Selection Example, which has both RubberBandXyZoomModifier and SeriesSelectionModifier.

<!-- Declare ChartModifiers -->
<s:SciChartSurface.ChartModifier>
    <s:ModifierGroup>

        <!-- Provides Interactivity -->
        <!-- NOTE: Order of Modifiers Matters because of e.Handled! -->
        <s:RubberBandXyZoomModifier/>
        
        <!-- Provides selection of series and custom styling to the selected series -->
        <s:SeriesSelectionModifier>                        
            <s:SeriesSelectionModifier.SelectedSeriesStyle>
                
                <!-- When a series is selected (on click), apply this style -->
                <!-- Changes SeriesColor to white on click -->
                <!-- Applies a PointMarker on click -->
                <Style TargetType="s:BaseRenderableSeries">                                
                    <Setter Property="SeriesColor" Value="White"/>                                
                    <Setter Property="PointMarkerTemplate">
                        <Setter.Value>
                            <!-- This is how we do PointMarkerTemplates in Scichart v2.0. You can either declare an EllipsePointMarker inline, -->
                            <!-- or, if temmplating (because you are sharing a style across many series), then use a ControlTemplate with a BasePointMarker, -->
                            <!-- or UIElement to replicate across each data-point -->
                            <ControlTemplate>
                                <s:EllipsePointMarker Fill="#FF00DC" Stroke="White" Width="7" Height="7"/>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
                
            </s:SeriesSelectionModifier.SelectedSeriesStyle>
        </s:SeriesSelectionModifier>                    
        
    </s:ModifierGroup>
</s:SciChartSurface.ChartModifier>

The RubberBandXyZoomModifier also does not start a zoom unless the user drags a few pixels, to prevent spurious handling of mouse events.

For more information about ChartModifiers and Modifier Precendence, see How to Add Mouse Interaction to SciChart.

3 votes

Hi there,

Thank you for your question! In order to synchronize two charts, you need to perform the following steps:

  1. Set the MouseManager.MouseEventGroup attached property on the root SciChartSurface.ChartModifier. This synchronizes the mouse events between two or more charts which share the same MouseEventGroup ID.

  2. Set the SciChartGroup.VerticalChartGroup attached property on the SciChartSurface itself. This synchronizes the sizes of the YAxis on multiple charts so that the charts line up

  3. Bind the XAxis.VisibleRanges together. This ensures that the charts are always in sync as you zoom / pan (eliminates rounding error).

These are demonstrated in our example Synchronize Multi Chart Mouse.

Doing this in Code Behind (no XAML)

Everything that can be done in XAML can be done in code. It is our convention to use XAML but there are various resources on the web which show you how to convert the two.

A code sample for mouse synchronization can be found below:

var scs0 = new SciChartSurface();
var scs1 = new SciChartSurface();

// omitted Axis, RenderableSeries for brevity
// ...

// 1. Set Mouse Event Group to sync mouse events
MouseManager.SetMouseEventGroup(scs0.ChartModifier, "MyGroup");
MouseManager.SetMouseEventGroup(scs1.ChartModifier, "MyGroup");

// 2. Set Vertical Chart Group to sync YAxis sizes
SciChartGroup.SetVerticalChartGroup(scs0, "ChartGroup");
SciChartGroup.SetVerticalChartGroup(scs1, "ChartGroup");

// 3. Bind XAxis together to sync XAxis.VisibleRange (avoids rounding errors)
var xAxis0 = scs0.XAxis as AxisBase;
var xAxis1 = scs1.XAxis as AxisBase;
Binding myBinding = new Binding("VisibleRange");
myBinding.Source = xAxis0.VisibleRange;
myBinding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(xAxis1, AxisBase.VisibleRangeProperty, myBinding);

Let me know if this helps!

Best regards,
Andrew

3 votes

Hey miles,

I’m also annoyed by this SciChartOverview width thing, so thanks for your request, it’s prompted me to look into it. Ok – so I managed to get this working by modifying the Xaml for the CreateRealTimeStockChart example like this:

        <s:SciChartSurface x:Name="priceChart">
             <!-- omitted for brevity ... -->
        </s:SciChartSurface>

        <!--  Define the Overview control, binding to XVisibleRange, which is shared between this and the SciStockChart control  -->
        <Grid Grid.Row="2" Height="32" >
          
            <!-- The grid is used to set paddings around the Overview, so that the size of the Overview matches the size of the XAxis on the parent chart -->
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20"/> <!-- Matches parent surface padding left -->
                <ColumnDefinition Width="*"/> <!-- Hosts overview control -->
                <ColumnDefinition Width="{Binding ElementName=priceChart, Path=YAxis.ActualWidth, Mode=OneWay}"/> <!-- Used to bind to parent surface YAxis -->
                <ColumnDefinition Width="15"/> <!-- Matches parent surface padding right -->
            </Grid.ColumnDefinitions>
          
            <s:SciChartOverview Grid.Column="1"
                            ParentSurface="{Binding ElementName=priceChart, Mode=OneWay}"
                            s:ThemeManager.Theme="{Binding ElementName=ThemeCombo, Path=SelectedItem}"                           
                            SelectedRange="{Binding XVisibleRange, Mode=TwoWay}" />
          
        </Grid>

The power of binding! Perhaps this ought to go into the SciChartOverview control template in future builds!

Best regards,
Andrew

3 votes

Hi there,

Unfotunately column series doesn’t support this behavior out of the box. But you could easily implement it by overriding GetColumnWidth method:

public class ConstantColumnRenderableSeries : FastColumnRenderableSeries
{
    public static readonly DependencyProperty ColumnWidthProperty = DependencyProperty.Register(
        "ColumnWidth", typeof (IComparable), typeof (ConstantColumnRenderableSeries), new PropertyMetadata(default(IComparable), OnInvalidateParentSurface));

    public IComparable ColumnWidth
    {
        get { return (IComparable) GetValue(ColumnWidthProperty); }
        set { SetValue(ColumnWidthProperty, value); }
    }

    protected override double GetColumnWidth(IPointSeries points, IRenderPassData renderPassData)
    {
        ICoordinateCalculator<double> xCoordinateCalculator = renderPassData.XCoordinateCalculator;

        var value = ConvertToDouble(ColumnWidth);

        return xCoordinateCalculator.GetCoordinate(value) - xCoordinateCalculator.GetCoordinate(0);
    }

    private static double ConvertToDouble(IComparable comparable)
    {
        if (comparable == null)
            return 0;

        if (comparable is DateTime)
            return ((DateTime)comparable).Ticks;

        if (comparable is TimeSpan)
            return ((TimeSpan)comparable).Ticks;

        return Convert.ToDouble(comparable, CultureInfo.InvariantCulture);
    }
}

Then you just need to use it instead of FastColumnRenderabeSeries.

Best regards,
Yura.

3 votes
In reply to: Wrap-around ECG chart

Thanks for asking this question! This gets asked a lot so we took the time to create a short video and example for the benefit of our user-base.

Please take a look at the KB Article here on How to Create a Sweeping ECG Chart.

Best regards,
Andrew

3 votes

Hello Vaquita,

Hmmm … you will need to set the ResamplingMode on XyScatterSeries to None. I do apologise, we should do this by default and there is an open work item to make this change (plus develop other Resampling Algorithms which are more suitable to scatter charts with no loss of visual information).

<SciChart:XyScatterRenderableSeries ResamplingMode="None">
 ...
</SciChart:XyScatterRenderableSeries>

Please try this code, it should work. The downside is it will be slower … You may have to disable AntiAliasing also to get the speed up depending on how many data-points you are adding. I also see you are using the new CrossPointMarker – is that from the v2.0 API? If so, great! If not try it vs. the old ControlTemplate point marker as it might be faster!

Best regards,
Andrew

3 votes

I have no idea. It sounds like a similar error VS puts out (in English) – the type X cannot be found in the namespace Y.

Typically when I’ve seen this its been a badly resolved reference, e.g. you have two DLLs of different versions referenced by different projects, or, you have recently upgraded the DLL version and Visual Studio has not cleared its cache.

Try restarting Visual Studio, deleting all Bin/Obj directories and doing a clean re-build. Does it work?

If not, try creating a blank project and start including these classes. Can Visual Studio see them?

Andrew

3 votes

Hi Danila,

Please take a look on this thread – Correlating metadata with datapoints. It shows several possible approaches to add additional information for data points in your series.

Hope it helps!

3 votes

Hello Ian,

I’m cleaning the threads and found your solution to bind the Overview to the first SciChartSurface inside an ItemsControl

Hi Andrew

In the end I looked at this problem from a completely different angle and came up with this solution.

This is the full SciChartOverviewExtension code.

Thanks & Regards
Ian

SciChartOverviewExtension Source Code

public class SciChartOverviewExtensions
{
    public static readonly DependencyProperty ItemsControlParentSurfaceProperty =
        DependencyProperty.RegisterAttached(&quot;ItemsControlParentSurface&quot;,
        typeof(ItemsControl),
        typeof(SciChartOverviewExtensions),
        new PropertyMetadata(default(ItemsControl), OnItemsControlPropertyChanged));

    public static void SetItemsControlParentSurface(UIElement element, ItemsControl value)
    {
        element.SetValue(ItemsControlParentSurfaceProperty, value);
    }

    public static ItemsControl GetItemsControlParentSurface(UIElement element)
    {
        return (ItemsControl)element.GetValue(ItemsControlParentSurfaceProperty);
    }

    private static void OnItemsControlPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var overview = (SciChartOverview)d;
        var itemsControl = e.NewValue as ItemsControl;
        if (itemsControl == null)
        {
            overview.ParentSurface = null;
            return;
        }

        itemsControl.ItemContainerGenerator.StatusChanged += (_, __) =&gt;
        {
            if (itemsControl.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) return;

            var item0Element = itemsControl.ItemContainerGenerator.ContainerFromIndex(0) as FrameworkElement;
            if (item0Element == null)
            {
                overview.ParentSurface = null;
                return;
            }

            item0Element.Loaded += (s, a) =&gt;
            {
                var sciChartSurfaceOnLoad = item0Element.FindChild&lt;SciChartSurface&gt;();

                overview.ParentSurface = sciChartSurfaceOnLoad;

                overview.ParentSurface.RenderableSeries.CollectionChanged += (o, args) =&gt;
                {
                    if (args.Action == NotifyCollectionChangedAction.Add)
                        overview.DataSeries = overview.ParentSurface.RenderableSeries[0].DataSeries;

                };
            };

        };
    }
}

Usage:

<ItemsControl x:Name="ItemsControl" ItemSource="{Binding ViewModelList}">
    <ItemsControl.ItemTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

<SciChartOverview local:SciChartOverviewExtensions.ItemsControlParentSurface="{Binding ElementName=ItemsControl}"/>

UPDATE v3.2

The above solution will only work with ItemsControl in WPF. It will not work with the SciChartGroup. To add an Overview in the SciChartGroup we have created an FAQ titled How to add a SciChartOverview to a SciChartGroup using the ScrollBar API in v3.2

enter image description here

Hope this helps!

Andrew

3 votes

Hi Jonny,

Try testing Keyboard.Modifiers instead of e.Modifier to see if the CTRL key is pressed.

Then, try setting ExecuteOn=MouseLeftButton for the RubberBandXyZoomModifier.

Like this:

public class RubberBandXyZoomModifierEx : RubberBandXyZoomModifier
{
    public override void OnModifierMouseDown(ModifierMouseArgs e)
    {
        IsXAxisOnly = !e.IsMaster;

        if (!(Keyboard.Modifiers == ModifierKeys.Control))
        {
            base.OnModifierMouseDown(e);
        }
    }
}

public class ZoomPanModifierEx : ZoomPanModifier
{
    public override void OnModifierMouseDown(ModifierMouseArgs e)
    {
        if ((e.MouseButtons == MouseButtons.Left) && (Keyboard.Modifiers == ModifierKeys.Control))
        {
            base.OnModifierMouseDown(e);
        }
    }
}

Hope this helps!
Andrew

3 votes

Thanks for the pointers. They were enough to get me traveling in the right direction with this one.

The issue was the FindAncestor binding. However, rather than having to manage the changes down at the Data Template ViewModel level I found a very nice technique for passing the correct DataContext in the XAML. Here is is…

  1. Create a Binding Proxy
    public class BindingProxy : Freezable
    {
        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        public object Data
        {
            get { return GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Data.
        // This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DataProperty = 
            DependencyProperty.Register("Data", typeof(object), 
            typeof(BindingProxy), new UIPropertyMetadata(null));
    }
  1. Declare it in the XAML and Bind its Data property to the context you need to reference
        <uiServices:BindingProxy x:Key="BindingProxy" Data="{Binding}"/>
  1. Use it to replace the FindAncestor binding
<visuals:RolloverModifier  IsEnabled="{Binding Path=Data.ChartModifier, Source={StaticResource BindingProxy},  Converter={StaticResource IsModifierTypeConverter}, ConverterParameter=Rollover, Mode=TwoWay }"

Hope this helps someone else.

Regards
Ian

3 votes

I am considering applying server-side licensing for my javerScript application.

In the document below, there is a phrase “Our server-side licensing component is written in C++.”
(https://support.scichart.com/index.php?/Knowledgebase/Article/View/17256/42/)

However, there is only asp.net sample code on the provided github.
(https://github.com/ABTSoftware/SciChart.JS.Examples/tree/master/Sandbox/demo-dotnet-server-licensing)

I wonder if there is a sample code implemented in C++ for server-side licensing.

Can you provide c++ sample code?
Also, are there any examples to run on Ubuntu?

Showing 41 - 60 of 6k results