SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, and iOS Chart & Android Chart Components

1 vote
2k views

Upon your advice for a recent question, I have converted my annotations over to an XY scatter point series, using custom point markers to render them. That is working beautifully and is much faster than creating annotations as WPF UIElements. I now need to figure out if/how I can get the tooltips displaying like I would prefer.

I currently have a cursor modifier that I’m using to display a consolidated rollover tooltip for all of the visible series. However, now that the annotations are data series also, their information is getting included in this tooltip and I don’t want it to. I would like to have a separate tooltip that appears when the mouse is hovering over a custom point marker that displays the custom metadata for the point. This was the functionality I had working when I was creating the annotations as actual annotations. I’m hoping I can achieve it when creating them as regular data points.

So I guess my questions are: Is there a way to only include certain data series for a given chart modifier so I can keep my “regular” data and “annotation” data segregated? And, is there a way to show a tooltip with metadata only when the actual point is hovered? I was looking at the Series With Metadata example which is similar to what I want to achieve, but the tooltip appears whenever the mouse hovers over anything, not just when it’s over an actual point.

Thanks,
Scott

  • sdamge asked 3 years ago
  • last active 3 years ago
1 vote
2k views

Hi,

I observed a weird behavior with custom pointmarkers in SciChart. The issue is related to the previous one I reported, so I will not post my code snippets here, because it’s actually the same code as in the link. However, it is a different kind of issue, so I thought I should start a new thread.

Description (I attached a .gif to illustrate the issue):
Inside my RenderSurface, I have some pointmarkers which are filled with a transparent color and others which are filled with the color of the stroke of this pointmarker. Whether or not the pointmarker is filled depends on the IPointMetaData object attached to the point (and the meta data won’t change in any way).
When the chart gets displayed I have 4 pointmarkers visible. All 4 are filled, BUT only the leftmost SHOULD be filled. When I now scroll the chart to the right so that the leftmost pointmarker is not visible anymore the 3 remaining pointmarkers lose their fill and are now displayed as they should. Scrolling the first one back in the get filled again. I can only assume that this is maybe caused by SciChart cacheing some resources internally?

Important: I did only observe this issue with the Direct3D10RenderSurface. Everything works as expected with the other three surface types.

Edit:
seems like the gif animation does not work, so I added two more pictures.

DiaryPointMarker.cs

public class DiaryPointMarker : BasePointMarker
{
    private IList<IPointMetadata> _dataPointMetadata;
    IList<int> _dataPointIndexes = new List<int>();

    private readonly List<Point> _points = new List<Point>();

    public bool UseClustering { get; set; }
    public bool IndicateCommentWithFill { get; set; }

    private IPen2D _lowStrokePen;
    private IPen2D _midStrokePen;
    private IPen2D _highStrokePen;


    private IBrush2D _lowRatingColor;
    private IBrush2D _midRatingColor;
    private IBrush2D _highRatingColor;
    private IBrush2D _noCommentColor;

    public Brush LowRatingColor { get; set; }
    public Brush MidRatingColor { get; set; }
    public Brush HighRatingColor { get; set; }

    public override void BeginBatch(IRenderContext2D context, Color? strokeColor, Color? fillColor)
    {
        _dataPointMetadata = RenderableSeries.DataSeries.Metadata;

        _dataPointIndexes = new List<int>();
        _points.Clear();

        base.BeginBatch(context, strokeColor, fillColor);
    }


    public override void MoveTo(IRenderContext2D context, double x, double y, int index)
    {
        if (IsInBounds(x, y))
        {
            _dataPointIndexes.Add(index);
            _points.Add(new Point(x,y));
        }

        if (UseClustering)
        {
            base.MoveTo(context,x,y,index);
        }
    }

    public override void EndBatch(IRenderContext2D context)
    {
        if (UseClustering)
        {
            base.EndBatch(context);
        }
        else
        {
            Draw(context, _points);
            context.SetPrimitvesCachingEnabled(false);
        }
    }

    public override void Draw(IRenderContext2D context, IEnumerable<Point> centers)
    {
        TryCasheResources(context);

        var markerLocations = centers.ToArray();

        for (int i = 0; i < markerLocations.Length; ++i)
        {
            var diaryMetaInfo = _dataPointMetadata[_dataPointIndexes[i]] as DiaryPointMetaData;

            var center = markerLocations[i];

            var strokePen = diaryMetaInfo.Rating < 60
                ? _lowStrokePen
                : diaryMetaInfo.Rating < 80 ? _midStrokePen : _highStrokePen;
            var fillBrush = diaryMetaInfo.Rating < 60
                ? _lowRatingColor
                : diaryMetaInfo.Rating < 80 ? _midRatingColor : _highRatingColor;

            context.DrawEllipse(
                strokePen,
                IndicateCommentWithFill ? (String.IsNullOrEmpty(diaryMetaInfo.Comment) ? _noCommentColor : fillBrush) : fillBrush,
                center,
                Width,
                Height
                );
        }
    }

    private void TryCasheResources(IRenderContext2D context)
    {
        _lowStrokePen = _lowStrokePen ?? context.CreatePen(LowRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);
        _midStrokePen = _midStrokePen ?? context.CreatePen(MidRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);
        _highStrokePen = _highStrokePen ?? context.CreatePen(HighRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);

        _lowRatingColor = _lowRatingColor ?? context.CreateBrush(LowRatingColor);
        _midRatingColor = _midRatingColor ?? context.CreateBrush(MidRatingColor);
        _highRatingColor = _highRatingColor ?? context.CreateBrush(HighRatingColor);
        _noCommentColor = _noCommentColor ?? context.CreateBrush(Color.FromArgb(0, 0, 0, 0));
    }
}
0 votes
2k views

Hi,

I ran into a problem with EllipsePointMarkers when I have a big amount of PointMarkers inside the Rendersurface. Here is a brief description of what I am trying to do:

  1. I created a custom BasePointMarker-derived class which draws Ellipses with a varying Color – depending on some threshold defined in the IPointMetaData implementation

  2. Everything works fine as long as the points are not tightly spaced between each other. That said, when I rescale my Rendersurface, the points get shifted so that the red points (who are below the threshold) will move up and suddenly green points are at minimum YValue positions.

I attached two pictures which hopefully illustrate my problem better than my words can do. Maybe somebody knows a workaround / fix for this problem. Any help is appreciated! ( I assume it has sth to do with the RescaleMode which prevents some points from being drawn to the surface and shifts others so that the Chart isn’t too dense)

Best,
Matthias

Update
Since Andrew suggested that the behavior comes from my custom code (which is hopefully true), here are the relevant parts:

DiaryView.xaml

<s:SciChartSurface Grid.Row="1" Grid.Column="0"
                       s:ThemeManager.Theme="Chrome"
                       Background="White" RenderableSeries="{s:SeriesBinding DiarySeriesViewModels}">
        <s:SciChartSurface.RenderSurface>
            <s:HighQualityRenderSurface/>
        </s:SciChartSurface.RenderSurface>
        <s:SciChartSurface.YAxis>
            <s:NumericAxis AxisAlignment="Left" DrawMajorBands="False" VisibleRange="0,900"/>
        </s:SciChartSurface.YAxis>
        <s:SciChartSurface.XAxis>
            <s:DateTimeAxis DrawMajorBands="True" VisibleRangeLimit="{Binding VisibleRangeLimit}"
                            DrawMinorGridLines="False"
                            x:Name="XAxis"
                            CursorTextFormatting="dd.MM.yyyy HH:mm"/>
        </s:SciChartSurface.XAxis>
    </s:SciChartSurface>

DiaryViewModel.cs

Property:

    public List<IRenderableSeriesViewModel> DiarySeriesViewModels { get; private set; }

inside the Constructor:

this.DiarySeriesViewModels.Add(new LineRenderableSeriesViewModel()
        {
            AntiAliasing = true,
            PointMarker = new DiaryPointMarker()
            {
                AntiAliasing = true,
                LowRatingColor = Brushes.Red,
                MidRatingColor = Brushes.Orange,
                HighRatingColor = Brushes.Green,
                Width = 10,
                Height = 10,
                StrokeThickness = 2
            },
            DataSeries = GetSampleSeries()
        });

GetSampleSeries():

private XyDataSeries<DateTime, double> GetSampleSeries()
    {
        var series = new XyDataSeries<DateTime, double>();

        var startDate = this.VisibleRangeLimit.Min.AddDays(5);
        var currentDate = startDate;
        int i = 0;
        while (currentDate < DateTime.Now)
        {
            var yval = 60*(Math.Log(i))*Math.Abs(Math.Sin(Math.PI*(i++/100.0)));
            currentDate = currentDate.AddHours(5.3);
            string comment = i%20 == 0 ? String.Format("Sample comment #{0}", i) : string.Empty;
            double rating = 100*yval/60;
            series.Append(currentDate,yval,new DiaryPointMetaData(rating,comment));
        }

        return series;
    }

DiaryPointMarker.cs: (based on an example which I found here at SciChart)

public class DiaryPointMarker : BasePointMarker
{
    private IList<IPointMetadata> _dataPointMetadata;
    IList<int> _dataPointIndexes = new List<int>();


    private IPen2D _lowStrokePen;
    private IPen2D _midStrokePen;
    private IPen2D _highStrokePen;


    private IBrush2D _lowRatingColor;
    private IBrush2D _midRatingColor;
    private IBrush2D _highRatingColor;
    private IBrush2D _noCommentColor;

    public Brush LowRatingColor { get; set; }
    public Brush MidRatingColor { get; set; }
    public Brush HighRatingColor { get; set; }

    public override void BeginBatch(IRenderContext2D context, Color? strokeColor, Color? fillColor)
    {
        _dataPointMetadata = _dataPointMetadata ?? RenderableSeries.DataSeries.Metadata;

        _dataPointIndexes = new List<int>();

        base.BeginBatch(context, strokeColor, fillColor);
    }


    public override void MoveTo(IRenderContext2D context, double x, double y, int index)
    {
        if (IsInBounds(x, y))
        {
            _dataPointIndexes.Add(index);
        }

        base.MoveTo(context, x, y, index);
    }

    public override void Draw(IRenderContext2D context, IEnumerable<Point> centers)
    {
        TryCasheResources(context);

        var markerLocations = centers.ToArray();

        for (int i = 0; i < markerLocations.Length; ++i)
        {
            var diaryMetaInfo = _dataPointMetadata[_dataPointIndexes[i]] as DiaryPointMetaData;

            var center = markerLocations[i];

            context.DrawEllipse(
                diaryMetaInfo.Rating < 60 ? _lowStrokePen : diaryMetaInfo.Rating < 80 ? _midStrokePen : _highStrokePen,
                String.IsNullOrEmpty(diaryMetaInfo.Comment) ? _noCommentColor : diaryMetaInfo.Rating < 60 ? _lowRatingColor : diaryMetaInfo.Rating < 80 ?_midRatingColor : _highRatingColor,
                center,
                Width,
                Height
            );
        }
    }

    private void TryCasheResources(IRenderContext2D context)
    {
        _lowStrokePen = _lowStrokePen ?? context.CreatePen(LowRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);
        _midStrokePen = _midStrokePen ?? context.CreatePen(MidRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);
        _highStrokePen = _highStrokePen ?? context.CreatePen(HighRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);

        _lowRatingColor = _lowRatingColor ?? context.CreateBrush(LowRatingColor);
        _midRatingColor = _midRatingColor ?? context.CreateBrush(MidRatingColor);
        _highRatingColor = _highRatingColor ?? context.CreateBrush(HighRatingColor);
        _noCommentColor = _noCommentColor ?? context.CreateBrush(Color.FromArgb(0, 0, 0, 0));
    }
}
0 votes
2k views

Hi,
I’ve been trying to use CustomPointMarker to build a custom candle, but I need something like

CurrentRenderPassData.YCoordinateCalculator.GetCoordinate(yValue) to know the coordinates of a certain y value in the screen.

I look up CustomPointMarker documentation, but I haven’t found anything.

could you please help me?

Thank you

  • lorenzo522 asked 3 years ago
  • last active 3 years ago
Showing 4 results
This template supports the sidebar's widgets. Add one or use Full Width layout.