I'm developing a program that uses 121 pictureboxes on one form and when I run the program, it is rather slow. I haven't even coded yet! If I try to move the window, the contents get especially choppy. It's even worse if I'm moving the app and it hits the edge of the screen. Plus, my pictureboxes all need to have transparent backgrounds. That slows my app even more.
I know that the alternative is having one big picturebox and using the Paint event. I've tried that back in Visual Basic 2003 and it was a total nightmare.
Can anyone help me optimize my app so that all the pictureboxes don't slow it down Thanks!

121 PictureBoxes slow my app. Help me optimize?
eric xing
I'm creating an 11x11 square game board with pieces that move similarly to checkers. I want the user to be able to click and drag a piece and also be able to click to select and then click where he or she wants the piece to move.
You're right, though. I don't need all the overhead of pictureboxes. What I need each object to store is its status, type, x-coordinates for board squares, and y-coordinates for board squares. Maybe x & y coordinates for pixels, too. Any ideas
nick di pietrantonio
I personally would try to steer away from creating this many controls. It is resource intensive, and, as it seems, also cpu intensive (although you will have to measure this in order to see where the bottleneck is).
I've stated my opinion on performance optimization here. If you would still like to guess what the performance bottleneck is, my personal guess would be not to have this many controls, but paint the pictures yourself. Note that you don't necessarily need a picturebox to paint pictures. VB programmers seem to love the picturebox, but when I manually paint bitmaps I personally tend to use a simple Panel.
Can you explain what the images should be able to do Should they be able to move, should they need to scroll In other words: what are the benefits of putting the pictures in single controls
trhodes
You need to call panel1.Invalidate() when ever you want to repaint the panel, when you want to repaint only a specifiec area and not all the panel, call panel1.Invalidate(Rectangle rectToUpdate).
Anonymousxxx
Actually, I have so many projects I've started on my computer... I always have something that requires some minor tweaking.< xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
P.S: No dispose called on the brushes, but these brushes are static ones, OK
OL56814
R.A.F
Aha yes, good point indeed. I assume you are refering to the brushes from the Brushes class Yes, calling Dispose on them is a very bad idea, since it will cause an exception once someone will try to use them after the Dispose.
If you decide to cache custom brushes in your class, that's fine, but best practise would be to derive your class from IDisposable and Dispose of those brushes when you get disposed yourself.
For the people who don't have a clue what we're talking about, this is an addition to our discussion on the Dispose pattern from this previous post.
hary
Bryan Smith
Ah I assumed something like that. True, controls are very convenient for these kinds of situations. My advice would be to first measure the performance hotspots using a profiler, like I stated in the other post. You may be surprised what the actual performance bottleneck appears to be. I've often seen that it appears to be something you never would have thought of.
If you can't do that and would like to optimize by trial and error, I would personally put my money on creating a very lightweight structure for the cells in the grid. The cells hold data like their positions. You are actually creating mini-controls this way. However, I wouldn't paint in the cells, I would paint on one large canvas with the information provided from the cells. Something like that...
lbmcalli
Maybe you should create a panel override that holds the Board data, and on paint, draws it.
for events, over ride onmousedown, up etc. and create your own events.
This way you have one control only and the hard work is done on the painting.
class GameBoard : Panel
{
}
and on the Paint method,
protected override void OnPaint(PaintEventArgs e){
for (int x = 0; x < BoardData.Length; x++){
for (int y = 0; y < BoardData.GetLength(1); y++){
// Draw what ever you need for each piece}
}
}
Stalkers
P. Parent
Quick and dirty example:
copy-paste it to a new class, drag this class to a form, this will give you a kick start.
namespace DemoGame
{
class DemoBoard : Panel
{
public static readonly int PieceEdgeSize = 20;
public static readonly int NumberOfPiecesPerLine = 11;
public static readonly int NumberOfLines = 11;
private Square[,] _boardData =
new Square[NumberOfPiecesPerLine,NumberOfLines];
private int _dragedX = -1;
private int _dragedY = -1;
private bool _inDrag = false;
public DemoBoard()
{
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.UserPaint, true);
InitBoardData();
}
private void InitBoardData()
{
bool red = false;
bool hasPiece = false;
bool pieceWhite = true;
Piece piece = null;
for (int x = 0; x < _boardData.GetLength(0); x++)
{
for (int y = 0; y < _boardData.GetLength(1); y++)
{
hasPiece = false;
piece = null;
// piece on 0 or 10
if (y == 0)
{
hasPiece = true;
pieceWhite = true;
}
else if (y == 10)
{
hasPiece = true;
pieceWhite = false;
}
if (hasPiece)
{
piece = new Piece(pieceWhite);
}
// Draw what ever you need for each piece
_boardData[x,y] = new
Square(red, x, y, piece);
red = !red;
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
for (int x = 0; x < _boardData.GetLength(0); x++)
{
for (int y = 0; y < _boardData.GetLength(1); y++)
{
// Draw what ever you need for each piece
_boardData[x,y].Draw(e.Graphics);
}
}
}
/// <summary>
/// Search and saved the piece that we clicked on
/// </summary>
/// <param name="e"></param>
protected override void OnMouseDown(MouseEventArgs e)
{
_inDrag = false;
base.OnMouseDown(e);
// Search which piece was clicked
Square clickSquare = GetSquareAt(e.Location);
if (clickSquare != null && clickSquare.SquarePiece != null)
{
_dragedX = clickSquare.X;
_dragedY = clickSquare.Y;
_inDrag = true;
this.Cursor = Cursors.Hand;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (_inDrag)
{
_boardData[_dragedX, _dragedY].SquarePiece.PiecePosition
= new Rectangle(e.Location
, _boardData[_dragedX, _dragedY].SquarePiece.PiecePosition.Size);
this.Invalidate();
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (_inDrag)
{
// Moves the drag piece to the drop location:
Square clickSquare = GetSquareAt(e.Location);
if (clickSquare != null && clickSquare.SquarePiece == null)
{
clickSquare.SquarePiece =
_boardData[_dragedX, _dragedY].SquarePiece;
this.Invalidate();
}
}
_inDrag = false;
this.Cursor = Cursors.Hand;
}
private Square GetSquareAt(Point location)
{
for (int x = 0; x < _boardData.GetLength(0); x++)
{
for (int y = 0; y < _boardData.GetLength(1); y++)
{
// Draw what ever you need for each piece
if (_boardData[x, y].SquarePositionRectangle.Contains(location))
{
return _boardData[x, y];
}
}
}
return null;
}
}
class Square
{
private bool _greenBackground;
/// <summary>
/// Gets or sets a value indicating whether the square background is green
/// </summary>
public bool GreenBackground
{
get { return _greenBackground; }
set { _greenBackground = value; }
}
private Rectangle _suqarePositionRectangle;
/// <summary>
/// Gets or sets square position
/// </summary>
public Rectangle SquarePositionRectangle
{
get { return _suqarePositionRectangle; }
set { _suqarePositionRectangle = value; }
}
private Piece _piece;
/// <summary>
/// Gets or sets piece on the square or null
/// </summary>
public Piece SquarePiece
{
get { return _piece; }
set
{
_piece = value;
_piece.PiecePosition = _suqarePositionRectangle;
}
}
private int _x = -1;
public int X
{
get { return _x; }
set { _x = value; }
}
private int _y = -1;
public int Y
{
get { return _y; }
set { _y = value; }
}
public Square(bool greenBackground, int x, int y, Piece piece)
{
_x = x;
_y = y;
_greenBackground = greenBackground;
_piece = piece;
SetSquarePosition();
}
/// <summary>
/// Calculate the poistion of the square
/// </summary>
private void SetSquarePosition()
{
int edgeSize = DemoBoard.PieceEdgeSize;
int recX = _x * edgeSize;
int recY = _y * edgeSize;
_suqarePositionRectangle = new Rectangle(recX, recY, edgeSize, edgeSize);
if (_piece != null)
{
_piece.PiecePosition = _suqarePositionRectangle;
}
}
internal void Draw(Graphics graphics)
{
Brush recBrush = this._greenBackground
Brushes.Green : Brushes.Red;
graphics.FillRectangle(recBrush, _suqarePositionRectangle);
graphics.DrawRectangle(Pens.Black, _suqarePositionRectangle);
if (_piece != null)
{
_piece.Draw(graphics);
}
}
}
class Piece
{
private bool _pieceWhite;
/// <summary>
/// Gets or sets a value indicating whether piece on the square is white
/// </summary>
public bool PieceWhite
{
get { return _pieceWhite; }
set { _pieceWhite = value; }
}
private Rectangle _piecePosition;
/// <summary>
/// Gets or sets piece position
/// </summary>
public Rectangle PiecePosition
{
get { return _piecePosition; }
set { _piecePosition = value; }
}
public Piece(bool pieceWhite)
{
_pieceWhite = pieceWhite;
}
internal void Draw(Graphics graphics)
{
Brush brush = _pieceWhite Brushes.White : Brushes.Black;
graphics.FillEllipse(brush, _piecePosition);
}
}
}