如何在c#winform中将面板拆分为可点击的细分?

时间:2018-08-20 17:42:56

标签: c# winforms performance runtime panel

我正在尝试使用c#模拟LED显示板。我需要一个control,其中包含1536个可单击的control来模拟LED(宽度为96,高度为16)。我为此使用了一个名为panel的{​​{1}},用户将在运行时添加1536个微小的自定义pnlContainer。这些自定义的panel应该在运行时通过click事件更改其颜色。一切正常。但是,将如此数量的微小panel添加到容器需要花费很长时间(大约10秒)。您对解决此问题有何建议?任何提示表示赞赏。

这是我的习惯panel

panel

这是一段向public partial class LedPanel : Panel { public LedPanel() { InitializeComponent(); } protected override void OnPaint(PaintEventArgs pe) { base.OnPaint(pe); } protected override void OnMouseDown(MouseEventArgs e) { if (e.Button == MouseButtons.Left) { if (this.BackColor == Color.Black) { this.BackColor = Color.Red; } else { this.BackColor = Color.Black; } } } } 添加微小的panel的代码:

pnlContainer

application is very slow at runtime

是否有更好的方法或更好的private void getPixels(Bitmap img2) { pnlContainer.Controls.Clear(); for (int i = 0; i < 96; i++) { for (int j = 0; j < 16; j++) { Custom_Controls.LedPanel led = new Custom_Controls.LedPanel(); led.Name = i.ToString() + j.ToString(); int lWidth = (int)(pnlContainer.Width / 96); led.Left = i * lWidth; led.Top = j * lWidth; led.Width = led.Height = lWidth; if (img2.GetPixel(i, j).R>numClear.Value) { led.BackColor = Color.Red; } else { led.BackColor = Color.Black; } led.BorderStyle = BorderStyle.FixedSingle; pnlContainer.Controls.Add(led); } } } 而非control来做到这一点?

2 个答案:

答案 0 :(得分:2)

我同意@TaW的建议。不要在表单上放置1000多个控件。使用某种数据结构,例如数组,以跟踪需要点亮的LED,然后在Panel的Paint事件中绘制它们。

这是一个例子。在面板上放置一个面板,并将其命名为ledPanel。然后使用类似于以下代码。我只是随机设置布尔数组的值。您需要根据鼠标的点击适当地设置它们。我没有包含该代码,但是基本上,您需要单击鼠标的位置,确定需要设置(或取消设置)哪个数组条目,然后使面板无效,以便其重绘。

public partial class Form1 : Form
{
    //set these variables appropriately
    int matrixWidth = 96;
    int matrixHeight = 16;

    //An array to hold which LEDs must be lit
    bool[,] ledMatrix = null;

    //Used to randomly populate the LED array
    Random rnd = new Random();

    public Form1()
    {
        InitializeComponent();

        ledPanel.BackColor = Color.Black;

        ledPanel.Resize += LedPanel_Resize;

        //clear the array by initializing a new one
        ledMatrix = new bool[matrixWidth, matrixHeight];

        //Force the panel to repaint itself
        ledPanel.Invalidate();
    }

    private void LedPanel_Resize(object sender, EventArgs e)
    {
        //If the panel resizes, then repaint.  
        ledPanel.Invalidate();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        //clear the array by initializing a new one
        ledMatrix = new bool[matrixWidth, matrixHeight];

        //Randomly set 250 of the 'LEDs';
        for (int i = 0; i < 250; i++)
        {
            ledMatrix[rnd.Next(0, matrixWidth), rnd.Next(0, matrixHeight)] = true;
        }

        //Make the panel repaint itself
        ledPanel.Invalidate();
    }

    private void ledPanel_Paint(object sender, PaintEventArgs e)
    {
        //Calculate the width and height of each LED based on the panel width
        //and height and allowing for a line between each LED
        int cellWidth = (ledPanel.Width - 1) / (matrixWidth + 1);
        int cellHeight = (ledPanel.Height - 1) / (matrixHeight + 1);

        //Loop through the boolean array and draw a filled rectangle
        //for each one that is set to true
        for (int i = 0; i < matrixWidth; i++)
        {
            for (int j = 0; j < matrixHeight; j++)
            {
                if (ledMatrix != null)
                {
                    //I created a custom brush here for the 'off' LEDs because none
                    //of the built in colors were dark enough for me. I created it
                    //in a using block because custom brushes need to be disposed.
                    using (var b = new SolidBrush(Color.FromArgb(64, 0, 0)))
                    {
                        //Determine which brush to use depending on if the LED is lit
                        Brush ledBrush = ledMatrix[i, j] ? Brushes.Red : b;

                        //Calculate the top left corner of the rectangle to draw
                        var x = (i * (cellWidth + 1)) + 1;
                        var y = (j * (cellHeight + 1) + 1);

                        //Draw a filled rectangle
                        e.Graphics.FillRectangle(ledBrush, x, y, cellWidth, cellHeight);
                    }
                }
            }
        }
    }

    private void ledPanel_MouseUp(object sender, MouseEventArgs e)
    {
        //Get the cell width and height
        int cellWidth = (ledPanel.Width - 1) / (matrixWidth + 1);
        int cellHeight = (ledPanel.Height - 1) / (matrixHeight + 1);

        //Calculate which LED needs to be turned on or off
        int x = e.Location.X / (cellWidth + 1);
        int y = e.Location.Y / (cellHeight + 1);

        //Toggle that LED.  If it's off, then turn it on and if it's on, 
        //turn it off
        ledMatrix[x, y] = !ledMatrix[x, y];

        //Force the panel to update itself.
        ledPanel.Invalidate();
    }
}

我确定此代码可以进行很多改进,但是应该可以使您了解如何执行此操作。

答案 1 :(得分:0)

@Chris和@ user10112654是正确的。 这是类似于@Chris的代码,但将显示逻辑隔离在单独的类中。 (@Chris在我编写代码时回答了您的问题:))))
只需创建一个2D数组来初始化类并将其传递给Initialize方法即可。

    public class LedDisplayer
    {
        public LedDisplayer(Control control)
        {
            _control = control;
            _control.MouseDown += MouseDown;
            _control.Paint += Control_Paint;

            // width and height of your tiny boxes
            _width = 5;
            _height = 5;

            // margin between tiny boxes
            _margin = 1;
        }

        private readonly Control _control;
        private readonly int _width;
        private readonly int _height;
        private readonly int _margin;
        private bool[,] _values;

        // call this method first of all to initialize the Displayer
        public void Initialize(bool[,] values)
        {
            _values = values;
            _control.Invalidate();
        }

        private void MouseDown(object sender, MouseEventArgs e)
        {
            var firstIndex = e.X / OuterWidth();
            var secondIndex = e.Y / OuterHeight();
            _values[firstIndex, secondIndex] = !_values[firstIndex, secondIndex];
            _control.Invalidate(); // you can use other overloads of Invalidate method for the blink problem
        }

        private void Control_Paint(object sender, PaintEventArgs e)
        {
            if (_values == null)
                return;

            e.Graphics.Clear(_control.BackColor);
            for (int i = 0; i < _values.GetLength(0); i++)
                for (int j = 0; j < _values.GetLength(1); j++)
                    Rectangle(i, j).Paint(e.Graphics);
        }

        private RectangleInfo Rectangle(int firstIndex, int secondIndex)
        {
            var x = firstIndex * OuterWidth();
            var y = secondIndex * OuterHeight();
            var rectangle = new Rectangle(x, y, _width, _height);

            if (_values[firstIndex, secondIndex])
                return new RectangleInfo(rectangle, Brushes.Red);

            return new RectangleInfo(rectangle, Brushes.Black);
        }

        private int OuterWidth()
        {
            return _width + _margin;
        }

        private int OuterHeight()
        {
            return _height + _margin;
        }
    }

    public class RectangleInfo
    {
        public RectangleInfo(Rectangle rectangle, Brush brush)
        {
            Rectangle = rectangle;
            Brush = brush;
        }

        public Rectangle Rectangle { get; }
        public Brush Brush { get; }

        public void Paint(Graphics graphics)
        {
            graphics.FillRectangle(Brush, Rectangle);
        }
    }

这是以下形式的用法:

    private void button2_Click(object sender, EventArgs e)
    {
        // define the displayer class 
        var displayer = new LedDisplayer(panel1);

        // define the array to initilize the displayer
        var display = new bool[,]
        {
            {true, false, false, true },
            {false, true, false, false },
            {false, false, true, false },
            {true, false, false, false }
        };

        // and finally
        displayer.Initialize(display);
    }