Get coordinates of a drawing point from Chart Values in TextBoxes? - c#

In the designer I have two TextBoxes.
And also a Chart control.
I want that when I type in the first textBox the number 120 and in the second one type the number 1 it will draw a point on the chart in 120,1 but I mean 120 and 1 as axis x and axis y values.
The red filled circle is not at 120 and 1.
I mean that the red circle should be drawn on the left axis on 120.
And if I will put instead 120 116 and instead 1 25 then the circle should be drawn at the left axis 116 and on the bottom axis on 25.
But now the circle is drawn out of the chart.
This is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Drawing.Drawing2D;
using System.Collections;
namespace Test
{
public partial class Form1 : Form
{
private Point startPoint = new Point();
private Point endPoint = new Point();
private int X = 0;
private int Y = 0;
private List<Point> points = new List<Point>();
private Point lastPoint = Point.Empty;
private ArrayList myPts = new ArrayList();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Random rdn = new Random();
for (int i = 120; i > 0; i--)
{
chart1.Series["Series1"].Points.AddXY
(rdn.Next(0, 10), rdn.Next(0, 10));
}
chart1.Series["Series1"].ChartType = SeriesChartType.FastLine;
chart1.Series["Series1"].Color = Color.Red;
ChartArea area = chart1.ChartAreas[0];
area.AxisX.Minimum = 1;
area.AxisX.Maximum = 30;
area.AxisY.Minimum = 1;
area.AxisY.Maximum = 120;
LineAnnotation line = new LineAnnotation();
Point p1 = new Point(1, 120);
chart1.Annotations.Add(line);
line.AxisX = area.AxisX;
line.AxisY = area.AxisY;
line.IsSizeAlwaysRelative = false;
line.X = 1; line.Y = 120;
line.Right = 30; line.Bottom = 1;
line.LineColor = Color.Blue;
line.LineWidth = 3;
}
SolidBrush myBrush = new SolidBrush(Color.Red);
private void chart1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
foreach (Point p in myPts)
g.FillEllipse(myBrush, p.X, p.Y, 10, 10);
}
private void chart1_MouseClick(object sender, MouseEventArgs e)
{
myPts.Add(new Point(X,Y));
chart1.Invalidate();
}
private void txtT_TextChanged(object sender, EventArgs e)
{
X = int.Parse(txtWeight.Text);
}
private void txtDays_TextChanged(object sender, EventArgs e)
{
Y = int.Parse(txtDays.Text);
}
}
}
What I did is that after I enter both textBoxes values then when I click anywhere on the Chart control area with the mouse it should draw the circule on the coordinates from the TextBoxes.
But the circle is not drawing on the right place.
The textBox name txtT is the left one the axis on the left values.
The textBox txtDays should is the axis on the bottom values.

The task of translating drawing coordinates into DataPoints and back is not exactly intuitive.
It is possible but you need to know the rules and pay a certain price.
I have outlined the way in this post and it is worth looking into..
But as the problem is coming up repeatedly, here is a more general solution.
Here is how to call it:
private void button11_Click(object sender, EventArgs e)
{
valuePoints.Add(new PointF(640, 1));
valuePoints.Add(new PointF(670, 10));
paintToCalaculate = true;
chart1.Invalidate();
}
And here is the result: Two red points drawn at the values 640, 1 and 670, 10:
The points are placed correctly event though I have zoomed and scrolled in the chart and also resized it..
To make it work we need three class level variables: A flag and two point lists. One list is the input with the values in the chart where the points are, the other is the output, receiving the current pixel coordinates.
bool paintToCalaculate = false;
List<Point> drawPoints = new List<Point>();
List<PointF> valuePoints = new List<PointF>();
I use a flag to avoid recalculating the Points when the system causes redraws. And after setting it I trigger the Paint event by Invalidating the Chart. During the Paint event I reset the flag.
Please note that these values are very volatile: They change:
Whenever you zoom or scroll
Whenever you resize the chart
Whenever the layout changes, maybe because new points need room or trigger a change in a label format..
Therefore those drawing coordinates will have to be updated on each such event!
Here is one example, that takes care of zoom and scrolling:
private void chart1_AxisViewChanged(object sender, ViewEventArgs e)
{
paintToCalaculate = true;
chart1.Invalidate();
}
You need to add these two lines, or a function to wrap them, to a few other spots in your program, depending what things you allow to happen in the chart.. Resize is also an obvious candidate..
Now for the actual caculation. It is using the ValueToPixelPosition, which does all the work. Unfortunately is only works inside of any of the three paint events of a chart (PrePaint,Paint and PostPaint) . Here I use the normal Paint event.
private void chart1_Paint(object sender, PaintEventArgs e)
{
if (paintToCalaculate)
{
Series s = chart1.Series.FindByName("dummy");
if (s == null) s = chart1.Series.Add("dummy");
drawPoints.Clear();
s.Points.Clear();
foreach (PointF p in valuePoints)
{
s.Points.AddXY(p.X, p.Y);
DataPoint pt = s.Points[0];
double x = chart1.ChartAreas[0].AxisX.ValueToPixelPosition(pt.XValue);
double y = chart1.ChartAreas[0].AxisY.ValueToPixelPosition(pt.YValues[0]);
drawPoints.Add(new Point((int)x, (int)y));
s.Points.Clear();
}
paintToCalaculate = false;
chart1.Series.Remove(s);
}
//..
// now we can draw our points at the current positions:
foreach (Point p in drawPoints)
e.Graphics.FillEllipse(Brushes.Red, p.X - 2, p.Y - 2, 4, 4);
}
Note that I add and remove a dummy Series and add and clear one Point to it for each data point, just to calculate its pixel coordinates. Yes, a bit involved, but the results are worth it..
I assume you can change the Button_Click code to read in the values from your TextBoxes instead of the using my hard-coded numbers..

Related

C# WindowForm How can I make line cursor in PictureBox?

You know, we can easily to make line cursor for Chart (ex: Fig). But with PictureBox, how can I do it? Is there anyone has the solution?
You can intercept the MouseMove and the Paint events. Just draw the cross on the paint.
The advantage of using the Paint method, is that the original image is not changed, so no need to restore the overwritten pixels by the crosshair.
Here's an example:
I dropped a picturebox on a winform and linked some events.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace MouseCrosshair
{
public partial class Form1 : Form
{
// to store the latest mouse position
private Point? _mousePos;
// the pen to draw the crosshair.
private Pen _pen = new Pen(Brushes.Red);
public Form1()
{
InitializeComponent();
}
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
// when the mouse enters the picturebox, we just hide it.
Cursor.Hide();
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
var pictureBox = (PictureBox)sender;
// on a mouse move, save the current location (to be used when drawing the crosshair)
_mousePos = e.Location;
// force an update to the picturebox.
pictureBox.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
// if the mousepos is assigned (meaning we have a mouse pos, draw the crosshair)
if (_mousePos.HasValue)
{
var pictureBox = (PictureBox)sender;
// draw a vertical line
e.Graphics.DrawLine(_pen, new Point(_mousePos.Value.X, 0), new Point(_mousePos.Value.X, pictureBox.Height));
// draw a horizontal line
e.Graphics.DrawLine(_pen, new Point(0, _mousePos.Value.Y), new Point(pictureBox.Width, _mousePos.Value.Y));
}
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
// when the mouse is outside the picturebox, clear the mousepos
_mousePos = null;
// repaint the picturebox
pictureBox1.Invalidate();
// show the mouse cursor again.
Cursor.Show();
}
}
}
Because the events are using the sender, you can link multiple pictureboxes to these events.
It's also possible to inherit from the PictureBox, and write a new CrosshairPictureBox control, which has a crosshair by default.
If you want to draw charts in a PictureBox, use a Bitmap and draw on that using the Graphics.FromImage(bitmap) and put it in the PictureBox.Image. Don't forget to dispose the Graphics object.
You can achieve this by storing the position of the last point received, and then draw a line using the Graphics.DrawLine method between the old position and the new one.
Please also note, that when the mouse is moving, the Control.MouseMove event for every single pixel traveled by the mouse pointer isn't received for every single move. You do receive the Control.MouseMove events at a fairly consistent time interval. That means that the faster the mouse moves, the further apart the points you'll be actually receiving.
Check out this walkthrough for some examples - https://www.c-sharpcorner.com/UploadFile/mahesh/drawing-lines-in-gdi/
If I understand the question correctly, you are interested to draw x-axis and y-axis for a chart, but not using a chat control.
In this case, what you need to do is: Handle the Paint event of the PictureBox and draw the line from top middle to bottom middle and from left middle to right middle.
Here is the code which I write to produce above chart, y = Sin(x)
:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
var axisWidth = 3;
var axisColor = Color.Red;
var chartLineWidth = 2;
var chartLineColor = Color.Blue;
var scale = 90;
var gridSize = 45;
var gridLineWidth = 1;
var gridLineColor = Color.LightGray;
var g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
var w = pictureBox1.ClientRectangle.Width / 2;
var h = pictureBox1.ClientRectangle.Height / 2;
g.TranslateTransform(w, h);
g.ScaleTransform(1, -1);
//Draw grid
for (int i = -w / gridSize; i <= w / gridSize; i++)
using (var axisPen = new Pen(gridLineColor, gridLineWidth))
g.DrawLine(axisPen, i * gridSize, -h, i * gridSize, h);
for (int i = -h / gridSize; i <= h / gridSize; i++)
using (var axisPen = new Pen(gridLineColor, gridLineWidth))
g.DrawLine(axisPen, -w, i * gridSize, w, i * gridSize);
//Draw axis
using (var axisPen = new Pen(axisColor, axisWidth))
{
g.DrawLine(axisPen, -w, 0, w, 0); //X-Asxis
g.DrawLine(axisPen, 0, -h, 0, h); //Y-Asxis
}
//Draw y = Sin(x)
var points = new List<PointF>();
for (var x = -w; x < w; x++)
{
var y = System.Math.Sin(x * Math.PI / 180);
points.Add(new PointF(x, scale * (float)y));
}
using (var chartLinePen = new Pen(chartLineColor, chartLineWidth))
{
g.DrawCurve(chartLinePen, points.ToArray());
}
g.ResetTransform();
}
You also need the following piece of code to handle resizing of the picture box:
private void MyForm_Load(object sender, EventArgs e)
{
this.pictureBox1.GetType().GetProperty("ResizeRedraw",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance).SetValue(
this.pictureBox1, true);
}
You can also add a crosshair and rubber-band rectangle to the control, like the following image:

Placing buffered matrix rotated graphic on a fixed image

I am trying to put a movable needle (pointer) on a fixed graphic of a gauge (meter). The needle is moved by using a matrix rotate on a buffered graphics. I can get the fixed graphic and the needle to show. But when I render to the screen the last placed image deletes the prior graphic. I am using a timer to get the needle animation and a track bar input to produce the movement. The needle does the exact movement I am looking for.
I just cannot get the fixed background and needle to appear at the same time.
Any ideas?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Dial01
{
public partial class dial01Form : Form
{
// Establishes timer for graphics animation
private Timer timer01 = new Timer();
/* Establishes a graphic buffer to write to
* prior display on screen */
private Graphics myGraphics;
private BufferedGraphics myBufferedGraphics1;
// Establishes manager for embedded resources (Images)
private System.Resources.ResourceManager myRM = new
System.Resources.ResourceManager("Resources.resx",
System.Reflection.Assembly.GetExecutingAssembly());
int y = 0; // Rotation value
Graphics g,g1; // Graphics objects
public dial01Form()
{
// Establishes size of Dial01Form
this.Width = 500;
this.Height = 500;
// Gets reference to the current BufferedGraphicsContext
BufferedGraphicsContext myContext1 = BufferedGraphicsManager.Current;
// Specifically sets maximum buffer size
myContext1.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
// Sets the buffer size
myBufferedGraphics1 = myContext1.Allocate(this.CreateGraphics(),
new Rectangle(0, 0, this.Width, this.Height));
// Actvates timer and sets interval
timer01.Enabled = true;
timer01.Tick += onTimer;
timer01.Interval = 20;
timer01.Start();
// Initializes form components
InitializeComponent();
}
private void onTimer(object sender, System.EventArgs e)
{
myGraphics = this.CreateGraphics();
// Initializes graphics buffer variable
g1 = myBufferedGraphics1.Graphics;
// Clears graphic buffer with a color
g1.Clear(SystemColors.Control);
// Initializes an image variable for Dial Outline
Image dial01Outline = Dial01.Properties.Resources.DialOutline250x250;
// Draw Dial Outline to graphics buffer
myGraphics.DrawImage(dial01Outline, (ClientSize.Width / 2) - 100,
(ClientSize.Height / 2) - 100);
// Goto drawPointer method passing trackBar1 value
drawPointer(trackBar1.Value);
// Render buffered graphics to screen
// myBufferedGraphics.Render(Graphics.FromHwnd(this.Handle));
myBufferedGraphics1.Render();
}
public int drawPointer(int trkBarValue)
{
int x = trkBarValue;
y = 0;
if (225 + x <= 360) { y = 222 + x; }
else if (225 + x > 360) { y = x - 135; }
// These two labels are for testing purposes
label1.Text = ("Trk Bar Val = " + x).ToString();
label2.Text = ("Ptr value = " + y).ToString();
y = y + 180;
// Matrix rotation to pointer
Matrix myMatrix = new Matrix();
myMatrix.Rotate(y, MatrixOrder.Append);
myMatrix.Translate(this.ClientSize.Width / 2,
this.ClientSize.Height / 2, MatrixOrder.Append);
g1.Transform = myMatrix;
// Pointer polygon
PointF point1 = new PointF(0.0F, 0.0F);
PointF point2 = new PointF(0.0F, 50.0F);
PointF point3 = new PointF(3.0F, 55.0F);
PointF point4 = new PointF(7.0F, 50.0F);
PointF point5 = new PointF(7.0F, 0.0F);
PointF[] polyPoints =
{
point1,
point2,
point3,
point4,
point5
};
g1.FillPolygon(Brushes.Black, polyPoints);
return y;
}
private void dial01Form_Load(object sender, EventArgs e)
{
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
}
}
}
The general graphics approach you've taken is not appropriate for a winforms app.
The way graphics works in winforms, whenever the form is covered/uncovered/resized/etc, Windows tells it to repaint itself. Anything you've done with CreateGraphics will be overwritten at this point. This is why you shouldn't call CreateGraphics.
Instead, you should intercept the repainting process via the Paint event, and do all your custom painting there. You can still repaint on a timer, you just call Invalidate() inside the timer, which causes the form to repaint as soon as it can.
This is the general shape of the "right way" to do it:
public partial class dial01Form : Form
{
private Timer timer01 = new Timer();
int y = 0; // Rotation value
public dial01Form()
{
// Establishes size of Dial01Form
this.Width = 500;
this.Height = 500;
// Actvates timer and sets interval
timer01.Enabled = true;
timer01.Tick += onTimer;
timer01.Interval = 20;
timer01.Start();
// handle the paint event
this.Paint += OnPaint;
// Initializes form components
InitializeComponent();
}
private void OnPaint(object sender, PaintEventArgs e)
{
// all painting here, targeting e.Graphics
e.Graphics.Clear(SystemColors.Control);
Image dial01Outline = Dial01.Properties.Resources.DialOutline250x250;
e.Graphics.DrawImage(dial01Outline, (ClientSize.Width / 2) - 100,
(ClientSize.Height / 2) - 100);
drawPointer(e.Graphics, trackBar1.Value);
}
private void onTimer(object sender, System.EventArgs e)
{
this.Invalidate();
}
public int drawPointer(Graphics g1, int trkBarValue)
{
// elided: same code as before, but using the g1 parameter instead of a field
}
}
You shouldn't have problems with flickering, I think - double-buffering is enabled by default. Make sure your form's DoubleBuffered property is set to True though.

Drawing a new circle bitmap at the click location while preserving previously drawn circles

I am trying to draw circles using Bitmap.
Each time I click the mouse, the circle I previously drew is moved to the new position.
What I want to happen is: Each time I click the mouse, a new circle is created/drawn at the position I clicked and all previously drawn circles remain without moving.
I am working with the following code:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace multirectangle
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
}
Bitmap background;
Graphics scG;
Rectangle rectangleObj;
private Point clickCurrent = Point.Empty;
private Point clickPrev = Point.Empty;
private void Form1_Load(object sender, EventArgs e)
{
background = new Bitmap(Width, Height);
rectangleObj = new Rectangle(10, 10, 30, 30);
scG = Graphics.FromImage(background);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
if (clickPrev == Point.Empty) return;
rectangleObj.X = clickPrev.X - rectangleObj.Height / 2;// +radius;
rectangleObj.Y = clickPrev.Y - rectangleObj.Width / 2;
Refresh();
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.DrawImage(Draw(), 0, 0);
}
public Bitmap Draw()
{
Graphics scG = Graphics.FromImage(background);
Pen myPen = new Pen(System.Drawing.Color.Red, 3);
scG.Clear(SystemColors.Control);
scG.DrawEllipse(myPen, rectangleObj);
return background;
}
}
}
Your English was a little confusing. If I'm understanding your problem correctly, right now the only thing that's being drawn is the new circle where the click was, and you want all the old ones to persist as well? In which case, there are two options:
Don't clear the bitmap before you draw. scG.Clear(SystemColors.Control); will clear the bitmap you just drew. If you remove that line and don't clear the bitmap, then the next time you click, it will then draw the new ellipse right on top of the last bitmap.
If you want a fresh drawing/bitmap everytime, you would need a list of your rectangleObj . Each time you click, you add that point to your rectangleObj collection. Then in your draw method, you would iterate through the collection and draw all of them.
I notice a few things. First, in Form1_MouseDown(), you have this:
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
You are overwriting the old position (clickPrev) before you even save it. If you want to keep both positions, you should put them in a simple structure, like a List. When you get a new point, just Add() it to the list. Then, in your Draw() routine, loop over all the elements in the list and draw them all.
If you just want two positions--and only two--just swap your statements like this:
clickPrev = clickCurrent;
clickCurrent = PointToClient(Cursor.Position);
And you'll have to allocate another rectangle object for the drawing, although it would make more sense to take care of this in the Draw() routine.
Swap the position of the following statements
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
I think you are assigning the clickCurrent to clickPrevious after you initialize clickCurrent. It needs to be the other way.
Please try this
Rectangle rectangleObj;
Bitmap background;
Graphics scG;
Pen myPen;
private void Form1_Load(object sender, EventArgs e)
{
rectangleObj = new Rectangle(10, 10, 30, 30);
background = new Bitmap(Width, Height);
scG = Graphics.FromImage(background);
myPen = new Pen(Color.Red, 3);
BackgroundImage = background;
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
var point = PointToClient(Cursor.Position);
rectangleObj.X = point.X - rectangleObj.Height / 2;
rectangleObj.Y = point.Y - rectangleObj.Width / 2;
scG.DrawEllipse(myPen, rectangleObj);
Refresh();
}
OnPaint and Draw methods removed. As well as clickCurrent and clickPrev fields.
When you change the form size (for example, maximize it), Bitmap and Graphics remain the same, so you get this effect. To avoid this, you need to add the event handler
private void Form1_SizeChanged(object sender, EventArgs e)
{
background = new Bitmap(Width, Height);
scG = Graphics.FromImage(background);
BackgroundImage = background;
}
Note that each time you resize the form, all previously drawn is erased. If this is undesirable, a different approach is needed for drawing. Let me know, I will give another example.

How to keep two colors circles together

The goal of this little program I am working on is to draw a red circle while radiobutton1 is checked, and draw a black circle while radiobutton2 is checked.
Below is my code, the problem with this code is when radiobutton1 is checked, it does draw red circles, but then if I click radiobutton2, then all the red circles will turn black. Then if check radiobutton1 again, the all the dots will turn red again.
How do I keep both color circles on the panel?
List<Point> points = new List<Point>();
Graphics g;
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
points.Add(e.Location);
panel1.Invalidate();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
int count = 0;
if (radioButton1.Checked)
{
g = e.Graphics;
while (count < points.Count())
{
g.FillEllipse(Brushes.Red, points[count].X, points[count].Y, 10, 10);
count++;
}
}
else if (radioButton2.Checked)
{
g = e.Graphics;
while (count < points.Count())
{
g.FillEllipse(Brushes.Black, points[count].X, points[count].Y, 10, 10);
count++;
}
}
}
The way graphics works in Windows Forms is, the Paint method redraws the entire panel.
You are already drawing every point every time the paint method executes: this is correct.
But you have no way of remembering which point is supposed to be which color, so all you have to go on when you do the painting is the current values of the radiobuttons. You need some way of recording the current color when you add a circle.
One way to do this would be to define a Circle class which stores the location and color of a circle:
class Circle
{
public Point Location { get; set; }
public Brush Fill { get; set; }
}
Then instead of points being a List<Point>, it can be a List<Circle>, and when you see a mouse click, you can add a new Circle instead of a Point:
var circle = new Circle()
{
Location = e.Location,
Fill = radioButton1.Checked ? Brushes.Red : Brushes.Black
};
points.Add(circle);
And when you do the painting, you can check each circle's color as you draw them - all you have to do is this:
foreach (var circle in points)
{
e.Graphics.FillEllipse(circle.Fill, circle.Location.X, circle.Location.Y, 10, 10);
}
Note that you do not need a member level Graphics g - and it's a bad idea to keep a Graphics object after the Paint method has finished. It will not necessarily still be valid later. Always just use e.Graphics.
I also replaced your while loop with a simpler foreach.
If I understand your intent correctly, you need to keep two lists of points, one for each color. Then, when you click somewhere, put the clicked point in the appropriate list (red or black). Then, in your Paint event handler, replace the conditional code with two loops, one through each list of points (drawing the points from the red list in red and the points from the black list in black).
Code:
List<Point> redPoints = new List<Point>();
List<Point> blackPoints = new List<Point>();
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
if (radioButton1.Checked)
redPoints.Add(e.Location);
else
blackPoints.Add(e.Location);
panel1.Invalidate();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
int count = 0;
Graphics g = e.Graphics;
foreach (Point p in redPoints)
{
g.FillEllipse(Brushes.Red, p.X, p.Y, 10, 10);
}
foreach (Point p in blackPoints)
{
g.FillEllipse(Brushes.Black, p.X, p.Y, 10, 10);
}
}
Note: if your circles overlap one another and you care about maintaining the layering order (first-clicked circles drawing first), then #Blorgbeard's solution is better because it keeps all the circles in the same list, thus maintaining the original layering. Feel free to switch the accepted answer.

Picture box filling with some percentages

I am very new to C sharp programming. I have the design like as shown below attached image.
My concept is i have to set some volume in "Transfer volume" text box (for example 100) and then press "set" button. It automatically sets the scale of picture box, it is working fine.
Now i want to fill the picture box with colors when i click "Regenerate" button. The percentage of color to be filled in picture box should be the number in the TextBox of that regarding color or liquid.
for example
if i set GasPhase =5 ; Hydrocarbon Liquid =5; Water= 5; Oil Based Mud =5; Water Based Mud =5 ; Not Identified = 75.
then the picture has to fill with 75 % with "Not Identified" color and GasPhase color with 5 % e.t.c.
I have the written some code as shown below.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void txtTransferVolume_TextChanged(object sender, EventArgs e)
{
}
private void txtTransferSet_Click(object sender, EventArgs e)
{
string value = txtTransferVolume.Text;
double midvalue = Convert.ToDouble(value);
lblTransferBottleMax.Text = value;
lblTransferBottleMid.Text = (midvalue / 2).ToString();
}
private void chkTransferManual_CheckedChanged(object sender, EventArgs e)
{
}
private void btnTransferBottleRegenerate_Click(object sender, EventArgs e)
{
}
}
}
Please help me how to fill as i want.
This can be achieved quite easily by drawing directly on the picturebox control or creating a bitmap in memory and show in the picturebox.
Example:
private void DrawPercentages(int[] percentages, Color[] colorsToUse)
{
// Create a Graphics object to draw on the picturebox
Graphics G = pictureBox1.CreateGraphics();
// Calculate the number of pixels per 1 percent
float pixelsPerPercent = pictureBox1.Height / 100f;
// Keep track of the height at which to start drawing (starting from the bottom going up)
int drawHeight = pictureBox1.Height;
// Loop through all percentages and draw a rectangle for each
for (int i = 0; i < percentages.Length; i++)
{
// Create a brush with the current color
SolidBrush brush = new SolidBrush(colorsToUse[i]);
// Update the height at which the next rectangle is drawn.
drawHeight -= (int)(pixelsPerPercent * percentages[i]);
// Draw a filled rectangle
G.FillRectangle(brush, 0, drawHeight, pictureBox1.Width, pixelsPerPercent * percentages[i]);
}
}
Of course you have to check that both arrays are the same length, etc. I just want to give you the basic idea of how to do this.
Here's a concept of how to get your data in arrays and pass them to the function. Since you are using a different textbox for each value, it's hard to iterate over them, so right now I'm showing you how to do it using the 6 values you have now.
private void btnTransferBottleRegenerate_Click(object sender, EventArgs e)
{
int[] percentages = new int[6];
percentages[0] = int.Parse(txtTransferNotIdentified.Text);
percentages[1] = int.Parse(txtTransferWater.Text);
// And so on for every textbox
Color[] colors = new Color[6];
colors[0] = Color.Red;
colors[1] = Color.Yellow;
// And so on for every color
// Finally, call the method in my example above
DrawPercentages(percentages, colors);
}
If your percentages don't always sum to 100, you could use a third parameter that specifies the sum and change the value 100f to this value in the DrawPercentages method.

Categories

Resources