Search code examples
c#annotationsmschart

I want to see remaining line annotation in mschart


In mschart, I use line annotation with SetAnchor(point 8, point 12). If I scroll chart and hide point 8, I cannot see remaining line annotation(point 9 to 12). I want to see remaining line annotation. help me!

my reference ; Samples Environments for Microsoft Chart Controls --> Chart Features --> Annotations --> Annotation Anchoring

private void AddLineAnnotation()
{
    // create a line annotation
    LineAnnotation annotation = new LineAnnotation();

    // setup visual attributes
    annotation.StartCap = LineAnchorCapStyle.Arrow;
    annotation.EndCap = LineAnchorCapStyle.Arrow;
    annotation.LineWidth = 3;
    annotation.LineColor = Color.OrangeRed;
    annotation.ShadowOffset = 2;
    annotation.ClipToChartArea = "Default";

    // prevent moving or selecting
    annotation.AllowMoving = false;
    annotation.AllowAnchorMoving = false;
    annotation.AllowSelecting = false;

    if(Chart1.Series[0].Points.Count > 13)
    {
        // Use the Anchor Method to anchor to points 8 and 12...
        annotation.SetAnchor(Chart1.Series[0].Points[8], Chart1.Series[0].Points[12]);
    }

    // add the annotation to the collection
    Chart1.Annotations.Add(annotation);
}

enter image description here


Solution

  • This is a tricky one.

    The bad news is: I don't think it is possible. I think MSChart will omit all annotations that start outside of the visible area. Maybe the reasoning was to avoid clutter, but who can tell..?

    A workaround would have to take into acount the case when both endpoints are outside and we still would like to see the annotation..

    The good news is that with ownerdrawing one can code a workaround that will indeed draw the lines for both cases.

    The following example shows the drawing code. Make sure to separate the modes for dragging to zoom and dragging to draw a new annotation. I use a checkbox and its CheckChanged event.

    Let's first see it in action:

    enter image description here

    When the start of an annotation is scrolled off, the line drawing sets in. Pretty hard to notice..

    Here is the code for a xxxPaint event:

    private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
    {
        // loop only over line annotations:
        List<LineAnnotation> annos =
            chart1.Annotations.Where(x => x is LineAnnotation)
                                .Cast<LineAnnotation>().ToList();
        if (!annos.Any()) return;
    
        // a few short references
        Graphics g = e.ChartGraphics.Graphics;
        ChartArea ca = chart1.ChartAreas[0];
        Axis ax = ca.AxisX;
        Axis ay = ca.AxisY;
    
        // we want to clip the line to the innerplotposition excluding the scrollbar:
        Rectangle r = Rectangle.Round(InnerPlotPositionClientRectangle(chart1, ca));
        g.SetClip(new Rectangle(r.X, r.Y, r.Width, r.Height - (int)ax.ScrollBar.Size));
        g.InterpolationMode = InterpolationMode.NearestNeighbor;  // pick your mode!
        foreach (LineAnnotation la in annos)
        {
            if (Double.IsNaN(la.Width)) continue;  // *
            // calculate the coordinates
            int x1 = (int)ax.ValueToPixelPosition(la.AnchorX);
            int y1 = (int)ay.ValueToPixelPosition(la.AnchorY);
            int x2 = (int)ax.ValueToPixelPosition(la.AnchorX + la.Width);
            int y2 = (int)ay.ValueToPixelPosition(la.AnchorY + la.Height);
    
            // now we draw the line if necessary:
            if (x1 < r.X || x1 > r.Right)
                using (Pen pen = new Pen(la.LineColor, 0.5f)) g.DrawLine(pen, x1, y1, x2, y2);
        }
        // reset the clip to allow the system drawing a scrollbar
        g.ResetClip();
    }
    

    A few notes:

    • The code assumes (*) that the Annotations are all anchored with AnchorX/Y and have a Width/Height set. If you have used a different ways of anchoring you need to adapt the code.

    • For the clipping part we need to know the pixel size/positon of the InnerPlotPosition. For this you can use the code in e.g. at the bottom of this link.

    • I didn't code anything but a straight line. If you have adorned your annotation you may need to expand on the code;