Is there a way to animate a chart in a C# Form Application in Visual Studio 2013 to show the line being gradually drawn onto the screen once a button is pressed? Thanks in advance :)
Here is an example that should get you started. It first creates test data and then uses a Timer to display them..:
List<PointF> data = new List<PointF>();
Timer timer = new Timer();
private void button1_Click_1(object sender, EventArgs e)
{
data.Clear();
for (int i = 0; i < 400; i++)
{
float x = i / 50f * (float)( Math.Cos(i / 10f));
float y = i / 50f * (float)(Math.Sin(i / 10f));
data.Add(new PointF(x,y));
}
chart1.Series.Clear();
Series S1 = chart1.Series.Add("S1");
Series S2 = chart1.Series.Add("S2");
S2.MarkerSize = 2;
S2.MarkerStyle = MarkerStyle.Circle;
S2.Color = Color.Green;
S1.Color = Color.FromArgb(64, Color.Red);
S1.BorderWidth = 9;
S2.ChartType = SeriesChartType.Point;
S1.ChartType = SeriesChartType.Line;
chart1.ChartAreas[0].AxisX.Minimum = -10;
chart1.ChartAreas[0].AxisX.Maximum = 10;
chart1.ChartAreas[0].AxisY.Minimum = -10;
chart1.ChartAreas[0].AxisY.Maximum = 10;
chart1.ChartAreas[0].BackColor = Color.White;
timer.Interval = 15;
timer.Start();
}
Note that I am using a PointF structure to store the test data as it is a handy floating point structure..
This is the Timer.Tick event. Don't forget to hook it up! Here we draw all data until we are through; then we stop the Timer:
void timer_Tick(object sender, EventArgs e)
{
Series S1 = chart1.Series[0];
Series S2 = chart1.Series[1];
int pointsSoFar = S1.Points.Count;
if (pointsSoFar < data.Count)
{
S1.Points.AddXY(data[pointsSoFar].X, data[pointsSoFar].Y);
S2.Points.AddXY(data[pointsSoFar].X, data[pointsSoFar].Y);
}
else
{
timer.Stop();
chart1.ChartAreas[0].BackColor = Color.AntiqueWhite;
}
}
Note that I have chosen to draw the Lines in a semitransparent color. It is instructive to watch the results of the strong overlapping of the line segments!
And here..
..the resulting..
.. animation..:
Related
I am getting via the serial port some values from an aquisition board. I want to display the values on Y-axis and also have the time on the x-Axis. The values come from the serial port in batches that are NOT of equal size. I therefore created a time-stamp of my own. It doesn't work as seen in the picture below. However, if I set it to x-coordinate to 0 in AddXY, i get my result but of course without the time on the x-axis. I want to have it in seconds. I use a timer set to get data every 500 ms.
private void Timer_Tick(object sender, EventArgs e)
{
string data;
data = _port.ReadExisting();
Axis ax = chart1.ChartAreas[0].AxisX;
ax.IntervalType = DateTimeIntervalType.Milliseconds;
ax.Interval = 500;
chart1.Series[0].XValueType = ChartValueType.DateTime;
ax.LabelStyle.Format = "ss";
var dataBlocks = data.Split('\n');
int counter = 0;
double delta = (double)dataBlocks.Length / (double)500;
foreach (var block in dataBlocks)
{
var numbers = block.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < numbers.Length; i++)
{
double n = double.NaN;
bool ok = double.TryParse(numbers[i], out n);
if (ok)
{
if (n > 100)
{
double temp = counter * delta;
// double temp = 0;
chart1.Series[i].Points.AddXY(temp, n);
counter++;
if (chart1.Series[0].Points.Count > 1000)
chart1.Series[0].Points.RemoveAt(0);
//chart1.ResetAutoValues();
}
}
}
}
}
timer = new Timer();
timer.Tick += Timer_Tick;
timer.Interval = 500;
chart1.ChartAreas[0].AxisY.Maximum = 1024;
chart1.ChartAreas["ChartArea1"].AxisY.MajorGrid.Enabled = false;
chart1.ChartAreas["ChartArea1"].AxisX.MajorGrid.Enabled = false;
chart1.Series[0].Name = "Semnal EKG";[enter image description here][1]
Currently we are having sync issue using TeeChart's Chart Control (.net / c#). In the example shown in attached screenshot, we have two chart controls whose right vertical axis are synced perfectly. Top chart contains area chart while bottom chart contains volume chart. While drawing vertical lines on both charts at specific time interval, we found that vertical lines drawn in both charts having the same value are not synced. Please note that both charts are plotted with same dataset.
We did some r&d on the same issue and our observations states that it is due to different chart styles used in TeeChart's. But as per our client's requirement we need to sync this vertical lines across multiple charts. Any help on this would be greatly appreciated.
The code below align the vertical line for both Series. I think you can do something like that:
public Form1()
{
InitializeComponent();
InitializeChart();
}
Steema.TeeChart.Axis axis1;
DateTime dt;
private void InitializeChart()
{
tChart1 = new Steema.TeeChart.TChart();
tChart1.Dock = DockStyle.Fill;
this.Controls.Add(tChart1);
tChart1.Aspect.View3D = false;
axis1 = new Steema.TeeChart.Axis();
tChart1.Axes.Custom.Add(axis1);
axis1.Horizontal = false;
tChart1.Axes.Right.StartPosition = 0;
tChart1.Axes.Right.EndPosition = 50;
axis1.StartPosition = 51;
axis1.EndPosition = 100;
axis1.OtherSide = true;
axis1.AxisPen.Visible = false;
dt = DateTime.Now;
tChart1.Axes.Bottom.Labels.DateTimeFormat = "dd/MM";
tChart1.Axes.Bottom.Labels.Style = Steema.TeeChart.AxisLabelStyle.Value;
InitializeSeries();
tChart1.Draw();
tChart1.AfterDraw += TChart1_AfterDraw;
}
private void TChart1_AfterDraw(object sender, Steema.TeeChart.Drawing.Graphics3D g)
{
for (int i=0; i<tChart1[0].Count; i++)
{
Point point1, point2;
point1= new Point(tChart1.Axes.Bottom.CalcPosValue(tChart1[0].XValues[i]),tChart1.Axes.Right.CalcYPosValue(tChart1[0].YValues[i]));
point2= new Point(tChart1.Axes.Bottom.CalcPosValue(tChart1[0].XValues[i]), axis1.CalcYPosValue(tChart1[0].YValues[i]));
g.Line(point1, point2);
}
}
private void InitializeSeries()
{
for (int i=0; i<10; i++)
{
if (i==0)
{
new Steema.TeeChart.Styles.Area(tChart1.Chart);
tChart1.Series[i].XValues.DateTime = true;
(tChart1.Series[i] as Steema.TeeChart.Styles.Area).AreaLines.Visible = false;
(tChart1.Series[i] as Steema.TeeChart.Styles.Area).Color = Color.BlueViolet;
(tChart1.Series[i] as Steema.TeeChart.Styles.Area).Transparency = 20;
Random rnd = new Random();
for (int j=0; j<10; ++j)
{
tChart1.Series[i].Add(dt, rnd.Next(100));
dt = dt.AddDays(1);
}
tChart1.Series[i].Marks.Visible = false;
tChart1.Series[i].VertAxis = Steema.TeeChart.Styles.VerticalAxis.Right;
}
else if(i==1)
{
new Steema.TeeChart.Styles.Volume(tChart1.Chart);
tChart1.Series[i].DataSource = tChart1.Series[i - 1];
tChart1.Series[i].CustomVertAxis = axis1;
}
}
}
I have searched some examples of ZedGraph, but I couldn't perform what I wanted. I am drawing real-time data each 20 ms, and I want to show the system time on the x-axis (using the ZedGraph class XAxis). However when I try to draw milliseconds on the x-axis I cannot see any data. Here is my code:
//X-Axis Settings
pane.XAxis.Scale.MinorStep = 1;
pane.XAxis.Scale.MajorStep = 5;
pane.XAxis.Type = AxisType.Date;
pane.XAxis.Scale.Format = "HH:mm:ss.fff";
pane.XAxis.Scale.Min = new XDate(DateTime.Now);
pane.XAxis.Scale.Max = new XDate(DateTime.Now.AddSeconds(10));
pane.XAxis.Scale.MinorUnit = DateUnit.Second;
pane.XAxis.Scale.MajorUnit = DateUnit.Second;
XDate time = new XDate(DateTime.Now.ToOADate());
for (int i = 1; i < 16; i++)
{
listAuido.Add(time, (double)Read_Data1[i]);
}
Scale xScale1 = zgcMasterPane.MasterPane.PaneList[0].XAxis.Scale;
if (time.XLDate > xScale1.Max)
{
xScale1.Max = (XDate)(DateTime.Now.AddSeconds(1));
xScale1.Min = (XDate)(DateTime.Now.AddSeconds(-20));
}
Edit: This code structure is solved my problem.
The following code is drawing all the data on the same x point!
for (int i = 1; i < 16; i++)
{
listAuido.Add((XDate)(DateTime.Now.Millisecond), (double)Read_Data1[i]);
}
Why do you set your XAxis to the date format if you don't want it to appear like that?
OK, try this:
//Declare the x coordinate (time) variable
double xValue = 0;
//Setting the axis
pane.XAxis.Scale.MinorStep = 1;
pane.XAxis.Scale.MajorStep = 5;
pane.XAxis.Scale.Max = 0;
pane.XAxis.Scale.Min = -10;
//drawing the data
private void draw(double dataValue)
{
LineItem curve1 = zedGraphControl1.GraphPane.CurveList[0] as LineItem;
IPointListEdit list1 = curve1.Points as IPointListEdit;
list1.Add(xValue*(20/1000), dataValue); //
//Scroll
Scale XScale = zedGraphControl1.GraphPane.XAxis.Scale as Scale;
XScale.Max = xValue*(20/1000);
XScale.Min = XScale.Max - 10;
xValue++;
zedGraphControl1.AxisChange();
zedGraphControl1.Invalidate();
}
//Now you call the function draw every 20 ms using a [Timer][1] for example
private void timer1_Tick(object sender, EventArgs e)
{
draw(data[xValue]);
}
By the way I am not using MasterPane here.
I'm using Teechart for .net V3.
When trying to rotate the X-labels to 45° some of the labels are not displayed, but if the angle is set to 90° it is OK.
Please see the following images:
This is 45° rotation:
This is 90° rotation:
Is it possible to show all the labels with 45° angle?
I think you can use custom labels to achieve all labels appear when you use an angle of 45º. You can do something as next code:
private Steema.TeeChart.TChart tChart1;
public Form1()
{
InitializeComponent();
tChart1 = new Steema.TeeChart.TChart();
this.Controls.Add(tChart1);
tChart1.Left = 100;
tChart1.Top = 50;
tChart1.Width = 500;
tChart1.Height = 350;
tChart1.Dock = DockStyle.Fill;
InitialzieChart();
}
private void InitialzieChart()
{
Steema.TeeChart.Styles.Bar bar1 = new Steema.TeeChart.Styles.Bar(tChart1.Chart);
DateTime dt = DateTime.Today;
Random rnd = new Random();
bar1.XValues.DateTime = true;
//bar1.date
for (int i = 0; i < 20; i++)
{
bar1.Add(dt, rnd.Next(100));
dt = dt.AddDays(5);
}
tChart1.Axes.Bottom.Labels.Angle = 45;
tChart1.Panel.MarginLeft = 10;
tChart1.Legend.Alignment = Steema.TeeChart.LegendAlignments.Bottom;
AddCustomLabels();
}
private void AddCustomLabels()
{
tChart1.Axes.Bottom.Labels.Items.Clear();
for (int i = 0; i < tChart1[0].Count; i++)
{
tChart1.Axes.Bottom.Labels.Items.Add(tChart1[0].XValues[i], DateTime.FromOADate(tChart1[0].XValues[i]).ToLongDateString());
}
}
Could you tell us if previous code works in your end?
Thanks,
i am testing out how to draw a line that connects the button to another button upon clicking the the button, i got confuse with the coordinates and the settop setleft, how do they actually work in simple terms. i know that we have to set the X2 Y2 ( subtract of start point with end point ) of the line, but i really confuse at what to subtract and how do i do this .
this is so far what i have tried:
int k = 20;
for (int i = 0; i < 4; i++)
{
Button btn = new Button();
btn.Content = i.ToString();
btn.Height = 20;
btn.Width = 20;
Canvas.SetTop(btn,k); // 20
Canvas.SetLeft(btn, 20); // 10
Canvas1.Children.Add(btn);
btn.PreviewMouseDown += (source, e) =>
{
// No idea how to set X2 , Y2 for the line's end point.
Line line = new Line();
//line.X2 = ;
//line.Y2 = ;
line.Stroke = Brushes.Red;
line.StrokeThickness = 4;
Canvas.SetLeft(line,40); // Suppose this is where the line should start
Canvas.SetTop(line ,40); // for button " 0 " .
Canvas1.Children.Add(line);
};
k += 20;
}
for (int i = 0; i < 4; i++)
{
Button btn2 = new Button();
btn2.Content = i.ToString();
btn2.Height = 20;
btn2.Width = 20;
Canvas.SetTop(btn2, k); // 20
Canvas.SetRight(btn2, 20); // 10
Canvas1.Children.Add(btn2);
btn2.PreviewMouseDown += (source, e) =>
{
//Draw Line to connect here.
};
k += 20;
}
i am trying to draw a line from btn to btn2 .
And also , how do i adjust the buttons to be in the same level, for now the right buttons ( btn2 ) is abit lower than the left buttons ( btn) and i want to draw a line to connect the right buttons to the left buttons upon clicking button 0 , so 0 will draw a line to 0 .
Whilst you can do it by moving the Canvas around, you might be better off using the positions of the Buttons themselves on the Canvas.
Essentially, you need to have two sets of coordinates, your LineStart and LineEnd, which in this case will be obtained from the two different Buttons you're clicking.
You could use Point location = ((Button)source).TransformToAncestor(Canvas1).Transform(new Point(0, 0)) to obtain the location of the button being clicked, relative to the parent Canvas.
You would then need to do a similar thing to find the location of the original object that you had clicked (which you don't seem to be storing). You could then simply draw a line between the two calculated Points by setting the X1, X2, Y1, Y2 values.
Adapting your example, you could do something like this (just to demonstrate):
public partial class MainWindow : Window
{
public Button LastClicked { get; set; }
public MainWindow()
{
InitializeComponent();
InitButtons();
}
public void InitButtons()
{
int k = 20;
for (int i = 0; i < 4; i++)
{
Button btn = new Button();
btn.Content = i.ToString();
btn.Height = 20;
btn.Width = 20;
Canvas.SetTop(btn, k); // 20
Canvas.SetLeft(btn, 20); // 10
Canvas1.Children.Add(btn);
btn.PreviewMouseDown += (source, e) =>
{
if (LastClicked != null)
{
// Get button locations.
Point LastClickedLocation = LastClicked.TransformToAncestor(Canvas1).Transform(new Point(0, 0));
Point ThisClickedLocation = ((Button)source).TransformToAncestor(Canvas1).Transform(new Point(0, 0));
// Stop same side lines.
if (LastClickedLocation.X != ThisClickedLocation.X)
{
Line line = new Line();
line.X1 = LastClickedLocation.X;
line.Y1 = LastClickedLocation.Y + btn.Height / 2; // Button Middle.
line.X2 = ThisClickedLocation.X + btn.Width; // Adjust Left side.
line.Y2 = ThisClickedLocation.Y + btn.Height / 2; // Button Middle.
line.Stroke = Brushes.Red;
line.StrokeThickness = 4;
Canvas1.Children.Add(line);
}
}
LastClicked = (Button)source;
};
k += 20;
}
k = 20; // Reset k, this is why your buttons weren't aligned.
for (int i = 0; i < 4; i++)
{
Button btn2 = new Button();
btn2.Content = i.ToString();
btn2.Height = 20;
btn2.Width = 20;
Canvas.SetTop(btn2, k); // 20
Canvas.SetRight(btn2, 20); // 10
Canvas1.Children.Add(btn2);
btn2.PreviewMouseDown += (source, e) =>
{
if (LastClicked != null)
{
// Get button locations.
Point LastClickedLocation = LastClicked.TransformToAncestor(Canvas1).Transform(new Point(0, 0));
Point ThisClickedLocation = ((Button)source).TransformToAncestor(Canvas1).Transform(new Point(0, 0));
// Stop same side lines.
if (LastClickedLocation.X != ThisClickedLocation.X)
{
Line line = new Line();
line.X1 = LastClickedLocation.X + btn2.Width; // Adjust Left side.
line.Y1 = LastClickedLocation.Y + btn2.Height / 2; // Button Middle.
line.X2 = ThisClickedLocation.X;
line.Y2 = ThisClickedLocation.Y + btn2.Height / 2; // Button Middle.
line.Stroke = Brushes.Red;
line.StrokeThickness = 4;
Canvas1.Children.Add(line);
}
}
LastClicked = (Button)source;
};
k += 20;
}
}
}
I wouldn't recommend using this exact code, as it's not particularly bright about handling Button clicks and deciding when to draw lines, but it should demonstrate the drawing and obtaining coordinates that you're after.