Link to home
Start Free TrialLog in
Avatar of yaronusa
yaronusa

asked on

C# GDI Drawing in Panel in Custom Control in c sharp

I am trying to draw a simple square inside a panel which is inside a user custom control. I have been able to: (1) draw exactly that inside a panel in a form (2) been able to draw in a custom control, and use that control in another project.

What I can't do is see my drawing (a simple square) drawn inside a panel that is inside a custom control because the panel (pnlMain) Paint event never seems to get called.

The code snippet is from my custom control:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
 
namespace gridCustomControl
{
    
 
    public partial class GridCustomControl : System.Windows.Forms.Control
    {
        public GridCustomControl()
        {
            InitializeComponent();
        }
 
        private void GridCustomControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            //These 3 line of test code worked, so I commented them out:
            
            /*
            Graphics g = e.Graphics;
            g.FillRectangle(Brushes.Red, ClientRectangle);
            g.DrawString("Hello World!", Font, Brushes.Black, 0, 0);
            */
        }
 
        private void pnlMain_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            //The path of execution never goes through this function
            Graphics dc = e.Graphics;
            Console.WriteLine("in pnlMain_Paint"); 
            Pen blackPen = new Pen(Color.Black, 1);
            Point topLeftRec = new Point(0, 0);
            Size howBig = new Size(75, 75);
            Rectangle rec = new Rectangle(topLeftRec, howBig);
            dc.DrawRectangle(blackPen, rec);
            blackPen.Dispose();
        }
 
    } // End Class GridCustomControl
} // End namespace gridCustomControl

Open in new window

Avatar of jgordos
jgordos
Flag of United States of America image

Hang the painting of the panel off the custom control's paint event; not the panel.

Once  you have a custom control, it's all up to you.. windows won't do the messaging for you
Avatar of yaronusa
yaronusa

ASKER

OK, so how do I do the messaging, is it difficult? It sounds like it would be the clean solution...

I did try the following: In the Paint method of the custom control, my panel (pnlMain) does not have a Graphics property, only createGraphics. I don't think I want to create the graphics all over again everytime the Paint function for the custom control is called do I?
private void GridCustomControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            //These 3 line of test code worked, so I commented them out:
            
            /*
            Graphics g = e.Graphics;
            g.FillRectangle(Brushes.Red, ClientRectangle);
            g.DrawString("Hello World!", Font, Brushes.Black, 0, 0);
            */
 
            Graphics dc = /* ??? *?
            Console.WriteLine("in pnlMain_Paint"); 
            Pen blackPen = new Pen(Color.Black, 1);
            Point topLeftRec = new Point(0, 0);
            Size howBig = new Size(75, 75);
            Rectangle rec = new Rectangle(topLeftRec, howBig);
            dc.DrawRectangle(blackPen, rec);
            blackPen.Dispose();
        }

Open in new window

Yeah, you do... it's not as bad as you think; just be sure to dispose of it after every paint...

You aren't trying to do some real-time video gamey graphics are you?

I'm not sure what this does, but this line was added to my InitializeComponent() method of the custom control:

this.pnlMain.Paint += new System.Windows.Forms.PaintEventHandler(this.pnlMain_Paint);

where pnlMain is the panel I'm trying to draw to.
that certainly looks like the hook for the paint event was added to the handler for the form.

in other words, the form now controls the paint event for the handler....

This is in the portion of the code you can't change, yes?
No game programming... according to your advice, the code snippet shows what I have so far, but I still don't see a black square.... any ideas why (When debugging the execution path does go thru the paint event method)?
namespace gridCustomControl
{
    public partial class GridCustomControl : System.Windows.Forms.Control
    {
        public GridCustomControl()
        {
            InitializeComponent();
        }
 
        private void GridCustomControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Graphics dc = pnlMain.CreateGraphics();
            Console.WriteLine("in pnlMain_Paint"); 
            Pen blackPen = new Pen(Color.Black, 1);
            Point topLeftRec = new Point(0, 0);
            Size howBig = new Size(75, 75);
            Rectangle rec = new Rectangle(topLeftRec, howBig);
            dc.DrawRectangle(blackPen, rec);
            blackPen.Dispose();
            dc.Dispose();
        }
    } // End Class GridCustomControl
} // End namespace gridCustomControl

Open in new window

This is how my InitializeComponent of the custom control looks like now:
private void InitializeComponent()
        {
            this.pnlMain = new System.Windows.Forms.Panel();
            this.SuspendLayout();
            // 
            // pnlMain
            // 
            this.pnlMain.Location = new System.Drawing.Point(0, 0);
            this.pnlMain.Name = "pnlMain";
            this.pnlMain.Size = new System.Drawing.Size(200, 100);
            this.pnlMain.TabIndex = 0;
            // 
            // GridCustomControl
            // 
            this.Paint += new System.Windows.Forms.PaintEventHandler(this.GridCustomControl_Paint);
            this.ResumeLayout(false);
 
        }

Open in new window

did you paint the base object?

ie,

base.OnPaint(e)
before you drawyour custom stuff...

and... don't create your graphics

do

Graphics dc = e.Graphics....

-john

That's what I was saying in my previous post, that my panel (pnlMain) does not have a Graphics property, only createGraphics within the Paint Event Method -- take a look at the ??? at the code snippet -- I don't know what to put there.

Should I put pnlMain.CreateGraphics() or e.Graphics. If I put e.Graphics... then how can I update my panel??

I don't know where to put base.OnPaint(e)
private void GridCustomControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            //These 3 line of test code worked, so I commented them out:
            
            /*
            Graphics g = e.Graphics;
            g.FillRectangle(Brushes.Red, ClientRectangle);
            g.DrawString("Hello World!", Font, Brushes.Black, 0, 0);
            */
 
            Graphics dc = /* ??? *?
            Console.WriteLine("in pnlMain_Paint"); 
            Pen blackPen = new Pen(Color.Black, 1);
            Point topLeftRec = new Point(0, 0);
            Size howBig = new Size(75, 75);
            Rectangle rec = new Rectangle(topLeftRec, howBig);
            dc.DrawRectangle(blackPen, rec);
            blackPen.Dispose();
        }

Open in new window

Graphics dc = e.Graphics;
And, remember, first thing in the paint method should be


base.OnPaint(e);

-john
OK...

Let me see if I can explain this better...

I need the lline of code where I draw the rectangle IN the panel (pnlMain) that is IN the control? Otherwise, the code as it is, just writes to the panel, which I could already do...

hmmm... I thought the base.OnPaint(e) is called only if you are overriding a paint event method, but in the custom control context, I'm in a private event method, so I don't think there is any "base" to call, my code crashes if I put Base.OnPaint(e).

Maybe I'm misunderstanding something, should I be overriding the control paint method with a paint event for the panel, and in that panel paint event call the base.OnPaint(e)?

yaronusa said:

>OK...
>
>Let me see if I can explain this better...
>
>I need the lline of code where I draw the rectangle IN the panel (pnlMain) that is IN the control? Otherwise, the >code as it is, just writes to the panel, which I could already do...
>
and
> Maybe I'm misunderstanding something, should I be overriding the control paint method with a paint event for  >the panel, and in that panel paint event call the base.OnPaint(e)?

Suddenly, you've confused me....

You have a custom control.
Within that custom control is a panel.
you want to draw a square within the PANEL that is within the CONTROL on the "onPaint" method, yes?

-john



Yes, I would like to do that, that is correct. I would prefer the "onPaint" method belong to the panel that is IN the control, but I'll take it ANYWAY  I can get it! :)
Okay, good! I understand the problem again!

In the routine

GridCustomControl_Paint

if you call

base.OnPaint(e);

You're crashing?

-john
Oh shit.

Are you extending "Control" or "UserControl"?

-john
this is correct, that line makes me crash. Without the line, it doesn't crash.

You can see the line in the code snippet.
private void GridCustomControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            base.OnPaint(e); //This line causes C# VS to crash
            Graphics dc = e.Graphics;
            Console.WriteLine("in pnlMain_Paint");
            Pen blackPen = new Pen(Color.Black, 1);
            Point topLeftRec = new Point(0, 0);
            Size howBig = new Size(75, 75);
            Rectangle rec = new Rectangle(topLeftRec, howBig);
            dc.DrawRectangle(blackPen, rec);
            blackPen.Dispose();
            dc.Dispose();
        }

Open in new window

I don't think I'm extending it, I basically created a control and dragged and dropped a panel into it in design mode. Here is the full code for the full user custom control:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
 
namespace gridCustomControl
{
    public partial class GridCustomControl : System.Windows.Forms.Control
    {
        public GridCustomControl()
        {
            InitializeComponent();
        }
 
        private void GridCustomControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            base.OnPaint(e); //This line causes C# VS to crash
            Graphics dc = e.Graphics;
            Console.WriteLine("in pnlMain_Paint");
            Pen blackPen = new Pen(Color.Black, 1);
            Point topLeftRec = new Point(0, 0);
            Size howBig = new Size(75, 75);
            Rectangle rec = new Rectangle(topLeftRec, howBig);
            dc.DrawRectangle(blackPen, rec);
            blackPen.Dispose();
            dc.Dispose();
        }
 
        private void pnlMain_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Graphics dc = e.Graphics;
            Console.WriteLine("in pnlMain_Paint");
            Pen blackPen = new Pen(Color.Black, 1);
            Point topLeftRec = new Point(0, 0);
            Size howBig = new Size(75, 75);
            Rectangle rec = new Rectangle(topLeftRec, howBig);
            dc.DrawRectangle(blackPen, rec);
            blackPen.Dispose();
            dc.Dispose();
        }
    } // End Class GridCustomControl
} // End namespace gridCustomControl

Open in new window

change what you're inheriting from to UserControl

-j
OK I tried that just now, when I run the program, I get a blank form nothing drawn on it.

Here's my current code below... something maybe I'm missing?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
 
namespace gridCustomControl
{
    public partial class GridCustomControl : UserControl
    {
        public GridCustomControl()
        {
            InitializeComponent();
        }
 
        private void GridCustomControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            //Graphics dc = e.Graphics;
            //Console.WriteLine("in pnlMain_Paint");
            //Pen blackPen = new Pen(Color.Black, 1);
            //Point topLeftRec = new Point(0, 0);
            //Size howBig = new Size(75, 75);
            //Rectangle rec = new Rectangle(topLeftRec, howBig);
            //dc.DrawRectangle(blackPen, rec);
            //blackPen.Dispose();
        }
 
        private void pnlMain_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Graphics dc = e.Graphics;
            Console.WriteLine("in pnlMain_Paint");
            Pen blackPen = new Pen(Color.Black, 1);
            Point topLeftRec = new Point(0, 0);
            Size howBig = new Size(75, 75);
            Rectangle rec = new Rectangle(topLeftRec, howBig);
            dc.DrawRectangle(blackPen, rec);
            blackPen.Dispose();
        }
    } // End Class GridCustomControl
} // End namespace gridCustomControl

Open in new window

okay...

now name the paint method IN THE CONTROL

protected override void OnPaint(PaintEventArgs e)


that should get you a paint method for the custom control

I renamed:

private void GridCustomControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

TO:

protected override void GridCustomControl_Paint(object sender, PaintEventArgs e)

What's next?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
 
namespace gridCustomControl
{
    public partial class GridCustomControl : UserControl
    {
        public GridCustomControl()
        {
            InitializeComponent();
        }
 
        // igordos: is this what you wanted me to type?
        protected override void GridCustomControl_Paint(object sender, PaintEventArgs e)
        {
            //Graphics dc = e.Graphics;
            //Console.WriteLine("in pnlMain_Paint");
            //Pen blackPen = new Pen(Color.Black, 1);
            //Point topLeftRec = new Point(0, 0);
            //Size howBig = new Size(75, 75);
            //Rectangle rec = new Rectangle(topLeftRec, howBig);
            //dc.DrawRectangle(blackPen, rec);
            //blackPen.Dispose();
        }
 
        private void pnlMain_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            //Graphics dc = e.Graphics;
            //Console.WriteLine("in pnlMain_Paint");
            //Pen blackPen = new Pen(Color.Black, 1);
            //Point topLeftRec = new Point(0, 0);
            //Size howBig = new Size(75, 75);
            //Rectangle rec = new Rectangle(topLeftRec, howBig);
            //dc.DrawRectangle(blackPen, rec);
            //blackPen.Dispose();
        }
    } 
} 

Open in new window

yes, that's right...

Now, the custom control has a paint method...

Now, here's the ROOT OF OUR PROBLEM!

The custom control (to your way of thinking) has some "sub controls" on it.

You want to call the paint method of the panel, yes?

First things, first...

in the paint method do

base.OnPaint(e);

then...

plnMain_Paint( e );

That should get us pretty close...

-john




Before implementing your above suggestion, I'm getting an error:

Error      1      'gridCustomControl.GridCustomControl.GridCustomControl_Paint_1(object, System.Windows.Forms.PaintEventArgs)': no suitable method found to override      C:\Users\PMC TB3\Documents\Visual Studio 2005\Projects\gridCustomControl\gridCustomControl\GridCustomControl.cs      19      33      gridCustomControl

No suitable method to override... should i change something?
namespace gridCustomControl
{
    public partial class GridCustomControl : UserControl
    {
        public GridCustomControl()
        {
            InitializeComponent();
        }
 
        // igordos: the following line has the error:
        protected override void GridCustomControl_Paint(object sender, PaintEventArgs e)
        {
            //Graphics dc = e.Graphics;
            //Console.WriteLine("in pnlMain_Paint");
            //Pen blackPen = new Pen(Color.Black, 1);
            //Point topLeftRec = new Point(0, 0);
            //Size howBig = new Size(75, 75);
            //Rectangle rec = new Rectangle(topLeftRec, howBig);
            //dc.DrawRectangle(blackPen, rec);
            //blackPen.Dispose();
        }
 
        private void pnlMain_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            //Graphics dc = e.Graphics;
            //Console.WriteLine("in pnlMain_Paint");
            //Pen blackPen = new Pen(Color.Black, 1);
            //Point topLeftRec = new Point(0, 0);
            //Size howBig = new Size(75, 75);
            //Rectangle rec = new Rectangle(topLeftRec, howBig);
            //dc.DrawRectangle(blackPen, rec);
            //blackPen.Dispose();
        }
 
    } 
} 

Open in new window

try calling it

OnPaint

not

GridCustomControl_Paint

-j
OK, so far so good. The snippet of code has what I have so far.

It compiles and runs. But the square is drawn on the Form, not in the panel.

What should we do next?
namespace gridCustomControl
{
    public partial class GridCustomControl : UserControl
    {
        public GridCustomControl()
        {
            InitializeComponent();
        }
 
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            pnlMain_Paint(pnlMain, e);
        }
 
        private void pnlMain_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Graphics dc = e.Graphics;
            Console.WriteLine("in pnlMain_Paint");
            Pen blackPen = new Pen(Color.Black, 1);
            Point topLeftRec = new Point(0, 0);
            Size howBig = new Size(75, 75);
            Rectangle rec = new Rectangle(topLeftRec, howBig);
            dc.DrawRectangle(blackPen, rec);
            blackPen.Dispose();
        }
    } 
} 

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of jgordos
jgordos
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
OK  I just did that, same result as before so far... Here's the snippet:
namespace gridCustomControl
{
    public partial class GridCustomControl : UserControl
    {
        public GridCustomControl()
        {
            InitializeComponent();
        }
 
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            pnlMain_Paint(pnlMain, e);
            
        }
 
        private void pnlMain_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics dc = e.Graphics;
            Console.WriteLine("in pnlMain_Paint");
            Pen blackPen = new Pen(Color.Black, 1);
            Point topLeftRec = new Point(0, 0);
            Size howBig = new Size(75, 75);
            Rectangle rec = new Rectangle(topLeftRec, howBig);
            dc.DrawRectangle(blackPen, rec);
            blackPen.Dispose();
        }
    } 
} 

Open in new window

good!

Now, we've got a paint loop working for the panel...

The reason it looks odd to you is the topLeftRect really isn't a rect... you just set it (arbitrarily) to 0,0

You now need to base it from the topleft corner of the panel you installed...

So, get the rect of the panel, and add it's top left corner to the co-ordinates of the square you want....
The panel is hiding the rectangle. It is like they are on different layers, with the panel being on top, and the rectangle hidden behind it..
I don't know if the following will help, but this is the code I used when drawing IN a panel that was IN a regular form.

The rectangle shows up on top of the panel:
private void pnlClientView_Paint(object sender, PaintEventArgs e)
        {
            Graphics dc = e.Graphics;
            Size scrollOffset = new Size(pnlClientView.AutoScrollPosition);
            Pen blackPen = new Pen(Color.Black, 1);
            Point topLeftRec = (Point)scrollOffset;
            Size howBig = new Size(75, 75);
            Rectangle rec = new Rectangle(topLeftRec, howBig);
            dc.DrawRectangle(blackPen, rec);
        }

Open in new window

It was my mistake the rectangle was showing up behind the panel, it was just an illusion, your solution worked ...  thanks so much for helping a newbie...  you put a lot of effort.... till next time... =)