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

1 vote

Hi Andy,

I must confess, we haven’t built it yet! … However we are getting a lot of feature requests (and hence interest) in our WPF 3D Charts so I will add this one to the list.

Thanks for requesting!

Best regards,
Andrew

1 vote

Hi Cale

In SciChart (any platform) you can’t change the z-order of points.

You can change the z-order of series (series are drawn in order in the RenderableSeries collection). Also you can use PointMetadata.IsSelected to denote points as selected and draw them differently using a PaletteProvider (see DatapointSelectionModifier docs) however the z order won’t be affected.

While it’s possible to create a CustomRenderableSeries to do three drawing passes (default points, selected, highlighted) it would be reimplementing our drawing. Far simpler to maintain three series and use the z-order of series to draw selected / highlighted points on top.

Can you instead consider grouping series logically in your app to achieve this?

0 votes

Hi Grigoriy,

This is one of those things that’s really easy to solve if you have the source-code, but not so easy if you don’t.

The MouseWheelZoomModifier calls a function Axis.Scroll and passes in ClipMode.None. I’ve added a property ClipModeX to the MouseWheelZoomModifier and pushed this to Nightly build 6.2.2.13447.

If you try that build, try the new property can you let me know if it solves your problem?

Thanks and regards,
Andrew

0 votes

Hi Jean Philippe.

A simple calculation to determine the memory requirement of SciChart is as follows.

40,000,000 points of X=Double, Y=Double requires 40,000,000 * 2 * 8 bytes just to store the data values. This is 640MBytes.

In addition, scichart needs some memory to store drawing commands and pixel coordinates (but not much).

Adding Metadata to X,Y points requires a further 4 bytes per data-point = 800MByte. Metadata is not added unless you add it, however for FIFO charts we have to create an empty Metadata array anyway. So it is possible to have 800MByte memory requirement for 40M points.

This is the raw amount of memory required to just store the data.

The limit per-process for 32-bit applications is 1.5GByte of memory and less if the heap is fragmented. So it is easy to get out of memory exception in 32-bit mode with this many data points.

What you can do is change the data-types.

  1. Don’t use FIFO and don’t use metadata as this will discard the 4 bytes per data-point required for the metadata.
  2. Use XType or YType int, float to reduce memory requirements.

e.g. 40M points, non FIFO, X,Y types float will require just 320MByte. A considerable reduction.

Alternatively, force your application to run in 64-bit mode when you have such high data requirements and everything will be OK!

Best regards,
Andrew

0 votes

Hi Gang Xu,

Unfortunately we don’t support such behavior out of the box and it will be quite tricky to implement, because of how inertial scroll is implemented, but it’s possible to do by tracking changes in VisibleRange of target axis. Here a custom modifier which allow to do this:

class CustomZoomPanModifier extends ZoomPanModifier {
    private boolean isInertialAnimation = false;

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        isInertialAnimation = true; // we use inertial scroll for handling fling gesture

        return super.onFling(e1, e2, velocityX, velocityY);
    }

    @Override
    public void onRenderSurfaceRendered(RenderedMessage message) {
        final IRange visibleRange = getXAxis().getVisibleRange();

        final double oldMin = visibleRange.getMinAsDouble();
        final double oldMax = visibleRange.getMaxAsDouble();

        // if we still animating using inertial scroll visible range will change in this call
        super.onRenderSurfaceRendered(message);

        final double newMin = visibleRange.getMinAsDouble();
        final double newMax = visibleRange.getMaxAsDouble();

        // detect when inertial scroll ended
        if(isInertialAnimation && Double.compare(oldMin, newMin) == 0 && Double.compare(oldMax, newMax) == 0) {
            Log.d(TAG, "animation ended");
            // here goes your code
            isInertialAnimation = false; // animation ended
        }
    }
}

Is this suitable for your needs?

Hope this will help you!

Best regards,
Yura

0 votes

Re-Posting:

Hi Andrew. The problem is simply that I don’t think the data is being properly cleared using this code and I can’t see why it shouldn’t work (and neither can anyone else here).

I have a chart defined in XAML. I add 3 series to the bar chart in stacked side-by-side mode using the test button. I then press the Clear button and the chart clears. I then add a new series to the blank chart – nothing is displayed. If you then drag the xaxis modifier the chart displays 4 series but still only 1 on the legend.

I’m having to create the series dynamically based on user choices so I’m not binding anything through XAML. I had a label provider but have removed this to keep everything as simple as possible.

Anyway – the XAML

<UserControl x:Class="MKSCharts.SummaryDrillDownCharting.MassScanAxisUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:s="http://schemas.abtsoftware.co.uk/scichart"
         xmlns:ChartUserControls="clr-namespace:MKSCharts.SummaryDrillDownCharting"
         xmlns:CreateMultiseriesChart="clr-namespace:MKSCharts.SummaryDrillDownCharting"  
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="600">

<UserControl.Resources>
    <!-- <CreateMultiseriesChart:MassScanLabelProviderClass x:Key="MassScanLabelProvider"/> -->
</UserControl.Resources>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition x:Name="Button1Row" Height="Auto"/>
        <RowDefinition x:Name="Button2Row" Height="Auto"/>
        <RowDefinition x:Name="ChartRow" Height="*"/>
    </Grid.RowDefinitions>        

    <Button x:Name="Test" Content="Test Button" Grid.Row="0" Click="Test_Click" />
    <Button x:Name="Clear" Content="Clear Button" Grid.Row="1" Click="Clear_Click" />

    <s:SciChartSurface x:Name="MassScanSciChartSurface" s:ThemeManager.Theme="Chrome" Grid.Row="2">
        <s:SciChartSurface.XAxis>
            <s:NumericAxis AutoRange="Once" AutoTicks="True" MajorDelta="1" MinorDelta="1.0" />
            <!-- VisibleRangeLimit="1,20" LabelProvider="{StaticResource MassScanLabelProvider}"  -->
        </s:SciChartSurface.XAxis>

        <s:SciChartSurface.YAxis>
            <s:NumericAxis AutoRange="Always" AxisAlignment="Left" AxisTitle="Pressure" ScientificNotation="E" TextFormatting="#.#E+0" CursorTextFormatting="#.#E+0" GrowBy="0.0, 0.1"/>
        </s:SciChartSurface.YAxis>

        <s:SciChartSurface.ChartModifier>
            <s:ModifierGroup>
                <s:RubberBandXyZoomModifier x:Name="rubberBandZoomModifier" IsEnabled="True" IsXAxisOnly="False" IsAnimated="True" ExecuteOn="MouseRightButton" ZoomExtentsY="False"/>
                <s:ZoomPanModifier x:Name="zoomPanModifier" ExecuteOn="MouseLeftButton" ZoomExtentsY="False" IsEnabled="True"/>
                <s:ZoomExtentsModifier x:Name="zoomExtentsModifier" ExecuteOn="MouseDoubleClick"/>
                <s:MouseWheelZoomModifier ActionType="Zoom" XyDirection="XYDirection"/>

                <!-- This activates "SCALING" (not zooming) on each axis -->
                <s:XAxisDragModifier x:Name="xAxisDragModifier" IsEnabled="True" ClipModeX="None" />
                <s:YAxisDragModifier x:Name="yAxisLeftDragmodifier" IsEnabled="True" />

                <s:LegendModifier x:Name="LegendModifier" Margin="5" VerticalAlignment="Stretch"  GetLegendDataFor="AllSeries" LegendPlacement="Right" ShowLegend="True" ShowVisibilityCheckboxes="True"/>

            </s:ModifierGroup>

        </s:SciChartSurface.ChartModifier>
    </s:SciChartSurface>
</Grid>

..and the code behind for the buttons:

        public void AddMassScanDataSeries()
    {
        double[] someDataAsArray;

        List<double> someData = new List<double>();
        for (int x = 0; x < 5; x++)
        {
            Random test = new Random();
            double rangeNumber = test.NextDouble();
            someData.Add(rangeNumber);
        }
        someDataAsArray = someData.ToArray();

        StackedColumnRenderableSeries newSeries = new StackedColumnRenderableSeries();
        newSeries.Name = "Series" + _chartNumber.ToString();
        newSeries.SeriesColor = ChartHelper.GetRandomColor();               // random colour from supporting component
        SolidColorBrush randomColorBrush = new SolidColorBrush();
        randomColorBrush.Color = newSeries.SeriesColor;
        newSeries.FillBrush = randomColorBrush;
        newSeries.DataPointWidth = 0.9;

        newSeries.StackedGroupId = "ID" + _chartNumber.ToString();
        var newDataSeries = new XyDataSeries<int, double> { SeriesName = newSeries.StackedGroupId };

        for (int x = 1; x < someDataAsArray.Count() - 1; x++)
        {
            newDataSeries.Append(x, someDataAsArray[x]);
        }
        newSeries.DataSeries = newDataSeries;
        MassScanSciChartSurface.RenderableSeries.Add(newSeries);
        MassScanSciChartSurface.XAxis.VisibleRange = new DoubleRange(1, someDataAsArray.Count());

        // increment the chart number so the next series has a different stackedGroupID
        _chartNumber++;
    }

    private void Test_Click(object sender, RoutedEventArgs e)
    {
        AddMassScanDataSeries();
    }

    private void Clear_Click(object sender, RoutedEventArgs e)
    {
        MassScanSciChartSurface.RenderableSeries.Clear();
    }

I’m using v 3.3.0.5909

1 vote

Hi there,

You should try setting

    <NumericAxis GrowBy="0.1, 0.1"/>

in XAML. This will add some padding around the VisibleRange Min and Max on the chart. The padding is computed as a fraction of VisibleRange. E.g. if XAxis.VisibleRange is 0, 10, then GrowBy of 0.1, 0.1 adds 10% extra to the left and right.

Best regards,
Andrew

0 votes

Answered my own… Should be:
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static sc:AutoRange.Once}"/>

0 votes

Hi

The way to customise the legend is to override legendModifier.sciChartLegend.getLegendItemHTML and legendModifier.sciChartLegend.getLegendHTML

The way it works by default is that within getLegendHTML, getLegendItemHTML is called for each element of the items collections. The default implementation for this is provided by the global getLegendItemHtml which looks like this

export const getLegendItemHtml = (
    orientation: ELegendOrientation,
    showCheckboxes: boolean,
    showSeriesMarkers: boolean,
    item: TLegendItem
): string => {
    const display = orientation === ELegendOrientation.Vertical ? "flex" : "inline-flex";
    let str = `<span class="scichart__legend-item" style="display: ${display}; align-items: center; margin-right: 4px; white-space: nowrap;">`;
    if (showCheckboxes) {
        const checked = item.checked ? "checked" : "";
        str += `<input ${checked} type="checkbox" id="${item.id}">`;
    }
    if (showSeriesMarkers) {
        if (item.gradient) {
            let gradientStr = "";
            item.gradient.gradientStops.forEach(s => {
                gradientStr += `,${s.color}`;
            });
            str += `<label for="${item.id}" style="background-image: linear-gradient(to right${gradientStr}); margin: 4px; width: 30px; height: 13px;"></label>`;
        } else {
            str += `<label for="${item.id}" style="background-color: ${item.color}; margin: 4px; width: 30px; height: 13px;"></label>`;
        }
    }
    str += `<label for="${item.id}" style="margin-left: 4px;">${item.name}</label>`;
    str += `</span>`;
    return str;
};

The resulting string is passed to the global getLegendContainerHtml function, which looks like this:

export const getLegendContainerHtml = (
    placement: ELegendPlacement,
    textColor: string,
    backgroundColor: string,
    margin: Thickness,
    body: string
): string => {
    if (!body) return "";

    const float = [ELegendPlacement.TopLeft, ELegendPlacement.BottomLeft].includes(placement) ? "left" : "right";
    let htmlStr = `<div class="scichart__legend" style="height: 100%; display: flex; float: ${float}; text-align: center;">`;

    const alignSelf = [ELegendPlacement.TopLeft, ELegendPlacement.TopRight].includes(placement)
        ? "flex-start"
        : "flex-end";
    const { left, right, bottom, top } = margin;
    const marginStyle = `margin-left: ${left}px; margin-top: ${top}px; margin-right: ${right}px; margin-bottom: ${bottom}px;`;
    htmlStr += `<div style="display: block; align-self: ${alignSelf}; width: fit-content; pointer-events: auto; ${marginStyle} padding: 5px; border-radius: 3px; background-color: ${backgroundColor}; color: ${textColor}">`;
    htmlStr += body;

    htmlStr += `</div></div>`;
    return htmlStr;
};

So if you just want to update the html for each series, override getLegendItemHTML. If you want to adjust the container as well, override getLegendHTML, and then you can either reuse the per item processing, or provide your own.

Regards
David

0 votes

This example is not working when the number of rows exceed the display size and needing scroll. When a row is scrolled out of view and back it becomes blanks and below errors are logged:

2020-07-16 12:23:48.909 25428-26052/com.scichart.examples E/BufferQueueProducer: [SurfaceTexture-0-25428-323] queueBuffer: BufferQueue has been abandoned
2020-07-16 12:23:48.909 25428-26052/com.scichart.examples E/Surface: queueBuffer: error queuing buffer to SurfaceTexture, -19
2020-07-16 12:23:48.909 25428-26052/com.scichart.examples I/Adreno: QueueBuffer: queueBuffer failed
2020-07-16 12:23:48.909 25428-26052/com.scichart.examples E/GLThread: eglSwapBuffers12301
2020-07-16 12:24:08.661 25428-26119/com.scichart.examples E/BufferQueueProducer: [SurfaceTexture-0-25428-350] dequeueBuffer: BufferQueue has been abandoned
2020-07-16 12:24:08.661 25428-26119/com.scichart.examples I/Adreno: DequeueBuffer: dequeueBuffer failed
2020-07-16 12:24:08.662 25428-26119/com.scichart.examples E/BufferQueueProducer: [SurfaceTexture-0-25428-350] dequeueBuffer: BufferQueue has been abandoned
2020-07-16 12:24:08.662 25428-26119/com.scichart.examples I/Adreno: DequeueBuffer: dequeueBuffer failed
2020-07-16 12:24:08.663 25428-26119/com.scichart.examples E/GLThread: eglSwapBuffers12301
2020-07-16 12:24:08.710 25428-26118/com.scichart.examples E/BufferQueueProducer: [SurfaceTexture-0-25428-349] queueBuffer: BufferQueue has been abandoned
2020-07-16 12:24:08.710 25428-26118/com.scichart.examples E/Surface: queueBuffer: error queuing buffer to SurfaceTexture, -19
2020-07-16 12:24:08.710 25428-26118/com.scichart.examples I/Adreno: QueueBuffer: queueBuffer failed
2020-07-16 12:24:08.711 25428-26118/com.scichart.examples E/GLThread: eglSwapBuffers12301
2020-07-16 12:24:08.778 25428-26104/com.scichart.examples E/BufferQueueProducer: [SurfaceTexture-0-25428-344] queueBuffer: BufferQueue has been abandoned
2020-07-16 12:24:08.778 25428-26104/com.scichart.examples E/Surface: queueBuffer: error queuing buffer to SurfaceTexture, -19
2020-07-16 12:24:08.778 25428-26104/com.scichart.examples I/Adreno: QueueBuffer: queueBuffer failed
2020-07-16 12:24:08.778 25428-26104/com.scichart.examples E/GLThread: eglSwapBuffers12301
2020-07-16 12:24:08.844 25428-26103/com.scichart.examples E/BufferQueueProducer: [SurfaceTexture-0-25428-343] queueBuffer: BufferQueue has been abandoned
2020-07-16 12:24:08.844 25428-26103/com.scichart.examples E/Surface: queueBuffer: error queuing buffer to SurfaceTexture, -19
2020-07-16 12:24:08.844 25428-26103/com.scichart.examples I/Adreno: QueueBuffer: queueBuffer failed
2020-07-16 12:24:08.844 25428-26103/com.scichart.examples E/GLThread: eglSwapBuffers12301
2020-07-16 12:24:08.994 25428-26102/com.scichart.examples E/BufferQueueProducer: [SurfaceTexture-0-25428-342] dequeueBuffer: BufferQueue has been abandoned
2020-07-16 12:24:08.994 25428-26102/com.scichart.examples I/Adreno: DequeueBuffer: dequeueBuffer failed
2020-07-16 12:24:08.994 25428-26102/com.scichart.examples E/BufferQueueProducer: [SurfaceTexture-0-25428-342] dequeueBuffer: BufferQueue has been abandoned
2020-07-16 12:24:08.994 25428-26102/com.scichart.examples I/Adreno: DequeueBuffer: dequeueBuffer failed
2020-07-16 12:24:08.996 25428-26102/com.scichart.examples E/GLThread: eglSwapBuffers12301

1 vote

Sorted it. I declare the SciChartLegend separately from the LegendModifier in the xaml and attach a behaviour to the SciChartLegend. The behaviour declares a dependency property and binds the LegendModifier.LegendData property to it. The behavour subscribes to the LegendModifier.LegendData.SeriesInfo.CollectionChanged event in order to maintain its own ObservableCollection (which is a copy of LegendModifier.LegendData.SeriesInfo with the unwanted SeriesInfo objects filtered out), which it uses to create a new ChartDataObject which is finally assigned to the behaviour’s AssociatedObject.LegendData property.

0 votes

Hi,

Different formats on axis and annotation is the default behavior. This can be worked around by that approach given in the second post. Please, take a look at the attachments for the complete code.

Best regards,
Yuriy

0 votes

This seems like a duplicate of this question:

The use of tooltips causes the discount chart to get stuck

Have you tried my solution there?

0 votes

I’ve found the series.getSegmentIndex(at: ), But how do I animate things like centerOffset?

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

0 votes

Hi Sahar,

Our scrollbar tracks the VisibleRange of the Axis it is bound to. So if the scrollbar is returning to initial position, it is because you are setting the Axis.VisibleRange that the Scrollbar is attached to.

Please double-check your code for anywhere you are setting Axis.VisibleRange. Put some Console.Writeline logging in there and you will find the problem.

Best regards,
Andrew

0 votes
In reply to: scichart2d.js Warnings

Hi Roman

Internally SciChart.js uses document.getElementById() and the warning says this is deprecated in HTML5.

I will add a task into our issue tracker to ensure that we migrate away from this deprecated API.

Best regards,
Andrew

0 votes

Hi there,

Take a look at our examples, we have one with 8 Y-Axes with different label colours per axis.

Update June 2014
From SciChart v3.0 we support customization of axis labels and title via appropriate styles. For more information please take a look on this article Styling Axis Labels, Axis Titles, Rotating Axis Labels

Best regards,
Andrew

0 votes

Yes there is, you just have to bind RenderableSeries.SeriesColor and StrokeThickness to a property in the viewmodel.

e.g.

<s:SciChartSurface>
   <s:SciChartSurface.RenderableSeries>
      <s:FastLineRenderableSeries SeriesColor="{Binding Color}" StrokeThickness="{Binding StrokeThickness}"/>
   </s:SciChartSurface.RenderableSeries>
</s:SciChartSurface>

Then in a viewmodel

public class MyViewModel : INotifyPropertyChanged
{ 
    private int _strokeThickness = 1;
    private Color _color = Color.FromArgb(0xFF, 0xFF, 0x66, 0x00);

    public int StrokeThickness 
    { 
       get { return this._strokeThickness; } 
       set
       { 
           this._strokeThickness = value;
           OnPropertyChanged("StrokeThickness");
       }
    }

    public Color Color 
    { 
       get { return this._color; } 
       set
       {
           this._color = value;
           OnPropertyChanged("Color");
       }
    }

    protected void OnPropertyChanged(string prop)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
             handler(this, new PropertyChangedEventArgs(prop));
        }
    }
}

I hope this helps!

0 votes

Hi Paul

There’s a static method in the BaseRenderrableSeries class called OnInvalidateParentSurface. If you set that as the property notifier then the parent surface will be invalidated.

public class MyRenderableSeries : CustomRenderableSeries
{
    public static readonly DependencyProperty FooProperty = DependencyProperty.Register(
        "Foo", typeof(bool), typeof(MyRenderableSeries), new PropertyMetadata(default(bool), OnInvalidateParentSurface));

    public bool Foo
    {
        get { return (bool) GetValue(FooProperty); }
        set { SetValue(FooProperty, value); }
    }
}

Alternatively, if you need a bit more customisation you can create a handler like this.

public class MyRenderableSeries : CustomRenderableSeries
{
    public static readonly DependencyProperty FooProperty = DependencyProperty.Register(
        "Foo", typeof(bool), typeof(MyRenderableSeries), new PropertyMetadata(default(bool), MyCustomHandler));

    private static void MyCustomHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // Do some logic here with d, e.

        // Then call OnInvalidateParentSurface
        OnInvalidateParentSurface(d, e);
    }

    public bool Foo
    {
        get { return (bool) GetValue(FooProperty); }
        set { SetValue(FooProperty, value); }
    }
}

Let me know if this helps!

Best regards,
Andrew

Showing 1 - 20 of 6k results

Try SciChart Today

Start a trial and discover why we are the choice
of demanding developers worldwide

Start TrialCase Studies