2

In Dynamic Data Display, there is a class called MarkerPointsGraph. This derives from FrameWorkElement (through a series of other classes along the way, the most immediate parent being PointsGraphBase) and overrides the OnRenderMethod to draw a set of markers on the chart.

It does this by calling a render method in the appropriate marker type (say, Triangle, Circle and so on) once for each point rendered on the screen. I need to find a way to identify when the mouse hovers on one of these markers so that I can set the tooltip for that marker.

I have a set of methods that allow me to convert a point from the screen position to the viewport position and to the data position and back. i.e. converts a screen value to a corresponding data value or viewport value and vice versa.

I also have the tooltip opening event on this framework element and the pixel size of each of these markers. As long as the user hovers on a particular marker, I need to identify which point he has hovered on and let the markerpointsgraph set the tooltip value.

However, the transforms and methods for converting the values don't seem to be working fine, especially in the x direction. THe y - direction seems to be fine.

Below is some sample code that will explain the idea:

                    double selectedPointX = 0;
                    double selectedPointY = 0;

                    CoordinateTransform transformLocal = this.primaryPlotter.Transform;

                    if (series.SeriesDescription.YAxisAffinity == AxisAffinity_Y.Y1)
                    {
                        selectedPointX = Mouse.GetPosition(this.primaryPlotter).ScreenToViewport(transformLocal).X; //Getting the mouse positions
                        selectedPointY = Mouse.GetPosition(this.primaryPlotter).ScreenToViewport(transformLocal).Y;
                    }
                    else if (series.SeriesDescription.YAxisAffinity == AxisAffinity_Y.Y2 && injSecondaryAxis != null)
                    {
                        transformLocal = injSecondaryAxis.Transform;

                        selectedPointX = Mouse.GetPosition(this.injSecondaryAxis).ScreenToViewport(transformLocal).X;
                        selectedPointY = Mouse.GetPosition(this.injSecondaryAxis).ScreenToViewport(transformLocal).Y;
                    }
                    else if (series.SeriesDescription.YAxisAffinity == AxisAffinity_Y.Y3 && injTertiaryAxis != null)
                    {
                        transformLocal = injTertiaryAxis.Transform;

                        selectedPointX = Mouse.GetPosition(this.injTertiaryAxis).ScreenToViewport(transformLocal).X;
                        selectedPointY = Mouse.GetPosition(this.injTertiaryAxis).ScreenToViewport(transformLocal).Y;
                    }

                    foreach (var item in SeriesList)
                    {
                        if (item.Key == GraphKey)
                        {
                            for (int i = 0; i < item.Value.Collection.Count; i++)
                            {
//Calculate the size of the marker on the screen and allow for some level of inaccuracy in identifying the marker i.e anywhere within the marker is allowed. 
                                double xlowerBound = item.Value.Collection[i].DataToViewport(transformLocal).X - series.MarkerSize;
                                double xUpperBound = item.Value.Collection[i].DataToViewport(transformLocal).X + series.MarkerSize;
                                double ylowerBound = item.Value.Collection[i].DataToViewport(transformLocal).Y - series.MarkerSize;
                                double yUpperBound = item.Value.Collection[i].DataToViewport(transformLocal).Y + series.MarkerSize;

                                //If point is within bounds
                                if (!(selectedPointX < xlowerBound || selectedPointX > xUpperBound || selectedPointY < ylowerBound || selectedPointY > yUpperBound))
                                {
                                    strToolTip = item.Value.Collection[i].X + ", " + item.Value.Collection[i].Y; //This point is set as the tooltip
                                    break;
                                }
                            }

                            break;
                        }
                    }

Here, injSecondary and injTertiary are two injected plotters that provide two vertical Y axes. They behave pretty similar to the primary chart plotter.

Is there anything wrong occurring here? For some reason, points well ahead of the actual clicked point are passing the buffer clause.

Harsha
  • 661
  • 1
  • 8
  • 21

2 Answers2

2

Hmm, looks like you're going about this the wrong way to me. If you dig into the source code of D3, you can open one of the marker classes and directly edit the tooltip there. Every System.Windows.Shapes element has a Tooltip property which you can assign right in the marker class. All you need to do is decide what data the tooltip holds.

Example - The CircleElementPointMarker Class :

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;

namespace Microsoft.Research.DynamicDataDisplay.PointMarkers
{
/// <summary>Adds Circle element at every point of graph</summary>
public class CircleElementPointMarker : ShapeElementPointMarker {

    public override UIElement CreateMarker()
    {
        Ellipse result = new Ellipse();
        result.Width = Size;
        result.Height = Size;
        result.Stroke = Brush;
        result.Fill = Fill;
        if (!String.IsNullOrEmpty(ToolTipText))
        {
            ToolTip tt = new ToolTip();
            tt.Content = ToolTipText;
            result.ToolTip = tt;
        }
        return result;
    }

    public override void SetMarkerProperties(UIElement marker)
    {
        Ellipse ellipse = (Ellipse)marker;

        ellipse.Width = Size;
        ellipse.Height = Size;
        ellipse.Stroke = Brush;
        ellipse.Fill = Fill;

        if (!String.IsNullOrEmpty(ToolTipText))
        {
            ToolTip tt = new ToolTip();
            tt.Content = ToolTipText;
            ellipse.ToolTip = tt;
        }
    }

    public override void SetPosition(UIElement marker, Point screenPoint)
    {
        Canvas.SetLeft(marker, screenPoint.X - Size / 2);
        Canvas.SetTop(marker, screenPoint.Y - Size / 2);
    }
}
}

You can see in this class we have code built in to handle tooltips. The ToolTipText is a property that derives from the parent class - ShapeElementPointMarker. All you have to do is assign that property with the data that you need to show.

Jason Higgins
  • 1,516
  • 1
  • 17
  • 37
  • Yep. Already tried this and it works fine. Also looks to be a greater performance hog than the MarkerPointsGraph. :) – Harsha Nov 16 '12 at 18:41
0

Use a cursorcoordinategraph on the plotter in place of the Mouse.GetPosition to get the correct screen position and avoid the ScreenToViewport transform. Also, use to DataToScreen while creating the bounds.

Harsha
  • 661
  • 1
  • 8
  • 21