How can I draw a stock chart with List<> and DataPointCollection.DataBindY at once?
I know how to add stock data on a chart but I don't want looping and repeating drawing by Add because actual list is much longer.
To draw a chart at once I tried DataPointCollection.DataBindY but it doesn't work. It needs IEnumerable[] but I don't know how to change List<> to IEnumerable[].
public partial class Form1 : Form
{
double[] Candle;
List<double[]> CandleList = new List<double[]>();
public Form1()
{
InitializeComponent();
Candle = new double[] { 5, 0, 3, 1 };
CandleList.Add(Candle);
Candle = new double[] { 10, 5, 7, 9 };
CandleList.Add(Candle);
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < CandleList.Count; i++)
{
chart1.Series[0].Points.Add(CandleList[i]); // works well
}
}
private void button2_Click(object sender, EventArgs e)
{
chart1.Series[0].Points.DataBindY(CandleList); // System.ArgumentException
}
}
Because a list is an iEnumerable, iEnumerable[] means an array of lists.
private void button1_Click(object sender, EventArgs e)
{
List<double>[] Lists = new List<double>[4];
for (int i = 0; i < 4; i++)
{
Lists[i] = new List<double>();
}
Lists[0].Add(5); // high
Lists[0].Add(10);
Lists[1].Add(0); // low
Lists[1].Add(5);
Lists[2].Add(3); // close
Lists[2].Add(8);
Lists[3].Add(2); // open
Lists[3].Add(7);
chart1.Series[0].Points.DataBindY(Lists);
}
Related
I have written a program that is supposed to connect 10 points generated by mouseclicks, with lines.
I am the following problems:
it draws all lines from the top left corner.
I can think of the solution being something along the lines of setting the first point as the first mouseclick, but i do not know how to do this.
code:
public partial class Form1 : Form
{
Point[] punten = new Point[10];
private int kliks = 0;
private int lijst = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
kliks = kliks + 1;
lijst = kliks;
punten[lijst] = e.Location;
if (lijst < 9)
{
punten[lijst] = e.Location;
}
else
{
Pen pen = new Pen(Color.Blue);
Graphics papier = this.CreateGraphics();
papier.DrawLines(pen, punten);
}
}
}
}
The problem for you is using a fixed-size array. Each entry defaults to a point (0,0) and so the Drawlines() method draws the 10 elements, even the ones not set by mouseclicks. This results in the final few points begin at the origin (0,0). In addition, there is a buggy implementation of which clicks to keep and how many have happened in using kliks and lijst for the same thing.
This being C#, it is better to learn how to separate your data model that keeps track of your mouselicks and your display codes that draws the lines.
PointHistory.cs
Consider the following class that uses an array of Point to keep track of the mouseclicks. The class is initialized with the maximum number of points to keep (here 10 for example). There are methods for adding a point AddPoint(point) and to remove the oldest point with RemoveFirstPoint(). The number of points defined is viewed with the .Count property.
Finally, the GetPoints() method, returns a copy of the array with only the elements that have been defined. This method is used in the code that draws the lines.
using System;
using System.Linq;
public class PointHistory
{
private readonly Point[] points;
private readonly int maxPoints;
private int count;
public PointHistory(int pointCapacity)
{
count = 0;
maxPoints = pointCapacity;
points = new Point[pointCapacity];
}
public Point[] GetPoints() { return points.Take(count).ToArray(); }
public int Count { get => count; }
public int Capacity { get => maxPoints; }
public void AddPoint(Point point)
{
if (count < maxPoints)
{
points[count++] = point;
}
}
public void RemoveFirstPoint()
{
if (count > 0)
{
for (int i = 0; i < count - 1; i++)
{
points[i] = points[i + 1];
}
count--;
}
}
}
Form1.cs
Another issue is your drawing, which needs to happen in Paint event handler. Use your MouseClick event to modify the list of points, and the Paint event to do the drawing.
public partial class Form1 : Form
{
readonly PointHistory history;
public Form1()
{
InitializeComponent();
history = new PointHistory(10);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.MouseClick += Form1_MouseClick;
this.Paint += Form1_Paint;
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
history.AddPoint(e.Location);
}
else if (e.Button == MouseButtons.Right)
{
history.RemoveFirstPoint();
history.AddPoint(e.Location);
}
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
if (history.Count >= 2)
{
e.Graphics.DrawLines(Pens.Blue, history.GetPoints());
}
foreach (var point in history.GetPoints())
{
e.Graphics.FillEllipse(Brushes.Blue, point.X - 2, point.Y - 2, 4, 4);
}
}
}
I have added some extra code to add little dots at the mouse clicks for a better visual effect.
Left button clicks try to add points to the history of mouseclicks, up to the limit set in the constructor for PointHistory. The right mousebutton cicks removes the oldest point first before adding the new point.
Based on the comments, I want to present a version below that does not define a separate class for the logic, and everything is done internally withing the Form1 class. Also no advanced libraries such as Linq are used
public partial class Form1 : Form
{
private readonly Point[] points;
private int count;
public Form1()
{
InitializeComponent();
points = new Point[10];
count = 0;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Paint += Form1_Paint;
this.MouseClick += Form1_MouseClick;
}
public Point[] GetPoints()
{
Point[] results = new Point[count];
for (int i = 0; i < count; i++)
{
results[i] = points[i];
}
return results;
// Alternative to the loop above
//
//Array.Copy(points, results, count);
//return results;
}
public void AddPoint(Point point)
{
if (count < points.Length)
{
points[count++] = point;
}
}
public void RemoveFirstPoint()
{
if (count > 0)
{
for (int i = 0; i < count - 1; i++)
{
points[i] = points[i + 1];
}
count--;
}
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
AddPoint(e.Location);
}
else if (e.Button == MouseButtons.Right)
{
RemoveFirstPoint();
AddPoint(e.Location);
}
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
Point[] mouseClicks = GetPoints();
if (count >= 2)
{
e.Graphics.DrawLines(Pens.Blue, mouseClicks);
}
foreach (var point in mouseClicks)
{
e.Graphics.FillEllipse(Brushes.Blue, point.X - 2, point.Y - 2, 4, 4);
}
}
}
The main method to look at is GetPoints() which returns an array of points that have been defined by mouse clicks and ignores any points that have not been defined yet.
There is an alternate implementation commented with uses Array.Copy() instead of a loop which would make for a better imlpementation.
The text document have more pages. They need to handle events for print more pages. But my code written for Copy From Screen() method.
code :
public partial class print : Form
{
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
e.Graphics.DrawImage(bmp, 0, 0);
}
private void printbtn_Click(object sender, EventArgs e)
{
printDocument1.DefaultPageSettings.PaperSize = new System.Drawing.Printing.PaperSize("210 X 297", 820, 800);
printPreviewDialog1.Document = printDocument1;
Graphics g = this.CreateGraphics();
bmp = new Bitmap(this.Size.Width, this.Size.Height, g);
Graphics g1 = Graphics.FromImage(bmp);
g1.CopyFromScreen(this.Location.X, this.Location.Y, 0, 0, this.Size);
printPreviewDialog1.ShowDialog();
}
}
It's up to you to break your pages up into pages and to keep track of what has been printed and whether there are more pages to print. Here is a simple example that will print an arbitrary list of text with ten lines to a page. It will keep printing pages with ten lines until there are no more lines to print.
private List<string> lines = new();
private int lineIndex;
private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
{
lineIndex = 0;
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
for (var i = 0; i < 10 && lineIndex < lines.Count; i++, lineIndex++)
{
e.Graphics.DrawString(lines[lineIndex], Font, Brushes.Black, 0, i * 15);
}
e.HasMorePages = lineIndex < lines.Count;
}
The code resets the line index to zero at the start of each print run. It then prints up to ten lines on the current page and then prints another page if and only if there are still lines to print. It's a simple principle and you need to implement it in the appropriate way for your data.
Another variation is to use the BeginPrint event to process the data and break it into pages there, then print one of those pages per PrintPage event, e.g.
// All the lines to be printed.
private List<string> lines = new();
// The groups of lines to be printed on each page.
private List<string[]> pages;
// The index of the page being printed.
private int pageIndex;
private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
{
// Print 10 lines per page.
const int pageLineCount = 10;
pages = new List<string[]>();
// Take groups of up to 10 lines from the line liust and add them to the page list.
for (var pageStartLineIndex = 0; pageStartLineIndex < lines.Count; pageStartLineIndex += pageLineCount)
{
pages.Add(lines.Skip(pageStartLineIndex).Take(pageLineCount).ToArray());
}
// Start printing at the first page.
pageIndex = 0;
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
// Get the current page and increment the page index.
var page = pages[pageIndex++];
// Print the current page.
for (var i = 0; i < page.Length; i++)
{
e.Graphics.DrawString(page[i], Font, Brushes.Black, 0, i * 15);
}
// Continue printing if and only if there are more pages to print.
e.HasMorePages = pageIndex < pages.Count;
}
I'm trying to add textbox and numericUpandDown values to an array, but it doesn't seem to be working.
Carro []carros = new Carro[1];
private Carro carro;
public Form1()
{
..
}
private void Form1_Load(object sender, EventArgs e)
{
..
}
private void AdicionarCarro()
{
this.carro = new Carro(textboxCor.Text, textboxMarca.Text, textboxModelo.Text,
(int.Parse(numUpDownCilindrada.Text)), (int.Parse(numUpDownVelocidade.Text)));
}
private Carro[] AdicionarArray(Carro carro, Carro[] array)
{
AdicionarCarro();
int novoTamanho = array.Length + 1;
Carro[] carros = new Carro[novoTamanho];
for (int i = 0; i < array.Length; i++)
{
carros[i] = array[i];
}
carros[novoTamanho] = carro;
return carros;
}
private void buttonGravar_Click(object sender, EventArgs e)
{
AdicionarArray(carro, carros);
}
When I type the values and click on the "buttonGravar", it gives me this
Error:
I'd be much delighted to get some tips/help on it.
Using System.Collection.Generic.List<T> would be much simpler, since it doesn't have a fixed size:
List<Carro> carros = new List<Carro>();
carros.AddRange(array);
carros.Add(carro);
return carros;
Better way:
private List<Carro> Carros;
public Form1()
{
Carros = new List<Carro>();
..
}
private void Form1_Load(object sender, EventArgs e)
{
..
}
private void AdicionarCarro()
{
var carro = new Carro(textboxCor.Text, textboxMarca.Text, textboxModelo.Text,
(int.Parse(numUpDownCilindrada.Text)), (int.Parse(numUpDownVelocidade.Text)));
Carros.Add(carro);
}
private void buttonGravar_Click(object sender, EventArgs e)
{
AdicionarCarro();
}
To help you understand your code:
carros[novoTamanho] = carro;
should be
carros[novoTamanho - 2] = carro;
Reason:
Array index starts from 0. novoTamanh represents new length (starting at 1, not 0 unlike index), which is outside array.
It's an index out of range exception because your array Carro is of size tmanho:
Carro[] carros = new Carro[novoTamanho];
and carros can contain exactly "novoTamanho" items indexed from "0" to "novoTamanho -1"
You can simply solve this by defining:
int novoTamanho = array.Length + 2;
Or if you do not want to manage indexes, use Lists:
List<Carro> listCarro = new List<Carro>;
listCarro.AddRAnge(array);
listCarro.Add(carro);
return listCarro.ToArray();
How to access array from another method in this example? I'm new in c# and i really appreciate if you will help me. Thanks in advance!
private void button1_Click(object sender, EventArgs e) {
int[] array1 = new int[5];
for (int i = 0; i < 5; i++) {
array1[i] = i;
}
}
private void button2_Click(object sender, EventArgs e) {
int[] array2 = new int[5];
for (int i = 0; i < 5; i++) {
array2[i] = array1[i];
}
}
As Jon Skeet mentioned in his comment, local variables are by definition local to the method which defines them. If you want something to be visible globally, you need to make it an instance variable, or return it from the method.
Assuming that only array1 needs to be visible, it suffices to declare that one outside.
// Declare the array globally.
int[] array1 = new int[5];
private void button1_Click(object sender, EventArgs e)
{
// Initially the array inside this method.
for(int i=0;i<5;i++)
array1[i]=i;
}
private void button2_Click(object sender, EventArgs e)
{
int[] array2 = new int[5];
// Copy from the global array
for(int i=0;i<5;i++)
{
array2[i]=array1[1];
}
}
Declare both arrays outside to enable access.
int[] array1, array2;
private void button1_Click(object sender, EventArgs e) {
array1 = new int[5];
for (int i = 0; i < 5; i++) {
array1[i] = i;
}
}
private void button2_Click(object sender, EventArgs e) {
array2 = new int[5];
for (int i = 0; i < 5; i++) {
array2[i] = array1[i];
}
}
I am creating some picturebox dynamically and click event for picturebox as follows
Image myImage = Image.FromFile("image/Untitled6.png");
PictureBox[] txtTeamNames = new PictureBox[5];
for (int i = 0; i < txtTeamNames.Length; i++)
{
var txt = new PictureBox();
txtTeamNames[i] = txt;
txtTeamNames[i].Image = myImage;
txtTeamNames[i].Height = 53;
txtTeamNames[i].Width = 48;
this.panel1.Controls.Add(txtTeamNames[i]);
txtTeamNames[i].Visible = true;
txtTeamNames[i].Click += new EventHandler(this.clcikeventhandle);
}
When someone clicks on any picture box, how do I find its array index and name?
void clickEventHandler(object sender, EventArgs e)
{
//???
}
You can access the PictureBox via the sender argument. So try this:
PictureBox[] txtTeamNames;
void YourMethod()
{
Image myImage = Image.FromFile("image/Untitled6.png");
txtTeamNames = new PictureBox[5];
//The same as your code
}
void clcikeventhandle(object sender, EventArgs e)
{
int index = txtTeamNames.IndexOf(sender As PictureBox);
}
EDIT: Approach #2
But if you are not happy with declaring that array in the class scope you can try this approach:
//Same as your code
for (int i = 0; i < txtTeamNames.Length; i++)
{
//Save as your code
txtTeamNames[i].Tag = i; // ADD THIS LINE
}
Then:
void clcikeventhandle(object sender, EventArgs e)
{
int index = int.Parse((sender as PictureBox).Tag.ToString());
}
Another suggestion - create a custom class, which inherits from PictureBox. It will have an extra Index property. And you can set it between these two lines:
txtTeamNames[i].Visible = true;
//assign the index here
txtTeamNames[i].Click += new EventHandler(this.clcikeventhandle);
like so:
txtTeamNames[i].Index = i;
Then in the handler:
void clickEventHandle(object sender, EventArgs e)
{
PictureBox pbox = sender As PictureBox;
int index = pbox.Index();
string name = pbox.Name();
}
You keep the same scope of variables, which may be useful if you are concerned about it. If you are okay with upgrading scope of txtTeamNames to class level, see another answer by Hossein Narimani Rad.
namespace your_name_project
{
public partial class Form_Begin : Form
{
PictureBox[] pictureBoxs = new PictureBox[6];
public Form_Begin()
{
InitializeComponent();
pictureBoxs[0] = pictureBox1; pictureBoxs[1] = pictureBox2; pictureBoxs[2] = pictureBox3;
pictureBoxs[3] = pictureBox4; pictureBoxs[4] = pictureBox5; pictureBoxs[5] = pictureBox6;
}
//continue
List<PictureBox> pictureBoxes = new List<PictureBox>();
private void buttonX1_Click(object sender, EventArgs e)
{
for (int i = 0; i <3; i++)
{
pictureBoxs[i].Image =your_name_project.Properties.Resources.image_1;// load image1 and Image_2from resource in property of picturebox
}
for (int i = 3; i < 6; i++)
{
pictureBoxs[i].Image = your_name_project.Properties.Resources.Image_2;
}
}
}
}
I've tried the 1st approach cited above, but it did not work for me. I needed to change the syntax in order to work. I am unsure why, but my way for approach 1 to work would be:
PictureBox[] txtTeamNames;
void YourMethod()
{
Image myImage = Image.FromFile("image/Untitled6.png");
txtTeamNames = new PictureBox[5];
//The same as your code
}
void clcikeventhandle(object sender, EventArgs e)
{
int index = Array.IndexOf(txtTeamNames, sender); //DIFFERENT HERE
}
If anyone has an idea...