Click here to Skip to main content
15,885,208 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi.
I have opened an image in the panel using openFileDialogue. It works fine and the background image is drawn into the panel ok, but the file selection window doesn't close and just keeps letting me select more files to put into the panel.
Thanks

C#
private void panel1_Paint(object sender, PaintEventArgs e)
{
    openFileDialog1.Filter = "JPEG IMAGES|*.jpg";
    openFileDialog1.InitialDirectory = "C:\\Users\\jason\\Documents\\IProject\\code\\imageAlign\\imageAlign\\bin\\Debug";
    openFileDialog1.Title = "Open Image";
    if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
   {
        panel1.BackgroundImage = Image.FromFile(openFileDialog1.FileName);
        Pen sb = new Pen(Color.Red,3);
        Graphics g = panel1.CreateGraphics();
        g.DrawRectangle(sb, 20, 20, 50, 50);
   }
}
Posted
Updated 23-Feb-13 22:32pm
v2
Comments
defunktlemon 24-Feb-13 4:45am    
Thanks OringinalGriff - I put it into a button click event and it worked fine.
But, unless I have a picturebox I can't see the drawn rectangle. Does this mean that the pen can't be used in a panel?
OriginalGriff 24-Feb-13 5:06am    
Answer updated

1 solution

Get that code out of the Paint event!
The paint event is called whenever the system needs to draw the panel.
So, the system calls Paint, which displays a dialog, which covers part of the panel.
You close the dialog, draw the rectangle and exit - at which point the system notices that the dialog position means the panel needs to be redrawn, so it calls Paint, which starts the whole thing off again!


"Thanks OringinalGriff - I put it into a button click event and it worked fine.
But, unless I have a picturebox I can't see the drawn rectangle. Does this mean that the pen can't be used in a panel?"


No, it means you are doing things wrong! :laugh:

What you need to do is split the code:
C#
openFileDialog1.Filter = "JPEG IMAGES|*.jpg";
openFileDialog1.InitialDirectory = "C:\\Users\\jason\\Documents\\IProject\\code\\imageAlign\\imageAlign\\bin\\Debug";
openFileDialog1.Title = "Open Image";
if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
    {
    panel1.BackgroundImage = Image.FromFile(openFileDialog1.FileName);
    }
goes in your button click handler, with the addition of a (probably unnecessary but it's a good idea)
C#
panel1.Invalidate();
This forces the system to redraw the panel, which causes the Paint event to happen.
In the Paint event you draw the rectangle, but with a couple of small changes from your code:
C#
using (Pen sb = new Pen(Color.Red,3))
   {
   e.Graphics.DrawRectangle(sb, 20, 20, 50, 50);
   }
You don't need to create a graphics object in a paint event - it is already there for you in the PaintEventArgs.
If you construct a Pen object, you should either make it a class level private variable, or Dispose of it yourself (the using block does it for you) as they (and Graphics contexts) are scarce resources which will run out a long time before the Garbage Collector gets in to dispose them for you.


"I'm excited now and would like to try and put this into a class :)
I have created a class


C#
class rectangle
{

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        using (Pen sb = new Pen(Color.Red, 3))
        {
            e.Graphics.DrawRectangle(sb, 20, 20, 50, 50);
        }


But don't know what to pass in the method call?"

C#
rectangle mc = new rectangle();


I'm not surprised you don't know what to pass - it's not quite what you want to do.

Take a step back, and think about it: why should a rectangle know about the panel it is contained in? Will it always be on a panel?

Of course it won't! It could be directly on a form, for example. But the idea was good - making the rectangle responsible for drawing itself is an excellent idea.
But first - Classes should begin with an Upper case letter, and there is already a Rectangle class in .NET, so lets call it a MyRectangle instead, yes?
C#
public class MyRectangle
    {
    public Rectangle Rect { get; set; }
    public MyRectangle(Point p, Size s)
        {
        Rect = new Rectangle(p, s);
        }

    public void Draw(Graphics g, Pen p)
        {
        g.DrawRectangle(p, Rect);
        }

Then, all you have to do is create instances and draw them:
C#
private List<MyRectangle> rects = new List<MyRectangle>();
Random r = new Random();
Pen pen = new Pen(Brushes.Red, 2);
private void butAddARectangle_Click(object sender, EventArgs e)
    {
    Point p = new Point(r.Next(10, 100), r.Next(10, 100));
    Size s = new Size(r.Next(10, 50), r.Next(10, 50));
    rects.Add(new MyRectangle(p, s));
    MyPanel.Invalidate();
    }

private void MyPanel_Paint(object sender, PaintEventArgs e)
    {
    foreach (MyRectangle mr in rects)
        {
        mr.Draw(e.Graphics, pen);
        }
    }
Add that code to your form, and hook up the two events.

Your MyRectangle class can now draw itself, when you hand it a context and a suitable pen.
It has a constructor which takes the minimum info it needs in order to exist.

So, every time you press the button, it creates a new rectangle, adds it to the list, and paints them all.
Try it! See what happens.


"wow! Now I'm really kazoonked! That is quite a complex piece of code for little ol' me. I have added the first part to the new class created, MyClass (with a capital beginning :))
And the second part you wrote to the 'public partial class Form1 : Form' page.
I need to 'hook them up'. Do you mean call the class - like 'MyRectangle mc = new MyRectangle();'?
Sorry - I'm newbie lemon.

When you say I need to had it context and a suitable pen, do you mean this:

C#
using (Pen sb = new Pen(Color.Red,3))
   {
   e.Graphics.DrawRectangle(sb, 20, 20, 50, 50);
   }



I wonder if I should explain what I am trying to do overall to be sure we are on the same page.
After I have opened the image in the background of the form I will scan different sections of it to sum up the pixel values in each section. If the pixel values in each section are above a certain threshold set then the rectangle should be drawn over / around that section highlighting it so that people would know it is above the threshold. Are we still on the right track OriginalGriff?"


Not call them, no - the first is a Button Click event handler method - you need to "hook it up" to a button, so that when you click it, the method gets called. Easy to do - if you have the method pasted into you form class, add a button in the designer and look at the button's Properties pane. Click the "Events" button in the pane (it looks like a little lightening bolt) and find the "Click" handler in the list. Open the dropdown to the right of the word "Click" and select the method from the list.

The second is Panel Paint event handler, so you can paste the code into your existing one, or hook the event the same way you did for the button above.

Are we still on the right track? Yes - it'll work fine - just add the appropriate rectangles to the list and it'll draw them where you want (and you can have more than one rectangle showing as well)

It looks lioke a lot of code, but it's pretty simple stuff really - it's just that you are new and it looks complicated. It's not, honest!

C#
public Rectangle Rect { get; set; }
This just creates a Property in the MyRectangle class which the outside world can interact with. The compiler takes care of the actual storage for you, so it's easy to create. If you think of it as a public variable in the class you will be fine for the moment (it isn't, it's a lot more powerful and useful than that but you'll come to them latter in your course) - for the moment, just accept that class variables should be private, and write them like this if you want the world outside the class to be able to access them.

C#
public MyRectangle(Point p, Size s)
    {
    Rect = new Rectangle(p, s);
    }
This is a constructor - it creates a new instance of the MyRetctangle class just like
C#
public MyRectangle(){}
would do, but it allows you to specify the data when you create it as parameters: so instead of using
C#
MyRectangle mr = new MyRectangle();
mr.Rect = new Rectangle(p, s);
you can do it in a single line. Since a rectangle with no location or size if pretty much useless it makes sense to only have a constructor that requires you to provide the parameters.

C#
public void Draw(Graphics g, Pen p)
    {
    g.DrawRectangle(p, Rect);
    }
Just a method in the MyRectangle class that can be called from outside it. All it does is draw the actual rectangle at it's current location and size. The outside world specifies where it is to draw via the Graphics context it passes in, and teh pen it is to draw whith via the other parameter.
C#
private List<MyRectangle> rects = new List<MyRectangle>();
Thos might be a bit more complicated... It declares a private variable called rects that can hold a number of MyRectangle objects. You don't have to tell it how many it is to hold, it doesn't care - it just remembers them for you. Again, you will come to these later (and probably use them a *LOT*) - if you know what an array is, think of it as an array where you don't need to say how big it is - it isn't anything like that, but it'll do for the moment!
C#
Random r = new Random();
Just sets things up so I can use a random number generator later, so my new rectangles aren't all the same...
C#
Pen pen = new Pen(Brushes.Red, 2);
Easy-peasy: It's just your line moves to a class level variable.
C#
private void butAddARectangle_Click(object sender, EventArgs e)
    {
    ...
    }
Button Click event handler - you have met these already, I think.
C#
Point p = new Point(r.Next(10, 100), r.Next(10, 100));
Size s = new Size(r.Next(10, 50), r.Next(10, 50));
Creates a new Point and Size object using random info. The r.Next bit says "give me the next random number from the "r" random number generator we created earlier" and the 10,100 just restricts the numbers it generates to be between 10 and 99 inclusive. (So they aren't too big, or too small)
C#
rects.Add(new MyRectangle(p, s));
Creates a new rectangle, and add it to the list for later.
C#
MyPanel.Invalidate();
Tells the system to redraw the Panel, by calling it's Paint event.
C#
private void MyPanel_Paint(object sender, PaintEventArgs e)
    {
    ...
    }
Paint event handler - you have met these!
C#
foreach (MyRectangle mr in rects
    {
    ...
    }
Loop through each of the MyRectangle objects in the list and each time round assign one of them to the variable mr - you have probably met these already.
C#
mr.Draw(e.Graphics, pen);
Calls the Draw method on the MyRectange instance in the variable mr


Christ! It's a lot easier to write than to explain! :laugh:
 
Share this answer
 
v4
Comments
defunktlemon 24-Feb-13 5:49am    
Fantastic!! Yeah, I was kinda doing it wrong - just a bit.
So, when a class level private variable ends it automatically disposes of any resources used?
but I don't need to worry about that with the code as follows?

<pre lang="c#">private void button1_Click(object sender, EventArgs e)
{
//Bitmap newImage;
openFileDialog1.Filter = "JPEG IMAGES|*.jpg";
openFileDialog1.InitialDirectory = "C:\\Users\\jason\\Documents\\IProject\\code\\imageAlign\\imageAlign\\bin\\Debug";
openFileDialog1.Title = "Open Image";
if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
panel1.BackgroundImage = Image.FromFile(openFileDialog1.FileName);
panel1.Invalidate();
}
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
using (Pen sb = new Pen (Color.Red,3))
{
e.Graphics.DrawRectangle(sb, 20, 20, 50, 50);
}
}</pre>
OriginalGriff 24-Feb-13 6:03am    
"Yeah, I was kinda doing it wrong - just a bit"
We all do it when we get started! :laugh:

"So, when a class level private variable ends it automatically disposes of any resources used?"
No - resources are only ever reclaimed when the object is Disposed - which only happens when you explicitly (by calling myObject.Dispose()) or implicitly (by exiting from a using block) get rid of uit, or when it is no longer referenced and the Garbage Collector is fired up to free some space - which could be weeks away! :laugh:

If you make a Pen a private class level variable then it is not Disposed until the containing class instance is Disposed - which is what you want if you want to draw in a Paint event because the pen is already created when you need it, so the Paint event handler can be as short and efficient as possible.

"but I don't need to worry about that with the code as follows?"
No - because the using block causes the Pen object to be automatically Disposed when it goes out of scope. But a class level variable would be quicker, and Paint events should be as quick as possible if you want smoothness.
defunktlemon 24-Feb-13 6:19am    
I'm excited now and would like to try and put this into a class :)
I have created a class

<pre lang="c#"> class rectangle
{

private void panel1_Paint(object sender, PaintEventArgs e)
{
using (Pen sb = new Pen(Color.Red, 3))
{
e.Graphics.DrawRectangle(sb, 20, 20, 50, 50);
}</pre>

But don't know what to pass in the method call?

<pre lang="c#">rectangle mc = new rectangle();</pre>
OriginalGriff 24-Feb-13 6:52am    
Answer updated
defunktlemon 24-Feb-13 7:55am    
wow! Now I'm really kazoonked! That is quite a complex piece of code for little ol' me. I have added the first part to the new class created, MyClass (with a capital beginning :))
And the second part you wrote to the 'public partial class Form1 : Form' page.
I need to 'hook them up'. Do you mean call the class - like 'MyRectangle mc = new MyRectangle();'?
Sorry - I'm newbie lemon.

When you say I need to had it context and a suitable pen, do you mean this:
using (Pen sb = new Pen(Color.Red,3))
{
e.Graphics.DrawRectangle(sb, 20, 20, 50, 50);
}
?
Thanks OG

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900