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

Answered
0
0

I’m in the process of evaluating SCIChart for purchase and have encountered what appears to be a bug in the Axis.GetDataValue method when a Title or Legend is present on the chart.

I am placing an annotation on the chart surface at the exact point a right mouse click raises a context menu. The annotation position is set as follows:

var newLabel = (LabelViewModel) e.NewItems[0];
newLabel.X1 = (DateTime) XAxis.GetDataValue(LastRightClickPoint.X);
newLabel.Y1 = (double) YAxis.GetDataValue(LastRightClickPoint.Y);
AddAnnotations(newLabel);

When there is no title, or legend on the chart surface this works exactly as expected and the annotation is placed exactly where the mouse click occurred.

When a title or legend is present on the surface, the annotation placement is shifted exactly the height of the title + the height of legend down from the spot of the mouse click coordinates.

Is this behavior expected? Is there something I’m not taking into account? Is there any easy way to get the pixel offset created by the title or legend?

I obtain the mouse cursor position by overiding the OnModifierMouseDown in a CustomAnnotation chart modifier class:

  /// <summary>
/// The CustomAnnotationChartModifiers provides a bridge between ViewModel and SciChartSurface, where we can 
/// access the Labels (via data binding) as well as the SciChart API to add, remove and manipulate annotations
/// </summary>
public class CustomAnnotationChartModifier : ChartModifierBase
{

    public Point LastRightClickPoint { get; set; }

    public static readonly DependencyProperty LabelsSourceProperty =
        DependencyProperty.Register(
            "LabelsSource",
            typeof (ObservableCollection<LabelViewModel>),
            typeof (CustomAnnotationChartModifier),
            new PropertyMetadata(OnLabelsSourceChanged));


    // Here LabelsSource is IEnumerable, but you could easily make it ObservableCollection<LabelViewModel> 
    // in order to get changed notifications when items are added to, or removed from the collection
    public ObservableCollection<LabelViewModel> LabelsSource
    {
        get
        {
            var labels =  (ObservableCollection<LabelViewModel>)GetValue(LabelsSourceProperty);
            return labels;
    }
        set { SetValue(LabelsSourceProperty, value); }
    }

    // Get a notification when new labels are set.
    private static void OnLabelsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var modifier = (CustomAnnotationChartModifier) d;
        var oldLabels = e.OldValue as INotifyCollectionChanged;
        var newLabels = e.NewValue as INotifyCollectionChanged;


        IEnumerable newValue = e.NewValue as IEnumerable;
        if (newValue != null)
        {
            if (oldLabels != null)
            {
                oldLabels.CollectionChanged -= modifier.OnLabelsCollectionChanged;
            }
            if (newLabels != null)
            {
                newLabels.CollectionChanged += modifier.OnLabelsCollectionChanged;
            }
            modifier.RebuildAnnotations();
        }

    }

    private  void OnLabelsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {

        if (e.NewItems  != null)
        {
            var newLabel = (LabelViewModel) e.NewItems[0];
            newLabel.X1 = (DateTime) XAxis.GetDataValue(LastRightClickPoint.X);
            newLabel.Y1 = (double) YAxis.GetDataValue(LastRightClickPoint.Y);
            AddAnnotations(newLabel);
        }

        if (e.OldItems != null)
        {
           RemoveAnnotations(e.OldItems);
        }
    }


    private void RemoveAnnotations(IList annotationViewModels)
    {
        if (base.ParentSurface != null)
        {
            foreach (var annotationViewModel in annotationViewModels)
            {
                var annotation = base.ParentSurface.Annotations.FirstOrDefault(a => a.DataContext == (annotationViewModel as LabelViewModel)) ;
                base.ParentSurface.Annotations.Remove(annotation);
            }
        }
    }

    private void AddAnnotations(LabelViewModel annotationViewModel)
    {
        if (base.ParentSurface != null)
        {
            base.ParentSurface.Annotations.Add( new CustomTextAnnotation() {DataContext = annotationViewModel});
        }
    }


    // Recreate all annotations
    private void RebuildAnnotations()
    {
        if (base.ParentSurface != null && LabelsSource != null)
        {
            // Take a look at the base class, ChartModifierBase for a wealth of API 
            // methods and properties to manipulate the SciChartSurface
            var annotationCollection = base.ParentSurface.Annotations;
            annotationCollection.Clear();

            foreach (var item in LabelsSource)
            {
                annotationCollection.Add(new CustomTextAnnotation() { DataContext = item });
            }
        }
    }

    /// <summary>
    /// Called when the Chart Modifier is attached to the Chart Surface
    /// </summary>
    public override void OnAttached()
    {
        base.OnAttached();

        // Catch the condition where LabelsSource binds before chart is shown. Rebuild annotations
        if (LabelsSource != null && base.ParentSurface.Annotations.Count == 0)
        {
            RebuildAnnotations();
        }

        //var mainWindow = FindLogicalParent<Window>();

        //mainWindow.PreviewKeyDown -= Annotation_PreviewKeyDown;
    }


    public override void OnModifierMouseDown(ModifierMouseArgs e)
    {
        base.OnModifierMouseDown(e);
        if (e.MouseButtons == MouseButtons.Right)
        {
            //  AddAnnotation(e.MousePoint);
            LastRightClickPoint = e.MousePoint;
        }
    }
  • You must to post comments
Best Answer
0
0

Hi Tell,

Do you think it’s related to this issue ChartModifierBase OnModifierMouseDown reports coordinates incorrectly?

Basically, you may have to translate mouse coordinates to the central viewport before calling GetDataValue / GetCoordinate.

We know these APIs work as all series rendering depends on them! 🙂

Let me know if this helps

Best regards,
Andrew

  • Tell O'Neal
    Yes this was exactly the issue. Thanks for the quick response.
  • You must to post comments
Showing 1 result
Your Answer

Please first to submit.