开发者

Performance issue when adding controls to a panel in C#

I am creating a panel and then adding some labels/buttons to it to form a grid. The issue is that if I add more than say 25x25 items to the panel, there is a terrible performance hit. I can resize the form ok but when I scroll the panel to see all the labels the program lags, the labels/buttons tear or flicker, and sometimes it can make the program unresponsive. I have tried adding the controls to a "DoubleBufferedPanel" that I created. This seems to have no effect. What else could I do? Sorry for such a large code listing. I didn't want to waste anyone's time.

namespace GridTest
{
    public partial class Form1 : Form
    {
        private const int COUNT = 50;
        private const int SIZE = 50;
        private Button[,] buttons = new Button[COUNT, COUNT];
        private GridPanel pnlGrid;

        public Form1()
        {
            InitializeComponent();

            pnlGrid = new GridPanel();
            pnlGrid.AutoScroll = true;
            pnlGrid.Dock = DockStyle.Fill;
            pnlGrid.BackColor = Color.Black;

            this.Controls.Add(pnlGrid);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            int x = 0;
            int y = 0;
            int offset = 1;

            for (int i = 0; i < COUNT; i++)
            {
                for (int j = 0; j < COUNT; j++)
                {
                    buttons[i, j] = new Button();
                    buttons[i, j].Size = new Size(SIZE, SIZE);
                    buttons[i, j].Location = new Point(x, y);
                    buttons[i, j].BackColor = Color.White;

                    pnlGrid.Controls.Add(buttons[i, j]);

                    x = x + SIZE + offset;
                }

                x = 0;
                y = y + SIZE + offset;
            }
        }
    }
}

Also, the GridPanel class:

namespace GridT开发者_Go百科est
{
    public class GridPanel : Panel
    {
        public GridPanel()
            : base()
        {
            this.DoubleBuffered = true;
            this.ResizeRedraw = false;
        }
    }
}


If you must add controls at run time, based on some dynamic or changing value, you might want to consider creating an image on the fly, and capturing mouse click events on its picturebox. This would be much quicker and only have one control to draw rather than hundreds. You would lose some button functionality such as the click animation and other automatic properties and events; but you could recreate most of those in the generation of the image.

This is a technique I use to offer users the ability to turn on and off individual devices among a pool of thousands, when the location in a 2-dimensional space matters. If the arrangement of the buttons is unimportant, you might be better offering a list of items in a listview or combobox, or as other answers suggest, a datagridview with button columns.

EDIT:

An example showing how to add a graphic with virtual buttons. Very basic implementation, but hopefully you will get the idea:

First, some initial variables as preferences:

int GraphicWidth = 300;
int GraphicHeight = 100;
int ButtonWidth = 60;
int ButtonHeight = 20;
Font ButtonFont = new Font("Arial", 10F);
Pen ButtonBorderColor = new Pen(Color.Black);
Brush ButtonTextColor = new SolidBrush(Color.Black);

Generating the image:

Bitmap ControlImage = new Bitmap(GraphicWidth, GraphicHeight);
using (Graphics g = Graphics.FromImage(ControlImage))
{
    g.Clear(Color.White);
    for (int x = 0; x < GraphicWidth; x += ButtonWidth)
        for (int y = 0; y < GraphicHeight; y += ButtonHeight)
        {
            g.DrawRectangle(ButtonBorderColor, x, y, ButtonWidth, ButtonHeight);
            string ButtonLabel = ((GraphicWidth / ButtonWidth) * (y / ButtonHeight) + x / ButtonWidth).ToString();
            SizeF ButtonLabelSize = g.MeasureString(ButtonLabel, ButtonFont);
            g.DrawString(ButtonLabel, ButtonFont, ButtonTextColor, x + (ButtonWidth/2) - (ButtonLabelSize.Width / 2), y + (ButtonHeight/2)-(ButtonLabelSize.Height / 2));
        }
}
pictureBox1.Image = ControlImage;

And responding to the Click event of the pictureBox:

// Determine which "button" was clicked
MouseEventArgs em = (MouseEventArgs)e;
Point ClickLocation = new Point(em.X, em.Y);
int ButtonNumber = (GraphicWidth / ButtonWidth) * (ClickLocation.Y / ButtonHeight) + (ClickLocation.X / ButtonWidth);
MessageBox.Show(ButtonNumber.ToString());


I think you won't be able to prevent flickering having 625 buttons (btw, windows) on the panel. If such layout is mandatory for you, try to use the DataGridView, bind it to a fake data source containing the required number of columns and rows and create DataGridViewButtonColumns in it. This should work much more better then your current result ...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜