2

How do you create an Annotation on-the-run and how do you enable end-user placement with Annotation.BeginPlacement()? I've tried to do this in multiple ways, but cannot get it working. It should render itself in real-time after the BeginPlacement() has been called.

Documentations on this subject is little to none - and mostly none - so I'm not able to find any help for this problem.

What I've tried so far, is to create an annotation and place it with AnchorX/Y, set all Allow- flags to true and called BeginPlacement() while mouse is moving, but cannot see the annotation while placing it nor will it go in it's place accordingly. For example, LineAnnotation starts in right position, but doesn't end where I left it. When I move it so it starts from my ChartAreas {0,0}, it will hit the end-point.

What I want to know, is when and how to use these tools available? What I am trying to do, is to let the user draw annotations on a chart and use as tools when analyzing the charts.

TaW
  • 53,122
  • 8
  • 69
  • 111
misthema
  • 173
  • 1
  • 11

1 Answers1

2

You need to calculate the right positions. Remember that the MouseMove will not give you positions (percentages) or values(data) but pixels. You can transform them using the various axis functions. Officially they only work in a xxxPaint event, but during mouse events they also work fine.

Update: There two ways to do the anchoring:

  • Either by using the 'Positions', i.e. the percentages or the 'Values', i.e. the data values.

Here is an example of the 1st kind:

enter image description here

    LineAnnotation laNew = null;

    private void chart1_MouseDown(object sender, MouseEventArgs e)
    {
        if (cbx_drawAnnotation.Checked)
        {
            Axis ax = chart1.ChartAreas[0].AxisX;
            Axis ay = chart1.ChartAreas[0].AxisY;
            laNew = new LineAnnotation();
            chart1.Annotations.Add(laNew);
            double vx = ax.ValueToPosition(ax.PixelPositionToValue(e.X));
            double vy = ay.ValueToPosition(ay.PixelPositionToValue(e.Y));
            laNew.X = vx;
            laNew.Y = vy;
        }
    }


    private void chart1_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button.HasFlag(MouseButtons.Left) && cbx_drawAnnotation.Checked)
        {
            Axis ax = chart1.ChartAreas[0].AxisX;
            Axis ay = chart1.ChartAreas[0].AxisY;
            double vx = ax.ValueToPosition(ax.PixelPositionToValue(e.X))- laNew.X;
            double vy = ay.ValueToPosition(ay.PixelPositionToValue(e.Y)) - laNew.Y;
            laNew.Width =  Math.Min(100, vx);
            laNew.Height =  Math.Min(100, vy);
            laNew.LineColor = rb_green.Checked ? Color.Green : Color.Red;
            laNew.AllowMoving = true;  // optional
        }
    }

This works fine unles you need to rescale the axis in some way, like changing the axis minimum and/or maximum values.

  • In the case you need to anchor to data values.

First we need to relate the Annotation to the Axes and also set IsSizeAlwaysRelative to false. Then we can calculate the anchor and size values:

private void chart1_MouseDown(object sender, MouseEventArgs e)
{
    if (cbx_drawAnnotation.Checked)
    {
        Axis ax = chart1.ChartAreas[0].AxisX;
        Axis ay = chart1.ChartAreas[0].AxisY;
        laNew = new LineAnnotation();
        chart1.Annotations.Add(laNew);

        laNew.IsSizeAlwaysRelative = false;

        laNew.AxisX = ax;
        laNew.AxisY = ay;

        laNew.AnchorX = ax.PixelPositionToValue(e.X);
        laNew.AnchorY = ay.PixelPositionToValue(e.Y);

        laNew.LineColor = rb_green.Checked ? Color.Green : Color.Red;
        laNew.AllowMoving = true;
    }
}


private void chart1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button.HasFlag(MouseButtons.Left) && cbx_drawAnnotation.Checked)
    {
        Axis ax = chart1.ChartAreas[0].AxisX;
        Axis ay = chart1.ChartAreas[0].AxisY;

        laNew.Width = ax.PixelPositionToValue(e.X) - laNew.AnchorX;   // values
        laNew.Height = ay.PixelPositionToValue(e.Y) - laNew.AnchorY;  
    }
}

Note how I now can scale the maximum and also still resize the the chart and the annotations stay with the data points..:

enter image description here

Update: To restrict the line to the ChartArea add this to the definition in the MouseDown event:

 laNew.ClipToChartArea = chart1.ChartAreas[0].Name;

To prevent an exception from leaving the Chart, add this to the condition in the MouseMove..:

.. && chart1.ClientRectangle.Contains(e.Location)
TaW
  • 53,122
  • 8
  • 69
  • 111
  • I noticed you use percentages in the width and height of the LineAnnotation. Is it possible to get it in Axis values? I do allow time-window resizing (just with Axis min/max, no zoom) and the lines do not "resize" correctly. EDIT: Or is your chart just 100x100...? – misthema Oct 07 '16 at 11:31
  • That is right. All ChartElement.Position.xxx values are in percentages of the respective container. I'll look into switching from this type of anchoring to anchoring relative to DataPoint values.. – TaW Oct 07 '16 at 11:38
  • That would be awesome if you'd find the solution... Thanks in advance! – misthema Oct 07 '16 at 11:45
  • 1
    Anchoring the starting point it easy but somhow the sizing is not. Strange. I'll keep you posted.. – TaW Oct 07 '16 at 12:52
  • Yeah, noticed the exact same thing, really strange.. I hope I don't have to fall into Paint functions, I'm too far ahead already.. :-) – misthema Oct 07 '16 at 13:09
  • 1
    Done. Please test! – TaW Oct 07 '16 at 14:17
  • Exactly what I was looking for. Great, thank you so much! – misthema Oct 07 '16 at 15:50