Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: C#
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
 
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 23-Feb-13 22:23pm
Edited 23-Feb-13 22:32pm
v2
Comments
defunktlemon at 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 at 24-Feb-13 5:06am
   
Answer updated

1 solution

Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

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 | :laugh:
 
What you need to do is split the code:
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)
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:
        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 Smile | :)
I have created a class

 
    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?"
 
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?
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:
        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 Smile | :) )
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);
           }
 

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!
 
    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.
 
    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
public MyRectangle(){}
would do, but it allows you to specify the data when you create it as parameters: so instead of using
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.

    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.
        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!
        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...
        Pen pen = new Pen(Brushes.Red, 2);
Easy-peasy: It's just your line moves to a class level variable.
        private void butAddARectangle_Click(object sender, EventArgs e)
            {
            ...
            }
Button Click event handler - you have met these already, I think.
            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)
            rects.Add(new MyRectangle(p, s));
Creates a new rectangle, and add it to the list for later.
            MyPanel.Invalidate();
Tells the system to redraw the Panel, by calling it's Paint event.
        private void MyPanel_Paint(object sender, PaintEventArgs e)
            {
            ...
            }
Paint event handler - you have met these!
            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.
                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 | :laugh:
  Permalink  
v4
Comments
defunktlemon at 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 at 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 at 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 at 24-Feb-13 6:52am
   
Answer updated
defunktlemon at 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
defunktlemon at 24-Feb-13 8:10am
   
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?
OriginalGriff at 24-Feb-13 9:01am
   
Answer updated.
Very, very updated!
defunktlemon at 24-Feb-13 9:47am
   
Holy cow!... That really is some rather awesome explaining - I will have to digest that a few times to really get it I think. Greatly appreciated.
I think I have hooked up correctly. Not getting a result but no errors, which is nice.
I have two buttons on the form. The first opens the image in background of panel. The second is now hooked up to butAddRectangle_click - which I believe is correct.
My class code looks like the following:
 
public class MyRectangle
{
public Rectangle Rect { get; set; }
public MyRectangle(Point p, Size s)
{
Rect = new Rectangle(p, s);
}
 
//public MyRectangle()
//{
// // TODO: Complete member initialization
//}
 
public void Draw(Graphics g, Pen p)
{
 
g.DrawRectangle(p, Rect);
}
}
 
The second section you wrote is just pasted in my Form1.cs page.
 
Button 1 opens image ok but nothing happens when button 2 is clicked. Might this be because button two is not related to what the code in button1_click is doing?
defunktlemon at 24-Feb-13 10:02am
   
I'll probably get arrested for posting too much code here - but just so you know what I have done here is the code from class From1: Form
 
private void button1_Click(object sender, EventArgs 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);
panel1.Invalidate();
}
}
private List rects = new List();
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));
panel1.Invalidate();
}
 
private void MyPanel_Paint(object sender, PaintEventArgs e)
{
foreach (MyRectangle mr in rects)
{
mr.Draw(e.Graphics, pen);
}
}
OriginalGriff at 24-Feb-13 10:24am
   
There are two possiblities as to whay nothing is happening - the first is that the button isn't hooked up correctly, the other is that teh paint event isn't hooked correctly.
 
So...time for a little magic! :laugh:
Or at least, magic if you are as old as I am, or haven't met it before...
The Debugger!
This is your best friend. Honest! It may not ever get a round in, but it will give you enough time to get to the bar. I grew up without any of the facilities it provides, and trust me, it can save you so much time.
Go to the first executable line in the button click handler:
Point p = new Point(r.Next(10, 100), r.Next(10, 100));
Put your cursor on it, and use the menu: "Debug...Toggle Breakpoint". A red dot will appear at teh start of the line. Remember where the dot is. If you click the dot, it disappears, and if you click there again it will come back. While it is there, when you run your code it will stop and let you look what is happening when it tried to execute that statement, so leave the dot there. Add a dot at the start of the MyPanel_Paint handler as well - you just have to click where the red dot will be to turn one on.
 
Run the program. Almost immediately, it should stop, with a yellow arrow overlaid over the red dot for the paint event. If it doesn't, the the paint event is not hooked up which would explain why you haven't got anything showing! If it does, take off the breakpoint for a moment by clicking on it, and continue the program by pressing F5. Press your new button - it should stop at the breakpoint in the click handler. If it doesn't, the click is not hooked up correctly. If it does, press F5 and see if anything changes on the display.
 
If not, tell me what does happen!
defunktlemon at 24-Feb-13 10:37am
   
It didn't make it to the first red dot. The form opened and when I clicked button1 the image loaded with not change in the dots. I then clicked button2 and the yellow arrow appeared over the first red dot - so I guess the paint_event is not hooked up?
defunktlemon at 24-Feb-13 10:39am
   
also, it has in hte error box the Locals with e {X=29 Y = 14 Button = Left}
marked in red
OriginalGriff at 24-Feb-13 10:48am
   
Yep - the paint event isn't hooked up. Click the panel in the designed, look at the Events in teh Property pane, and check / set via the Paint event and it's dropdown.
the "e {X=29 Y = 14 Button = Left}" in locals is another part of the magic - it shows you the current value of the variables and will let you watch what is happening as you step through the program if you are trying to fix your code. (Did I forget that - you can execute a line at a time, or even make changes and execute again to see what would happen: It's magic I tell you!) The "locals" tab is the variables within the current method, but there is also the "Auto" and "Watch" tabs as well - they are seriously useful!
defunktlemon at 24-Feb-13 10:58am
   
Oh my God!! You are my new best buddy Guru friend for life(however much life you have left)
I can't believe I have finally gotten this working at last(or you have) Been hitting this for weeks and weeks with others trying to help but all have fallen.
I have not had such excellent help on countless forums in all my life. Your responses today were highly detailed and totally committed. You earn a gold star sir - a beer if I could too.
Thank you thank you thank you.
I am your deepest admirer (even if you do have a long pony tail - even longer beard - and see the world in binary as all programmers of your era did.
Thank you sir. :)
OriginalGriff at 24-Feb-13 11:01am
   
You're welcome!
I'll take that lot as a complement - I think. :laugh:
defunktlemon at 24-Feb-13 11:07am
   
not today - I'm exhausted as I'm sure you are - but if I have any questions about using that with my bigger plan would you mind if I continued this?
OriginalGriff at 24-Feb-13 11:13am
   
Go for it - but we might have to move to a new question at some point as this is getting a bit long...
defunktlemon at 25-Feb-13 7:23am
   
OriginalGriff.
Guess what? I need to change the code you helped me complete to have a slightly different functionality. And, I think you're the best man for the task of helping me achieve that, given that you gave the initial solution. But, as you suggested, this would be better in a new post. Do you have some time in the near future? I can stick a link here to the new problem.

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

  Print Answers RSS
0 OriginalGriff 255
1 Maciej Los 225
2 Manfred R. Bihy 190
3 CHill60 180
4 _Amy 155
0 OriginalGriff 7,395
1 Sergey Alexandrovich Kryukov 6,163
2 Maciej Los 3,754
3 Peter Leow 3,448
4 CHill60 2,702


Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 24 Feb 2013
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100