How does the Paint Event work?

DarkD

Member
Joined
Jun 16, 2014
Messages
16
Programming Experience
Beginner
I just went through a lot of work trying to figure out why some of my PictureBoxes had their draw methods called while others didn't. I'm looking at the MSDN for PaintEventArgs and it doesn't seem to explain things very well.

So here's what happened. I had a TableLayoutPanel filled with Pictureboxes which were filled with images. I added the TableLayoutPanel to my form and that whole thing drew just fine. I had an additional 8 pictures to draw as well, but those weren't as lucky. Since they didn't need a TLP I just used PictureBoxes and added all 8 pictureboxes to my form, and only one of them was being drawn at a time. For some reason, only the first draw method that I had was being called. So the TLP and one of the 8 additional images were had their draw methods being called.

I fixed this by calling the draw methods of the other 7 images from the first image, but this seems hacky to me. This solution works in this specific instance, but I would like to know what went wrong with this setup. As far as I can see this is some logic which I'm not understanding rather than a flaw with my coding.
 
If you want us to be able to determine what you did wrong then it would help to show us what you did.

That said, if all you're doing is displaying an Image in a PictureBox then the Paint event is pretty much irrelevant. You assign your Image object to the Image property of the PictureBox or you call the Load method or set the ImageLocation property; job done. If you're trying to use GDI+ to draw on the PictureBox itself then you simply handle the Paint event and use e.Graphics in the event handler to do the drawing, e.g. by calling e.Graphics.DrawString.
 
My problem is that I have 8 pictureboxes and only one of them is getting their draw method called at a time.


My code is a bit too large so I tried to narrow it down to the affected portions as much as I could.


My best guess is that it's not so much a bug in my code, but a misunderstanding in how the draw event works.


I have a class which inherits from PictureBox which I am using instead of Picturebox. The only change is the constructor


C#:
    using System.Drawing;
    using System.Windows.Forms;


    public class DrawingSurface : PictureBox
    {
        public DrawingSurface(Rectangle bounds) : base() {
            base.Dock = DockStyle.Fill;
            base.SizeMode = PictureBoxSizeMode.Zoom;
            //I set the size and bounds which is probably redundant because 
            //I was trying random things to fix the problem
            base.Size = new Size(bounds.Width, bounds.Height);
            base.Bounds = bounds;
            base.Margin = new Padding(0) ;
            base.BackColor = Color.Transparent;
        }
    }



C#:
    public class MyForm:Form
    {
        public MyForm():base()
        {
            base.FormBorderStyle = FormBorderStyle.Sizable;
            base.MaximizeBox = false;
            base.MinimizeBox = false;
            base.Bounds = Screen.PrimaryScreen.Bounds;
        }


    }

Here's my main class




C#:
   class Program
    {
        static int Main()
        {
            short boardOffsetX, boardOffsetY, trayOffsetX, trayOffsetY;
            MyForm gameImage = null;
            Tray playerTray = null;
            ScrabbleBoard board = null;
            BagOfLetters bag = null;




            AppDomain currentDomain = AppDomain.CurrentDomain;
            currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);//this line of code doesn't work either if any of you can spot anything obvious here.  


            //this is my hacky way of centering the images, wouldn't mind you telling me
            //a better way of doing this either.  
            boardOffsetX = (short)(Screen.PrimaryScreen.Bounds.Width/4);
            boardOffsetY = (short)(Screen.PrimaryScreen.Bounds.Height/8);
            trayOffsetX = (short)(Screen.PrimaryScreen.Bounds.Width / 3.3);
            trayOffsetY = (short)(Screen.PrimaryScreen.Bounds.Height / 24);
            try
            {
                gameImage = new MyForm();
                bag = new BagOfLetters();
                board = new ScrabbleBoard(gameImage, new Point(boardOffsetX, boardOffsetY));
                playerTray = new Tray(bag, trayOffsetX, trayOffsetY, gameImage);
                gameImage.ShowDialog();
            }
            finally
            {
                 //Dispose all
            }
        }
    }

My images are drawn like this. I have one large class with a large image holds the 7 smaller classes and their images. I try to initialize everything from my big class.


C#:
   public class Tray{
        private Image TrayImage;
        private DrawingSurface TraySurface;
        private short OffsetY = 0;
        private short OffsetX = 0;
        public List<LB> Tray;


        public Tray(Bag bag, short offsetX, short offsetY, MyForm gameImage)
        {
            Letter tmp;


            //paramater validation


            TrayImage = Image.FromFile(PATHOFTRAY);
            //GetBoundsAsRectangle is just getting the image dimensions as a rectangle
            TraySurface = new DrawingSurface(GetBoundsAsRectangle()) ;
            TraySurface.Location = new Point(offsetX, offsetY);


            this.OffsetX = offsetX;
            this.OffsetY = offsetY;


            do
            {
                tmp = bag.PullLetter();
            }
            while (AddLetter(tmp, gameImage));
            //make this image draw
            TraySurface.Paint += new PaintEventHandler(this.Draw);
            gameImage.Controls.Add(TraySurface);
            TraySurface.SendToBack();
        }




        public bool AddLetter(Letter letter, MyForm gameImage) {
            //argument validation
            Count++;
            letter.PutOnTray(Count, OffsetX, OffsetY);
            gameImage.Controls.Add(letter.GetDrawingSurface());
            if (letterCount >= MAXLETTERS)
            {
                return false;
            }
            return true;
        }




        public void Paint(Object sender, PaintEventArgs drawEvent)
        {
            //paramater validation here
            Rectangle rectangleAreaToDrawImage = new Rectangle(OffsetX,
                                                                OffsetY,
                                                                TrayImage.Width,
                                                                TrayImage.Height);
            // Draw image to screen.
            drawEvent.Graphics.DrawImage(TrayImage, rectangleAreaToDrawImage);
        }
    }

C#:
  public class Letter
    {
        private Image LetterImage;
        private DrawingSurface LetterSurface;
        private int PositionOnTray;


        public Letter(char value, String fName) {
            LetterImage = Image.FromFile(fName);
            LetterSurface = new DrawingSurface(GetBoundsAsRectangle());
        }




        public void PutOnTray(short position, short x, short y)
        {
            //validation
            PositionOnTray = position;
            TrayOffsetX = (short)(x + (position*20));
            TrayOffsetY = y;
            IsOnTray = true;
            LetterBlockSurface.Location = new Point(TrayOffsetX, TrayOffsetY);
            LetterBlockSurface.Paint += new PaintEventHandler(this.Draw);
        }
        public void Paint(Object sender, PaintEventArgs drawEvent)
        {
            int x = 0, y = 0;


            //paramater validation here
            x = (TrayOffsetX + (LetterImage.Width * PositionOnTray));
            y = (TrayOffsetY + 6);
            Location = new Rectangle(x,
                                     y,
                                     LetterImage.Width,
                                     LetterImage.Image.Height);
            drawEvent.Graphics.DrawImage(LetterImage, Location);
    }

So basically the tray has an image and the 7 letters also have images. The letters get drawn on top of the tray for now, but in their own pictureboxes. I added a draw method to tray and letter and added those to the pictureboxes Paint thingy. I need the letters to have seperate pictureboxes because later on I wanna add the functionality to have them be picked up and dragged around the screen with a mouse click.


The problem is that the paint methods aren't being called. Just theory wise with psuedocode, how would you go about painting this? I am from a Java/C/C++ background and graphics were never my point of focus, so I am sure that rather than a bug, its how I am going about this that's wrong.
 
If the Paint event handler is not being executed then the Paint event is not being raised, which means that the control is not being painted. Could it be that you're handling the event of the wrong objects?
C#:
  public class Letter
    {
        private Image LetterImage;
        private DrawingSurface [B][COLOR="#008000"]LetterSurface[/COLOR][/B];
        private int PositionOnTray;


        public Letter(char value, String fName) {
            LetterImage = Image.FromFile(fName);
            LetterSurface = new DrawingSurface(GetBoundsAsRectangle());
        }




        public void PutOnTray(short position, short x, short y)
        {
            //validation
            PositionOnTray = position;
            TrayOffsetX = (short)(x + (position*20));
            TrayOffsetY = y;
            IsOnTray = true;
            LetterBlockSurface.Location = new Point(TrayOffsetX, TrayOffsetY);
            [B][COLOR="#FF0000"]Letter[U]Block[/U]Surface[/COLOR][/B].Paint += new PaintEventHandler(this.Draw);
        }
Should that perhaps be LetterSurface rather than LetterBlockSurface whose Paint event you should be handling? I can't find any other reference to LetterBlockSurface in your code other than those two lines so not sure what it actually is but, as that Letter class has a field of type DrawingSurface, it seems that you should be drawing on that object.
 
Sorry I posted a similar question on Stackoverflow and they were no help so I just copied and pasted the code overhere and added some more detail. The detail left out the change in names. All of it should be LetterSurface without the "Block" part. I didn't mean to shop around for an answer, its just I've been banging my head on this now for 2-3 days.

Anyways, I think I figured out a solution to the problem. I did it by adding the draw event to the forms Paint thingy instead of the picturebox. That seems to work for some reason... This solution seems to work for now, but I intend on adding mouseevents to the pictures. I have been playing around with adding those this last half hour, and I ran into a new problem. No matter where I click on the screen, the mouse event gets triggered. Is that supposed to happen? I can solve that by calculating myself if the mouseclick was over the picture or not, but I don't see this as an ideal solution.
 
Last edited:
Nevermind, it wasn't a solution.... Now I have the same problem, only with mouse events. Only the first LetterSurface is registering the mouse events. Since I'm not drawing from the pictureboxes anymore that works fine, however when I tried adding the mouse events to the picturebox I get the same problem as when I was drawing. Apparently the problem is that only one of the pictureboxes is being inserted into the form.
 
I didn't mean to shop around for an answer, its just I've been banging my head on this now for 2-3 days.
You don't have to apologise for posting the question in more than one place. What you do outside this site is none of our concern. Just as long as you only post each topic once here, that's fine.
 
Do you think I could just email you the visual studio project and you could take a look because I'm sure its something stupid. Just PM me an email I can use and Ill send it right over. I wanted to be somewhat discreet about the contents of the project until now but the differentiating functionality isn't there yet so it shouldn't matter if I send it over.
 
Back
Top Bottom