Drawing in two pictureBoxes - c#

I created two pictureBoxes on my windows form application. I want to draw in both of them, using FillEllipse(), but for some reason I can only draw in the first pictureBox. I belive the problem is that I don't understand properly how events work. Here is the code:
public Form1()
{
InitializeComponent();
pictureBox1.Paint += new PaintEventHandler(this.pictureBox1_Paint);
pictureBox2.Paint += new PaintEventHandler(this.pictureBox2_Paint);
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics gr = e.Graphics;
Point p1 = pictureBox1.Location;
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 40, p1.Y + 40, 20, 20));
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 40, p1.Y + 80, 20, 20));
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 80, p1.Y + 40, 20, 20));
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 80, p1.Y + 80, 20, 20));
}
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
Graphics br = e.Graphics;
Point p2 = pictureBox2.Location;
br.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p2.X + 40, p2.Y + 40, 20, 20));
br.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p2.X + 40, p2.Y + 80, 20, 20));
br.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p2.X + 80, p2.Y + 40, 20, 20));
br.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p2.X + 80, p2.Y + 80, 20, 20));
}

You are drawing in pictureBox2. Your problem is that you are drawing outside of the viewport, because you are drawing at pictureBox2's location on the form, but within the picturebox. pictureBox2.Location gives to position of the box on the form. If this box is at position x=240, y=240, you are drawing INSIDE the box at those positions. If your box is only w=50, h=50, you will not see what you are drawing because it's waaaay to the right and bottom

So, this is really inefficient you're drawing everything twice resulting in twice the load. What you need to do is draw once into a bitmap and load the bitmap into the two picture boxes.
https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-create-a-bitmap-at-run-time
However, if working with bitmaps won't allow you to achieve whatever it is you're trying to do, write a custom control. While this wont improve performance all that much it will at least stop code duplication.
https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/creating-a-wf-control-design-time-features
Bitmap img= new Bitmap(100, 100);
Point p1 = pictureBox1.Location;
Graphics gr = Graphics.FromImage(img);
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 40, p1.Y + 40, 20, 20));
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 40, p1.Y + 80, 20, 20));
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 80, p1.Y + 40, 20, 20));
picturebox1.Image = img;
picturebox2.Image = img;

The PaintEventArgs e.Graphics gives you a control's DeviceContext (consider it a reference to the graphics surface of a control) and a set of tools that can be used to perform graphics operations on that context.
When using e.Graphics, all drawings occur inside the underlying surface the graphics object references (a PictureBox ClipRectangle in this case). All coordinates are related to this area.
Thus, you just need to specify the position and size of a drawing using the control's Client Area as the only reference. The drawing area dimensions are also reported by the e.Graphics.ClipBounds (expressed in PageUnits) and the control ClientRectangle property (expressed in Pixels).
The ClientRectangle is the area of a control which excludes non-client elements such as Borders, Menus, ScrollBars, TitleBar etc.; it's "inner-body".
The client area definition can, however, change in relation to a control's internal structure (imagine a ListView or a ComboBox control, for example) .
The Paint() event of a control is raised each time a control needs to repaint itself.
It's always raised after a control is first created.
After that, it could be triggered when, for example, a control is for some reasons "obscured" by another control/window or when the Form that contains it is minimized.
It can be raised "manually" invoking the control's Invalidate() method.
This is probably the prefered way to force a control to repaint itself, because the Invalidate() method allows to specify a defined portion (a Rectangle or a Region) of a control that needs repainting, limiting the painting to this area.
You could modify your code this way:
public Form1()
{
InitializeComponent();
pictureBox1.Paint += new PaintEventHandler(this.pictureBox1_Paint);
pictureBox2.Paint += new PaintEventHandler(this.pictureBox2_Paint);
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(40, 40, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(40, 80, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(80, 40, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(80, 80, 20, 20));
}
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(40, 40, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(40, 80, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(80, 40, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(80, 80, 20, 20));
}
SmoothingMode = SmoothingMode.AntiAlias is used to "prettify" the graphics results.
It which will generate smoother borders.
The Drawing Brush used is a stock object (system-provided) which doesn't need to be Disposed(). If you create a Brush using one of the brush related classes, the object you create must be disposed.
using (SolidBrush brush = new SolidBrush(Color.Red))
{
e.Graphics.FillEllipse(brush, new Rectangle(40, 40, 20, 20));
}

Related

Is it posble create text on image with higher quality in .net

I need to create some kind of graph in .net desktop application.
I use .Net Framework 4.7.2 and System.Drawing library.
The scenario looked like this.
In the console application I create a bitmap with X and Y axes and some simple lines and some text on those lines, nothing complicated.
After creating the bitmap, I save it as .bmp file.
The problem is that the letters and numbers I create are bad and look furry, I tried with different fonts and different settings but the result was always the same.
The picture looks like this:
and under a magnifyer it looks like this:
This is my sample code:
// create new bitmap
Bitmap bitmap = new Bitmap(900, 700, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
Graphics g = Graphics.FromImage(bitmap);
g.Clear(Color.LightGray);
// set attr
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.Bicubic;
// draw X and Y axes
Pen pen = new Pen(Color.Black, 1);
g.DrawLine(pen, new Point(100, 100), new Point(100,600));
g.DrawLine(pen, new Point(100, 600), new Point(800, 600));
SolidBrush brush = new SolidBrush(Color.Black);
Font font = new Font("Lucida Console", 14, FontStyle.Bold);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.Bicubic;
// grid numbeers on x axis
for (int i = 0; i < 8; i++)
{
g.DrawString(i.ToString(), font, brush, 100 * (i+1), 620);
}
// grid numbeers on y axis
for (int i = 0; i < 6; i++)
{
g.DrawString(i.ToString(), font, brush, 70, 600 - (100 * i));
}
// label 1
font = new Font("Lucida Console", 14, FontStyle.Bold);
g.DrawString("Line - X axis", font, brush, 100, 650);
// label 2
font = new Font("Lucida Console", 32, FontStyle.Bold);
g.DrawString("PORCO El.01", font, brush, 550, 150);
// label 3
font = new Font("Lucida Console", 24, FontStyle.Regular);
g.DrawString("PORCO El.01", font, brush, 550, 200);
// save as .bmp file
bitmap.Save(Path.Combine(picturePath, "sample.bmp"));
Is it possible to get better text quality? Is there maybe another 3rd party library that can achieve better results?
Thanks in advance!

C# - Drawing a Rounded Rectangle on a panel

I am currently trying to draw a gradient filled rounded rectangle onto a panel bar in a form.
From some research I found some code that allowed me to create a custom rectangle :
static class CustomRectangle
{
public static GraphicsPath RoundedRect(Rectangle bounds, int radius)
{
int diameter = radius * 2;
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = bounds.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = bounds.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = bounds.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
public static void FillRoundedRectangle(this Graphics graphics, Brush brush, Rectangle bounds, int cornerRadius)
{
if (graphics == null)
throw new ArgumentNullException("graphics");
if (brush == null)
throw new ArgumentNullException("brush");
using (GraphicsPath path = RoundedRect(bounds, cornerRadius))
{
graphics.FillPath(brush, path);
}
}
Credit to this page
Next using this custom rectangle, I have tried using the paint method for the bar panel.
private void quickMenuBar_Paint(object sender, PaintEventArgs e)
{
LinearGradientBrush myBrush = new LinearGradientBrush(new Point(20, 20), new Point(120, 520), Color.DarkBlue, Color.RoyalBlue);
System.Drawing.Graphics formGraphics = this.CreateGraphics();
CustomRectangle.FillRoundedRectangle(formGraphics, myBrush, new System.Drawing.Rectangle(20, 20, 100, 500), 25);
myBrush.Dispose();
formGraphics.Dispose();
}
But after executing this code, it only prints a rounded rectangle directly onto the form and behind the bar panel.
I have other methods that fill a panel with a standard rectangle using PaintEventArgs e:
e.Graphics.FillRectangle(myBrush , otherBar.ClientRectangle);
So obviously I need to use PaintEventArgs e in my custom rectangle method, but I do not know how or where to.
If there are better ways than this way of drawing a rounded rectnagles, please share.
You generally should never be using CreateGraphics(). Simply remove this line:
System.Drawing.Graphics formGraphics = this.CreateGraphics();
And use e.Graphics where you would previously use formGraphics. The Paint event is basically asking you to "paint something for me, here's the graphics object to paint on".
Since you're already providing a Graphics object instance to your rounded rectangle method, no changes are required there.

Using trackbars to display and distort a cross section

The question asked to calculate the volume of a swimming pool and display its cross section in a picture box. Width of the pool is fixed at 5 meters and the length is fixed at 20 meters.
The program should have 2 trackbars - one to adjust the depth of the deep end and one to adjust the depth of the shallow end. The minimum depth of each end is one meter, choose suitable maximum and minimum track bar values at design time.
Volume = averageDepth * width * length
The trackbar for the deep end of the pool adjusts the depth perfectly.
The trackbar for the shallow end changes the cross section and does not diplay one of the lines when used.
Could someone please help me correct this and figure it out?
Heres the code:
public partial class Form1 : Form
{
private Graphics paper;
private int averageDepth;
private int answer;
public Form1()
{
InitializeComponent();
paper = pictureBox1.CreateGraphics();
deepEndTrackbar.Minimum = 120;
deepEndTrackbar.Maximum = 180;
deepEndLabel.Text = Convert.ToString(deepEndTrackbar.Value);
shallowEndTrackbar.Minimum = 120;
shallowEndTrackbar.Maximum = 180;
shallowEndLabel.Text = Convert.ToString(shallowEndTrackbar.Value);
}
private void button1_Click(object sender, EventArgs e)
{
Graphics paper;
paper = pictureBox1.CreateGraphics();
Pen pen = new Pen(Color.Black);
paper.DrawLine(pen, 40, 50, 200, 50);
paper.DrawLine(pen, 40, 50, 40, 120);
paper.DrawLine(pen, 200, 50, 200, 90);
paper.DrawLine(pen, 200, 90, 40, 120);
}
private void deepEndTrackbar_Scroll(object sender, EventArgs e)
{
Pen pen = new Pen(Color.Black);
deepEndLabel.Text = Convert.ToString(deepEndTrackbar.Value);
paper.Clear(Color.White);
paper.DrawLine(pen, 40, 50, 200, 50);
paper.DrawLine(pen, 200, 50, 200, 90);
paper.DrawLine(pen, 40, 50, 40, deepEndTrackbar.Value);
paper.DrawLine(pen, 200, 90, 40, deepEndTrackbar.Value);
}
private void shallowEndTrackbar_Scroll(object sender, EventArgs e)
{
Pen pen = new Pen(Color.Black);
shallowEndLabel.Text = Convert.ToString(shallowEndTrackbar.Value);
paper.Clear(Color.White);
paper.DrawLine(pen, 40, 50, 200, shallowEndTrackbar.Value);
paper.DrawLine(pen, 200, 50, 200, shallowEndTrackbar.Value);
}
The deepEndTrackbar_Scroll routine seems to work. But it doesn't really as it does not use the value of the shallowEndTrackbar. In fact both routines should draw the very same lines:
paper.DrawLine(pen, 40, 50, 200, 50);
paper.DrawLine(pen, 200, 50, 200, shallowEndTrackbar.Value);
paper.DrawLine(pen, 40, 50, 40, deepEndTrackbar.Value);
paper.DrawLine(pen, 200, shallowEndTrackbar.Value, 40, deepEndTrackbar.Value);
This is the bare minimum correction.
The next step would be to have this code only once. The direct solution would be to call the same event for both Trackbars.
However all these things are only working on the surface. Minimize the window, restore and look: All Lines are gone! You need to persist the drawing!
The right place for the code is the Paint event of the PictureBox. You could override it, but it is simpler to just code it: Doubleclick it in the properties tab and insert the code.
Prefix this line to the code:
paper = e.Graphics;
And replace all drawing in the two TrackBar scroll events for this:
pictureBox1.Invalidate();
Another error is with initializing the Trackbar. Judging from the initial display one value (90) is not in the allowed range (120-180).
You also don't need the lines
private Graphics paper;
and
paper = pictureBox1.CreateGraphics();
Edit: As requested here is a complete solution.
Note: I have added a LengthTrackbar and moved the drawing down by 100 pixels:
public partial class Form1 : Form
{
Pen pen = Pens.Black;
//private int averageDepth;
//private int answer;
public Form1 ()
{
InitializeComponent();
deepEndTrackbar.Minimum = 120;
deepEndTrackbar.Maximum = 180;
deepEndLabel.Text = Convert.ToString(deepEndTrackbar.Value);
shallowEndTrackbar.Minimum = 120;
shallowEndTrackbar.Maximum = 180;
shallowEndLabel.Text = Convert.ToString(shallowEndTrackbar.Value);
lengthTrackbar.Minimum = 120;
lengthTrackbar.Maximum = 180;
lengthLabel.Text = Convert.ToString(lengthTrackbar.Value);
}
private void button1_Click(object sender, EventArgs e)
{
// reset the trackbar values:
shallowEndTrackbar.Value = 120;
deepEndTrackbar.Value = 120;
lengthTrackbar.Value = 120;
pictureBox1.Invalidate();
}
private void deepEndTrackbar_Scroll(object sender, EventArgs e)
{
deepEndLabel.Text = Convert.ToString(deepEndTrackbar.Value);
pictureBox1.Invalidate();
}
private void shallowEndTrackbar_Scroll(object sender, EventArgs e)
{
shallowEndLabel.Text = Convert.ToString(shallowEndTrackbar.Value);
pictureBox1.Invalidate();
}
private void lengthTrackbar_Scroll(object sender, EventArgs e)
{
lengthLabel.Text = Convert.ToString(lengthTrackbar.Value);
pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics paper = e.Graphics;
// reset the paper
paper.Clear(Color.White);
// draw the 2D front lines:
paper.DrawLine(pen, 40, 150, 200, 150);
paper.DrawLine(pen, 200, 150, 200, 100 + shallowEndTrackbar.Value);
paper.DrawLine(pen, 40, 150, 40, 100 + deepEndTrackbar.Value);
paper.DrawLine(pen, 200, 100 + shallowEndTrackbar.Value, 40,
100 + deepEndTrackbar.Value);
// perspective 2:3
int lx = lengthTrackbar.Value / 2;
int ly = lengthTrackbar.Value / 3;
// draw the outer 3D lines:
paper.DrawLine(pen, 40, 150, 40 + lx, 150 - ly);
paper.DrawLine(pen, 200, 150, 200 + lx, 150 - ly);
paper.DrawLine(pen, 200, 100 + shallowEndTrackbar.Value,
200 + lx, 100 - ly + shallowEndTrackbar.Value);
paper.DrawLine(pen, 40 + lx, 150 - ly, 40 + lx + 200 - 40, 150 - ly);
paper.DrawLine(pen, 200 + lx, 150 - ly,
200 + lx, 100 -ly + shallowEndTrackbar.Value);
}
}

Strange DrawRectangle and DrawLine

I want to draw several figures on the PictureBox. I expected that the following code will draw a rectangle with it's full size diagonal, but it doesn't. Line do not connected with bottom-right corner of rectangle. I'm really curious what may be wrong?
private void onPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen p = new Pen(System.Drawing.Color.Black, 2);
g.DrawRectangle(p, 50, 10, 400, 400);
g.DrawLine(p, 50, 10, 400, 400);
}
In DrawRectangle, the last two arguments are width and height. In DrawLine, the last two arguments are final x and final y.
So just add the starting x and starting y to the width and height to get your diagonal line:
g.DrawRectangle(p, 50, 10, 400, 400);
g.DrawLine(p, 50, 10, 450, 410);
Also, if you declare a Rectangle then you can change the values without needing to change the drawing code. Something like:
private void onPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle rc = new Rectangle(50, 10, 400, 400);
using (Pen p = new Pen(System.Drawing.Color.Black, 2))
{
g.DrawRectangle(p, rc);
g.DrawLine(p, rc.Left, rc.Top, rc.Right, rc.Bottom);
}
}

How to draw with replacement instead of blending

I'm trying to "draw" areas of transparency onto a bitmap -- like cutting holes in the image.
The following code does not draw a line of transparency because drawing a transparent line onto a bitmap of course blends instead of replaces. (Why the default is to do the more complicated of the two drawing operations, makes no sense.)
Bitmap myBitmap = new Bitmap(50, 50);
Graphics g = Graphics.FromImage(myBitmap);
g.FillRectangle(Brushes.Black, 0, 0, 50, 50);
g.FillEllipse(Brushes.Transparent, 25, 0, 25, 25); //Does nothing
g.DrawLine(Pens.Transparent, 0, 0, 50, 50); //Does nothing
How would I modify this so that a transparent circle and line replace what's in the bitmap instead of blending?
(Note that this is the trivial case of "drawing" complete transparency. The end I'm going toward is the ability to "draw" modifying the alpha channel only without creating my own pixel by pixel operation. Being able to do complete transparency will suffice though.)
Following answer in article suggested as a duplicate, I've also tried the following (which does not work)
base.OnPaint(e);
Bitmap myBitmap = new Bitmap(50, 50);
e.Graphics.FillRectangle(Brushes.Black, 0, 0, 50, 50);
Graphics g = Graphics.FromImage(myBitmap);
g.FillEllipse(new SolidBrush(Color.FromArgb(150, 125, 125, 125)), 25, 0, 25, 25);
g.DrawLine(new Pen(Color.FromArgb(150,25,25,25)), 0, 0, 50, 50);
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
e.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
e.Graphics.DrawImage(myBitmap, 0, 0);
also tested this with SourceCopy
This works totally fine for me
Bitmap bmp = new Bitmap(50, 50, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (Graphics g = Graphics.FromImage(bmp))
{
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
g.FillRectangle(Brushes.Black, 0, 0, 50, 50);
g.FillEllipse(Brushes.Transparent, 25, 0, 25, 25);
g.DrawLine(Pens.Transparent, 0, 0, 50, 50);
g.Flush();
}
bmp.Save("Test.bmp");

Categories

Resources