0

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));
    }
  • `PictureBox` is meant to hold a bitmap. It is not meant to draw on the control directly. – Nyerguds May 22 '18 at 10:50
  • You can draw onto a PictureBox withoput any problems and as it is double-buffered this not just is a perfectly fine method but in fact meant to be used that way.. – TaW May 22 '18 at 11:25

3 Answers3

2

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

bixarrio
  • 373
  • 1
  • 10
0

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;
garty
  • 406
  • 3
  • 11
  • This is all wrong imo. You miss the proeblem and your advice is questionable at best, esp. since we do not really know enough about where OP want to go.. – TaW May 22 '18 at 11:23
  • Based on the information provided that is the best answer I could provide. As for the advice being "questionable" can you expand, please? – garty May 22 '18 at 11:45
0

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));
}
Jimi
  • 29,621
  • 8
  • 43
  • 61