How to draw a stripline diagonally using MSChart? - c#

Tweaking MS Charts. I have successfully draw a dynamic chart but need to draw a line (Yellow) across the chart. How will i draw (yellow) line. I have X and Y values.

Here is an example you can play with:
// we create a general LineAnnotation, ie not Vertical or Horizontal:
LineAnnotation lan = new LineAnnotation();
// we use Axis scaling, not chart scaling
lan.IsSizeAlwaysRelative = false;
lan.LineColor = Color.Yellow;
lan.LineWidth = 5;
// the coordinates of the starting point in axis measurement
lan.X = 3.5d;
lan.Y = 0d;
// the size:
lan.Width = -3.5d;
lan.Height = 5.5d;
// looks like we need an anchor point, no matter which..
lan.AnchorDataPoint = yourSeries.Points[0];
// now we can add the LineAnnotation;
chart1.Annotations.Add(lan);

Related

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);
}
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:
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;

Spline chart smooth corners

I am using Chart control from .NET framework in my project. I have added chart control to the form and configured as shown below.
// Add a new series.
chart1.Series.Add("1");
var series = chart1.Series[0];
series.ChartType = SeriesChartType.Spline;
// Hide the legend.
series.IsVisibleInLegend = false;
// configure x axis.
var cArea = chart1.ChartAreas[0];
cArea.AxisX.IntervalType = DateTimeIntervalType.Number;
cArea.AxisX.LabelStyle.Format = "00";
cArea.AxisY.LabelStyle.Format = "0.000";
cArea.AxisY.LabelStyle.IsEndLabelVisible = true;
cArea.AxisX.Minimum = 0;
cArea.AxisX.Maximum = 100;
cArea.AxisX.Interval = 20;
cArea.AxisY.Minimum = 0;
cArea.AxisY.Maximum = 100;
cArea.AxisX.Interval = 20;
Data point values are as below:
chart1.Series[0].Points.AddXY(0, 5);
chart1.Series[0].Points.AddXY(5, 10);
chart1.Series[0].Points.AddXY(10, 30);
chart1.Series[0].Points.AddXY(20, 100);
chart1.Series[0].Points.AddXY(30, 100);
chart1.Series[0].Points.AddXY(40, 90);
chart1.Series[0].Points.AddXY(50, 80);
For the above data points, series is not smooth. Upper edge is getting cut. Refer attached image.
How to make it smooth so that whole line is visible ?
It's not visible because of the smoothing, adapt the scale (using cArea.AxisX.Maximum = 150; for example) or remove the smoothing to make the whole curve visible.
As with the DrawCurves GDI+ method you can control the tension of the splines, i.e. how close they are to the points and their connecting lines and how much smoothing they create. Too much 'smoothing' creates the fantasy tops you see and also crazy whirls from even small bumps in the data..
Setting the tension it is done via the LineTension Custom attribute.
Lower it from the default of 0.8 to something smaller. Test to see what you prefer.
Here is an example for a Series S :
S.SetCustomProperty("LineTension", "0.4");
Note that you still should make the y-axis Maximum a little larger or else you may need to bring the tension down to 0, which will look like a line type..
Here are a few variations:

How to move a WPF shape using an animation

I want to move a shape using an animation and am currently using the following code. However this does not result in the object actually moving, it just seems to change the location the object is rendered in, which is expected given we are setting the crossHair.RenterTransform.
EDIT:
To clarify - I am using the animation to simulate what an input file instructions contain and as a result think I can only build the animations in code when parsing the input files. I could be wrong about this but can't see how one could do this in XAML. The input file format is not in XAML.
Because there are a number of sequential moves contained in the input file I an currently using the shapes current position as the start point for the next animation, however this does not work because it seems the animation is not actually moving the shape.
As a work around I am now changing the shapes actual position in the animations completion handler. This seems to be working.
So the question remains as to how can I use the same transform to actually move the shape rather than simply rendering it in a different position ?
private Storyboard MoveCrossHairToPoint(double x, double y)
{
// Adjust for crosshair size so its centered on the x
double xPos = x;
double yPos = y;
double xStart = Canvas.GetLeft(crossHair)+crossHair.Width/2;
double yStart = Canvas.GetTop(crossHair)+crossHair.Height/2;
// Create a NameScope for the page so that 
// we can use Storyboards.
NameScope.SetNameScope(this, new NameScope());
// Create a MatrixTransform. This transform 
// will be used to move the crossHair.
MatrixTransform crossHairMatrixTransform = new MatrixTransform();
crossHair.RenderTransform = crossHairMatrixTransform;
// Register the transform's name with the page 
// so that it can be targeted by a Storyboard. 
this.RegisterName("MoveCrossHairMatrixTransform", crossHairMatrixTransform);
// Create the animation path.
PathGeometry animationPath = new PathGeometry();
PathFigure pFigure = new PathFigure();
pFigure.StartPoint = new Point(xStart, yStart);
LineSegment lineSegment = new LineSegment(new Point(x, y),true);
pFigure.Segments.Add(lineSegment);
animationPath.Figures.Add(pFigure);
// Create a path to follow
Path path = new Path();
path.Data = animationPath;
path.Stroke = System.Windows.Media.Brushes.Green;
this.bedCanvas.Children.Add(path);
// Freeze the PathGeometry for performance benefits.
animationPath.Freeze();
// Create a MatrixAnimationUsingPath to move the 
// button along the path by animating 
// its MatrixTransform.
MatrixAnimationUsingPath matrixAnimation =
new MatrixAnimationUsingPath();
matrixAnimation.PathGeometry = animationPath;
double time = GetTimeForVelocityOverPath(animationPath, this.velocityMove);
matrixAnimation.Duration = TimeSpan.FromSeconds(time);
//matrixAnimation.RepeatBehavior = RepeatBehavior.;
// Set the animation's DoesRotateWithTangent property 
// to true so that rotates the rectangle in addition 
// to moving it.
matrixAnimation.DoesRotateWithTangent = false;
// Set the animation to target the Matrix property 
// of the MatrixTransform named "ButtonMatrixTransform".
Storyboard.SetTargetName(matrixAnimation, "MoveCrossHairMatrixTransform");
Storyboard.SetTargetProperty(matrixAnimation,
new PropertyPath(MatrixTransform.MatrixProperty));
// Create a Storyboard to contain and apply the animation.
Storyboard pathAnimationStoryboard = new Storyboard();
pathAnimationStoryboard.Children.Add(matrixAnimation);
return pathAnimationStoryboard;
}

Get chart area bounds in C#

Just to explain what I'm doing, I draw two selectors on a chart, and the part that will not be selected should appear under that blue rectangle. The part that will be selected will appear in the white area, between the two selectors. The figure below shows only the left selector.
Now, what I'm trying to do is to draw a rectangle inside a chart that always remain inside the plotting area, even when the windows is resized.
To get the top, left and bottom bounds, to draw the rectangle as shown in the figure below, I do the following:
(...)
int top = (int)(Chart.Height * 0.07);
int bottom = (int)(Chart.Height - 1.83 * top);
int left = (int)(0.083 * Chart.Width);
Brush b = new SolidBrush(Color.FromArgb(128, Color.Blue));
e.Graphics.FillRectangle(b, left, top, marker1.X - left, bottom - top);
(...)
But that's far from perfect, and it isn't drawn in the right place when the window is resized. I want the blue rectangle to always be bound on the top, left and bottom by the plotting area grid. Is that possible?
You probably want to use StripLine to achieve this.
Look into the Stripline Class Documentation.
Also I recommend downloading the Charting Samples which are a great help to understand the various features.
StripLine stripLine = new StripLine();
stripLine.Interval = 0; // Set Strip lines interval to 0 for non periodic stuff
stripLine.StripWidth = 10; // the width of the highlighted area
stripline.IntervalOffset = 2; // the starting X coord of the highlighted area
// pick you color etc ... before adding the stripline to the axis
chart.ChartAreas["Default"].AxisX.StripLines.Add( stripLine );
This assumes you are wanting something that is not what Cursor already does (see CursorX), such as letting the user mark up areas of the plot which provides some persistence. Combining the Cursor events with the striplines above would be a good way to do that.
So to highlight the start and end of the cursor you could do this
// this would most likely be done through the designer
chartArea1.AxisX.ScaleView.Zoomable = false;
chartArea1.CursorX.IsUserEnabled = true;
chartArea1.CursorX.IsUserSelectionEnabled = true;
this.chart1.SelectionRangeChanged += new System.EventHandler<System.Windows.Forms.DataVisualization.Charting.CursorEventArgs>(this.chart1_SelectionRangeChanged);
...
private void chart1_SelectionRangeChanged(object sender, CursorEventArgs e)
{
chart1.ChartAreas[0].AxisX.StripLines.Clear();
StripLine stripLine1 = new StripLine();
stripLine1.Interval = 0;
stripLine1.StripWidth = chart1.ChartAreas[0].CursorX.SelectionStart - chart1.ChartAreas[0].AxisX.Minimum;
stripLine1.IntervalOffset = chart1.ChartAreas[0].AxisX.Minimum;
// pick you color etc ... before adding the stripline to the axis
stripLine1.BackColor = Color.Blue;
chart1.ChartAreas[0].AxisX.StripLines.Add(stripLine1);
StripLine stripLine2 = new StripLine();
stripLine2.Interval = 0;
stripLine2.StripWidth = chart1.ChartAreas[0].AxisX.Maximum - chart1.ChartAreas[0].CursorX.SelectionEnd;
stripLine2.IntervalOffset = chart1.ChartAreas[0].CursorX.SelectionEnd;
// pick you color etc ... before adding the stripline to the axis
stripLine2.BackColor = Color.Blue;
chart1.ChartAreas[0].AxisX.StripLines.Add(stripLine2);
}
Somehow I suspect you may not have discovered the cursor yet, and doing so will make all this irrelevant. But anyway, the above code will do what you described.

C# Charting zoom problem with DataTime for X axis

using the MS Charting for .NET, I am trying to zoom into the chart which I have created.
This works fine on the Y axis (type = float) and on the X axis if type = int, but when I have DateTime values on the X axis, scrolling does not behave as it should on this axis.
Vertically, everything still behaves properly, but while I can zoom into the X axis, I cannot drag the sliding bar to move where I am zoomed into. However, I can click either side and it will jump.
Does anyone know how to fix this and make it behave like it does with float values?
Thanks!
Depending on your data, try setting the chart area's CursorX.IntervalType property to something other than Auto.
You may run into a similar issue when trying to use the small scroll arrows of the scroll bar once you are zoomed in. To fix that you can try to set the chart area's AxisX.ScaleView.SmallScrollSizeType property to the same thing as the CursorX.IntervalType.
For example, if you have a chart with data that is reported every 30 seconds you can use the following settings:
chart1.ChartAreas[0].CursorX.IsUserEnabled = true;
chart1.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
chart1.ChartAreas[0].CursorX.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Minutes;
chart1.ChartAreas[0].CursorX.Interval = 0.5D;
chart1.ChartAreas[0].AxisX.ScaleView.SmallScrollSizeType = DateTimeIntervalType.Minutes;
chart1.ChartAreas[0].AxisX.ScaleView.SmallScrollSize = 0.5D;
chart1.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
chart1.ChartAreas[0].AxisX.LabelStyle.Format = "hh:mm:ss";
I had the same problem and these settings solve it for me:
_chart.ChartAreas[0].CursorX.IsUserEnabled = true;
_chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
_chart.ChartAreas[0].CursorX.IntervalType = DateTimeIntervalType.Minutes;
_chart.ChartAreas[0].CursorX.Interval = 1D;
_chart.ChartAreas[0].AxisX.ScaleView.SmallScrollSizeType = DateTimeIntervalType.Minutes;
_chart.ChartAreas[0].AxisX.ScaleView.SmallScrollSize = 1D;
_chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
_chart.ChartAreas[0].AxisX.ScaleView.MinSizeType = DateTimeIntervalType.Minutes;
_chart.ChartAreas[0].AxisX.ScaleView.MinSize = 1D;
_chart.ChartAreas[0].AxisX.ScaleView.SmallScrollMinSizeType = DateTimeIntervalType.Minutes;
_chart.ChartAreas[0].AxisX.ScaleView.SmallScrollMinSize = 1D;
Especially the last two lines did the job.
add
chart.ChartAreas[0].AxisX.ScaleView.SmallScrollMinSizeType = DateTimeIntervalType.Seconds;
My solution was:
chart1.ChartAreas[0].CursorX.IntervalType = DateTimeIntervalType.Milliseconds;

Categories

Resources