# [.net] GDI+ repaint problem

## Recommended Posts

HI guys its me again , im now porting my picturebox based map editor to a GDI+ environment. what Im trying to do right now is to highlight with a white square the cell where the mouse cursor is hovering Ive been reading everywhere that I should do all my painting in the paint event , so what Im doing is setting a boolean to true in the mousemove event then the paint even reads this value and decides to paint a square brush , but sadly at runtime nothing shows up ive tried many things but I think ive run out of options help me out pls!
[SOURCE]
private void panel1_Paint(object sender, PaintEventArgs e)
{
if (tile_palette != null)
{
Pen highlight = new Pen(Color.White, 5.0f);
Pen grid      = new Pen(Color.Black, 1.0f);

Bitmap curBitmap = new Bitmap(panel1.ClientRectangle.Width, panel1.ClientRectangle.Height);

Graphics back_buffer = Graphics.FromImage(curBitmap);
Graphics front_buffer = panel1.CreateGraphics();

back_buffer.TranslateTransform(this.panel1.AutoScrollPosition.X, this.panel1.AutoScrollPosition.Y);

// BACKBUFFER DRAWING
// Draw Image
back_buffer.DrawImageUnscaled(tile_palette.GetImage(), 0, 0);

// Draw Grid
for (int id_x = 0; id_x <= this.tile_palette.GetImage().Width; id_x += this.tile_palette.GetGridSize())
{
back_buffer.DrawLine(grid, new Point(id_x, 0), new Point(id_x, this.tile_palette.GetImage().Height));
}

for (int id_y = 0; id_y <= this.tile_palette.GetImage().Height; id_y += this.tile_palette.GetGridSize())
{
back_buffer.DrawLine(grid, new Point(0, id_y), new Point(this.tile_palette.GetImage().Width, id_y));
}

if (mouse_data.hover)
{
// Highlight selected cell
if (this.tile_palette.GetSelectedCell(mouse_data.location).Size.Width != 0)
{
//e.DrawRectangle(rectangle, this.tile_palette.GetSelectedCell(r.Location));
back_buffer.DrawRectangle(highlight, tile_palette.GetSelectedCell(mouse_data.location));
}

mouse_data.hover = false;

this.Invalidate();
}

// Present and Dispose

front_buffer.DrawImageUnscaled(curBitmap, 0,0);
// Dispose objects
front_buffer.Dispose();
back_buffer.Dispose();
grid.Dispose();
highlight.Dispose();
}
}// End Paint event

[/SOURCE]
[/source]

##### Share on other sites
Mike.Popoloski    3258
OK, your code is looking a little crazy. Let's go over it a bit.

The first thing you need to do is create a custom control that will handle the painting. When doing custom painting on WinForms, it is almost always necessary to create a custom control instead of just using the exposed paint event.

class MapEditorView : Panel{}

When doing custom drawing, as you may have noticed, you can get some nasty flicker effects, which is probably why you are trying to use the double-buffered approach. WinForms actually has a way to do this built in, so let's take advantage of that.

// constructorMapEditorView(){    DoubleBuffered = true;    ResizeRedraw = true;    SetStyle( ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true );}

Doing this tells .NET a few things: first, we want the control to be double buffered. Second, we need to redraw any time our size changes. And third, we will be doing all of our painting in the paint handler, and no where else.

Instead of creating GDI drawing objects every time you need to paint, create them once and store them in the class, thereby removing the performance strain.

Pen highlightPen;Pen gridPen;// constructorMapEditorView(){    DoubleBuffered = true;    ResizeRedraw = true;    SetStyle( ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true );    highlightPen = new Pen(Color.White, 5.0f);    gridPen = new Pen(Color.Black, 1.0f);}

Before we take a look at the paint event, you should know a few things. You shouldn't call Invalidate within a paint event, as you are just causing the control to paint itself again. Second, you shouldn't use the CreateGraphics function to get a graphics object to draw to. Instead, use the provided object in the event arguments.

Let's take a look (note that I changed the tile_palette to use properties instead of functions, which makes everything look much cleaner).

protected override void OnPaint(PaintEventArgs e){    if (tile_palette == null)        return;    // draw the tile palette    e.Graphics.DrawImageUnscaled(tile_palette.Image, 0, 0);    // draw the grid    for (int x = 0; x <= tile_palette.Image.Width; x += tile_palette.GridSize)        e.Graphics.DrawLine(gridPen, new Point(x, 0), new Point(x, tile_palette.Image.Height));    for (int y = 0; y <= tile_palette.Image.Height; y += tile_palette.GridSize)        e.Graphics.DrawLine(gridPen, new Point(0, y), new Point(tile_palette.Image.Width, y));    // check if our client area contains the cursor    Point mousePosition = Control.PointToClient(Control.MousePosition);        if (ClientRectangle.Contains(mousePosition))    {        Rectangle selectedCell = tile_palette.GetSelectedCell(mousePosition);        if (selectedCell.Width != 0)            e.Graphics.DrawRectangle(highlightPen, selectedCell);    }}

See how much cleaner that is?

Finally, when you are done with your toys, you must put them away. Since we stored the pen objects, we need to get rid of them in the Dispose event.

protected override void Dispose(bool disposing){    if (disposing)    {        gridPen.Dispose();        highlightPen.Dispose();    }}

##### Share on other sites
thank a lot I will try this stuff as soon as possible

:D

lol sorry for the messy code i just posted, I kinda rush it when things dont go ok with my program, so what I posted was a rough layout of what I wanted to do