views:

449

answers:

3

Hey, I need to do my drawing over a panel in C# but without placing my drawing code inside "panel1_Paint", how can I do that ?? BTW, I'm using WinForms.

Update : I forgot to make something clear, I need not to place my drawing code inside paint handler, because I need to start drawing depending on buttons' events.

+2  A: 

Here's one way:

private void DrawOnPanel()
{
    IntPtr hwnd = panel1.Handle;  

    using (Graphics graphics = Graphics.FromHwnd(hwnd))
    {
        graphics.DrawRectangle(new Pen(Color.Blue, 1), 1, 1, 100, 100);
    }
}

Edit: I should point out that if you draw on a control this way, the graphics you draw will disappear when the control repaints itself.

Charles
That really worked thanks a lot :)
Shaza
I'm glad it worked for you, but you might find this to be unusable if you want the graphics to persist after a repaint. I think if somebody posts an answer with a more "proper" way, you should accept their answer instead (as long as it works for you).
Charles
I agree with overslacked and Charles: This is really not a very good approach.
Philip Daubmeier
This is *very* wrong.
Hans Passant
@nobugz: actually, it's quite right. The fact that there are better answers to a question does not make other answers wrong. It *exactly* answers the question, and it includes a warning as to its disadvantage. Not to mention the OP accepting it, and me advising them to consider other answers. But hey, thanks for downvoting me in spite of all that.
Charles
+4  A: 

Usually you do all your drawings in the paint event handler. If you want to do any update (if a user clicks on the panel for example) you have to defer that action: you store the required data (coordinates where the user clicked) and force a redraw of the control. This causes the paint event to be fired, where you can then draw things you stored before.

Another way would be (if you really want to draw outside of the 'panel1_Paint' event handler) to draw inside a buffer image, and copy the image to the controls graphics object in the paint event handler.

Update:

An example:

public class Form1 : Form
{
    private Bitmap buffer;

    public Form1()
    {
        InitializeComponent();

        // Initialize buffer
        panel1_Resize(this, null);
    }

    private void panel1_Resize(object sender, EventArgs e)
    {
        // Resize the buffer, if it is growing
        if (buffer == null || 
            buffer.Width < panel1.Width || 
            buffer.Height < panel1.Height)
        {
            Bitmap newBuffer = new Bitmap(panel1.Width, panel1.Height);
            if (buffer != null)
                using (Graphics bufferGrph = Graphics.FromImage(newBuffer))
                    bufferGrph.DrawImageUnscaled(buffer, Point.Empty);
            buffer = newBuffer;
        }
    }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        // Draw the buffer into the panel
        e.Graphics.DrawImageUnscaled(buffer, Point.Empty);
    }



    private void button1_Click(object sender, EventArgs e)
    {
        // Draw into the buffer when button is clicked
        PaintBlueRectangle();
    }

    private void PaintBlueRectangle()
    {
        // Draw blue rectangle into the buffer
        using (Graphics bufferGrph = Graphics.FromImage(buffer))
        {
            bufferGrph.DrawRectangle(new Pen(Color.Blue, 1), 1, 1, 100, 100);
        }

        // Invalidate the panel. This will lead to a call of 'panel1_Paint'
        panel1.Invalidate();
    }
}

Now the drawn images wont be lost, even after a redraw of the control, because it only draws out the buffer (the image, saved in the memory). Additionally you can draw things anytime a event occurs, by simply drawing into the buffer.

Philip Daubmeier
Upvoted, but I'd really like it if the Refresh() was changed to panel1.Invalidate(), and if the rendering was done in its own function, called by the button's click event. Attaching a resize handler to resize the buffer would be the icing. :)
overslacked
@overslacked: youre right. done :)
Philip Daubmeier
+1 Definitely preferable to my answer in any conceivable application. :)
Charles
Thanks that's what I was thinking about, but couldn't figure it all alone.
Shaza
Youre very welcome!
Philip Daubmeier
Can I use this technique to connect two panels with two different buffers?
Shaza
what do you mean with 'connect two panels'? do you want to write into the buffer of the other one? you could post a new question specifying the goal you want to archieve.
Philip Daubmeier
A: 

Try this: what happens when you come back to your application, after using another application that covers up part or all of your window?

What you should be doing is rendering what you need to an offscreen bitmap, called a buffer. You'll render the image whenever you want, in response to anything that changes what the image should display. Then, from the panel's paint event, you'll copy from that buffer to the panel.

Graphics programming is a lot of fun. It might seem like there are a lot of details to consider, but there aren't many once you learn them. But this is an area where a slapdash approach will show through, and can make an application feel very unreliable and unprofessional.

overslacked
Exactly, by the way thats what I already said.
Philip Daubmeier
@overslacked/phild: For a novice, this explanation might be difficult to understand. Maybe one of you could post a code sample? It would be good to have a less hackish way than mine shown.
Charles
@Charles: youre right. One moment...
Philip Daubmeier
@Charles M - honestly, my point is more about patience and consideration, rather than sticking in the first bit of code that seems to accomplish the task. I have other answers (as many do), easily searchable, that quite well describe the methods needed.
overslacked