Invalidating...

Hi everybody

I'm writing a chess game and I encountered a little problem.

Before the main Form loads, I have a Fform in which I set the time etc' and then the main Form shows up.

In my main Form I have a MenuStrip, a PictureBox and a lable.

My problem here is: After the main form loads the MeniStrip doesn't invalidate itself.

I have tried to use the Refresh and Invalidate methods for the Form and for the MenuStrip but it's still doesn't work.

The other problem, which is the same as the first one, is that the lable that represent the time changing during the game, doesn't change.

The MenuStrip and the lable change their appearance - Invalidate - after I move the Form or hide and the then reveal the Form.

When I removed the PictureBox from the Form, the MenuStrip and the lable loaded well.

A little information about the PictureBox: I draw inside the PictureBox the board cells and then the board pieces.

Here is part of the Paint event handker code of the PictureBox:

"pBoard" is the PictureBox.

Bitmap offScreenBitmap = new Bitmap(pBoard.Width, pBoard.Height);

Graphics g = this.pBoard.CreateGraphics();

g = Graphics.FromImage(offScreenBitmap);

Then I draw the cells and then the pieces.

Then II write this :

pBoard.Image = offScreenBitmap;

How can I make sure that the MenuStrip and the lable will invalidte themselfes

Thanks for the help




Answer this question

Invalidating...

  • Cristian Rosa

    Let's walk through your paint code.

    First you init some variables.
    Next you get the graphics object. You need to make sure you dispose of it otherwise you're leaking a resource.
    Then you enumerate through and drawing the board.
    Then you enumerate the pieces on the board.
    Finally you assign the new bitmap image to the background of the box. This requires the panel to paint itself, again.

    The problem is that your app is eating up the CPU trying to repaint the box. Therefore none of the other controls are getting a chance to do anything. You can verify this by commenting out the code that sets the background image of the box. Everything returns to normal. You should probably follow my original recommendation and move the paining code into your own custom control. Then you will only paint when you are told to paint and you won't cause another control (the box in this case) to paint itself. The CPU will return to normal and everything will work properly.

    Michael Taylor - 12/28/06
    http://p3net.mvps.org



  • lms07424

    Thanks a lot man!

  • Jeroen Bransen - J-Thread

    Hi Tylor

    I wrote the code that you wrote in the beginning of your reply but for some reason it's still doesn't work and I don't know why.

    I wrote the:

    base.OnPaint(e);

    line and after that I draw the board as usual but I got the same result.

    Maby I'm mising something here. I wrote the new code.

    protected override void OnPaint( PaintEventArgs e)

    {

    base.OnPaint(e);

    // Paints the board cells

    int x = 0,

    y = 0;

    #region "Code Change"

    // In this case, I never actually use the Paint event's graphics object.

    // It sounds weird, but it's actually way better to just create

    // your own Bitmap/Graphics object, and set the result to the picture

    // box's Image property.

    Bitmap offScreenBitmap = new Bitmap(this.Board.Width, this.Board.Height);

    Graphics g = this.Board.CreateGraphics();

    g = Graphics.FromImage(offScreenBitmap);

    #endregion

    // Draw the board cells

    for (int r = 0; r < 8; r++)

    {

    for (int c = 0; c < 8; c++)

    {

    Point p = new Point(x, y);

    g.DrawImage(_BoardCells[r, c].Image, p);

    x += _CellWidth;

    }

    y += _CellWidth;

    x = 0;

    }

    // Draw the board pieces

    for (int i = 0; i < ChessPieces.Count; i++)

    {

    GetPiece(i).Draw(g);

    }

    #region "Code Change"

    this.Board.BackgroundImage = offScreenBitmap;

    #endregion

    }

    About the best way that you mentioned, I agree with you, but I just want to see that the Form paints itself right.

    thanks for the help again



  • h1

    Hi

    First of all, thanks for your answer.

    The "Paint" event handler is connected to the PictureBox and is located inside the Form.

    I didn't understand what did you mean by "call the base classe's paint handler" or "creating a PictureBox-derived class and handle the painting in there".

    What is the base classe's paint handler I know that the Form can have his own Paint event handler and the PictureBox can have his own Paint event handler. What is the base classe's paint handler can you explain it to me. I have tried to create a PictureBox-derived class, I builded the class and added the PictureBox to the Form. but what do I do from there I don't think that creating a derived class is that good because I have other components in the Form that don't repaint themselfs when the Form is loaded.

    Thanks for your help



  • ChrisMcCabe

    Is the code you posted for the paint handler in the form or the PictureBox-derived class. If it is in the form then you need to be sure to call the base classe's paint handler otherwise none of the children will be notified to refresh.

    You should consider creating a PictureBox-derived class and handle the painting in there. Better yet I'd ditch the whole PictureBox derivation and derive directly from a control since you don't really want any of the functionality of the class anyway (imagine if someone tried to set your custom classe's Image property directly). To make it efficient be sure to call SetStyle(ControlStyles.AllPaintingInWmPaint).

    Michael Taylor - 12/27/06
    https://p3net.mvps.org


  • Sam Jost

    What I meant was if you are painting in the form's paint handler then you must be sure to call the base class implementation.


    public class MyForm : Form
    {
    protected override void OnPaint ( PaintEventArgs e )
    {
    base.OnPaint(e); //Call base class otherwise child controls won't paint

    //Paint the picture box
    }
    }

    However that is not the best way to do it. Instead you should create a custom control to paint your chess board. In the simplest case all you need is a basic control that draws a checkered background. By isolating the control's rendering you can change the implementation over time without impacting the main form. For example down the road you might allow users to specify their own background image to use so you might change the internal rendering code while the public interface (used by the form) stays the same.

    class ChessBoard : Control
    {
    protected override void OnPaintBackground ( PaintEventArgs pevent )
    {
    base.OnPaintBackground(pevent);

    //Foreground color is used for "white" team and background is used for "black" team
    //Draw an 8x8 grid with alternating colors
    SolidBrush brWhite = null;
    SolidBrush brBlack = null;

    try
    {
    brWhite =
    new SolidBrush(ForeColor);
    brBlack =
    new SolidBrush(BackColor);

    //The size of each grid
    Size szGrid = new Size(Width / 8, Height / 8);
    bool bUseWhite = true;

    //Draw the grid
    int posX = 0;
    int posY = 0;
    for (int nX = 0; nX < 8; ++nX)
    {
    for (int nY = 0; nY < 8; ++nY)
    {
    pevent.Graphics.FillRectangle(bUseWhite brWhite : brBlack, posX, posY, szGrid.Width, szGrid.Height);

    bUseWhite = !bUseWhite; //Toggle color
    posY += szGrid.Height; //New Y position is just the addition of the grid height
    };

    //Toggle the color again so we alternate
    bUseWhite = !bUseWhite;

    //New X position is just the addition of the grid width and height resets to 0
    posX += szGrid.Width;
    posY =
    0;
    };
    }
    finally
    {
    if (brWhite != null)
    brWhite.Dispose();
    if (brBlack != null)
    brBlack.Dispose();
    };
    }
    }

    The above code is a rudimentary chess board. It flickers when it renders and doesn't scale properly. However you can extend this base class to add new features such as designer support and whatnot. Additionally you can add code to manage the display of chess pieces (or better yet use a separate class to do that but assign the piece renderer to the board class during initialization).

    Michael Taylor - 12/28/06


  • Invalidating...