Медленное добавление элементов управления в форму

Почему краска так долго держится?

public Form1()
    {
        InitializeComponent();
        SuspendLayout();
        double scale = ClientSize.Width / 11;
        for (int i = 1; i < 10; i++)
        {
            for (int j = 1; j < 10; j++)
            {
                everybox[i - 1, j - 1] = new TextBox
                                             {
                                                 Location = new Point((int)(scale * i), (int)(scale * j)),
                                                 Size = new Size((int)scale - 2, (int)scale - 2),
                                                 Multiline = true
                                             };
                Controls.Add(everybox[i - 1, j - 1]);
            }
        }
        ResumeLayout();
    }


private void Form1_Paint(object sender, PaintEventArgs e)
    {
        float scale = ClientSize.Width / 11;
        Graphics g = this.CreateGraphics();
        int counter = 0;
        for (float i = scale; i <= this.ClientSize.Width - scale; i += scale)
        {
            counter++;
            if ((counter - 1) % 3 != 0)
            {
                g.DrawLine(new Pen(Color.Black), new Point((int)i, (int)scale),
                           new Point((int)i, ClientSize.Width - (int)scale));
                g.DrawLine(new Pen(Color.Black), new Point((int)scale, (int)i),
                           new Point(ClientSize.Width - (int)scale, (int)i));
            }
            else
            {
                g.DrawLine(new Pen(Color.Black, 3f), new Point((int)i, (int)scale),
                           new Point((int)i, ClientSize.Width - (int)scale));
                g.DrawLine(new Pen(Color.Black, 3f), new Point((int)scale, (int)i),
                           new Point(ClientSize.Width - (int)scale, (int)i));
            }
        }
    }

Это довольно раздражает и вызывает заметное отставание. Everybox — это объект TextBox[9,9].


person soandos    schedule 09.08.2011    source источник
comment
Изменение его на e.graphics делает его мгновенным. Спасибо.   -  person soandos    schedule 10.08.2011
comment
Я изменил свой комментарий на ответ ниже.   -  person LarsTech    schedule 10.08.2011
comment
Спасибо. Он звонил еще много раз. Теперь функция рисования даже не отображается в профилировщике как интенсивная функция, спасибо. Я приму это как ответ, если вы опубликуете его.   -  person soandos    schedule 10.08.2011


Ответы (4)


Согласно моему комментарию, измените:

Graphics g = this.CreateGraphics();

to

e.Graphics
person LarsTech    schedule 09.08.2011
comment
Мне жаль. Мне казалось, что мое событие рисования занимает много времени моего процессора. Казалось, его звали много-много раз. Я отредактирую свой вопрос. - person soandos; 10.08.2011

Возможная причина в том, что вы пытаетесь нарисовать слишком много тяжелых компонентов. Если моя математика верна, вы перерисовываете 9 * 9 * 9 * 9 = 6561 объект. WinForms не предназначены для эффективной перерисовки такого количества компонентов.

Возможно, вам придется подумать, действительно ли вам нужно использовать такое количество тяжеловесных графических компонентов с WinForms. Могут быть более легкие компоненты, или вы можете переключиться на XNA (который имеет камеру, виды и т. д. — все это уменьшает количество объектов, которые необходимо перерисовать) или WPF в зависимости от контекста.

person oleksii    schedule 09.08.2011
comment
XNA, конечно, для разработки игр. WPF был бы лучшим эквивалентом, который, например, поддерживает интерактивные элементы управления. - person Kieren Johnstone; 10.08.2011
comment
Я не понимаю. Я рисую 81 текстовое поле и 121 строку. Когда я делаю каждую по отдельности, они не занимают предсказуемого времени. Когда я делаю их вместе, это занимает около полсекунды. - person soandos; 10.08.2011
comment
Хотя это для игры, это судоку, а не то, что я обычно называю интенсивной графикой. Я действительно просто хочу знать, почему событие рисования возникает более двух раз. - person soandos; 10.08.2011
comment
Событие рисования @soandos AFAIK постоянно вызывается в цикле для перерисовки всей графики в окне. Может быть, попробовать быстрый сеанс профилирования, чтобы увидеть, что ест ваш процессор? - person oleksii; 10.08.2011
comment
Я сделал. Это функция рисования (~ 95% или около того). Точнее, это тело функции рисования, которая перерисовывает все линии. Это происходит более 100 раз. Почему? - person soandos; 10.08.2011
comment
Соответствующий код для судоку WinForms, возможно, посмотрите здесь: codeproject.com/KB/cs/ OOSudokuSolver.aspx - person oleksii; 10.08.2011
comment
Мне не нужен решатель судоку, я просто хочу получить ответ на этот вопрос. А именно, почему функция рисования вызывается более 100 раз? - person soandos; 10.08.2011
comment
Какое максимальное значение для counter? - person oleksii; 10.08.2011
comment
@oleski: событие рисования срабатывает только при перерисовке элемента управления, что происходит только тогда, когда есть причина его перерисовать. Не в петле. - person Igby Largeman; 10.08.2011
comment
Около 170 или около того. Трудно получить точное количество, так как каждый раз, когда он разворачивается (это VS 2010), он перерисовывается. - person soandos; 10.08.2011
comment
@soandos попробуйте рисовать линии в других событиях OnPaint, а не в форме, как здесь - person oleksii; 10.08.2011
comment
К событию краски чего именно? Я могу сделать это, когда рисую форму (как я делаю сейчас), или я могу создать панель только для этого. - person soandos; 10.08.2011

Код, который вы разместили, вызывает событие рисования 81 раз (9 * 9). Один раз для каждого элемента управления, добавляемого в форму. Большее количество раз связано с чем-то, что делает форму недействительной, например, с перемещением мыши по ней, перемещением другого окна поверх нее или изменением размера формы. Некоторый код, который вы нам не показываете, может быть ответственным.

person Igby Largeman    schedule 09.08.2011
comment
Нет на самом деле, это не так. Кроме того, все элементы управления добавляются без вызова единого события рисования. Другого кода нет (пустое событие загрузки). - person soandos; 10.08.2011
comment
@soandos: это так. Событие рисования не будет вызвано при добавлении элементов управления, потому что вы делаете это в конструкторе до создания окна. Событие рисования будет вызвано, когда вы покажете форму. В это время он будет вызван 81 раз. После этого он будет вызываться снова каждый раз, когда форма становится недействительной. - person Igby Largeman; 10.08.2011

Paint определенно может вызываться очень часто, и если вы получаете слишком много вызовов, это, вероятно, не имеет ничего общего с этим фрагментом кода. Одна вещь, которая поможет производительности этого конкретного бита, - это попытаться уменьшить объем работы, которую вы выполняете...

    Graphics g = e.Graphics;
    Pen bp = new Pen(Color.Black, 3f);
    Point start = new Point(0,0);
    Point stop = new Point(0,0);

    for (float i = scale; i <= this.ClientSize.Width - scale; i += scale)
    {
        int iAsInt = (int)i;
        int scaleAsInt = (int)scale;
        int w = ClientSize.Width;
        counter++;

        if ((counter - 1) % 3 != 0)
        {
            start.X = iAsInt;
            start.Y = scaleAsInt;
            stop.X = iAsInt;
            stop.Y = w-scaleAsInt;
            g.DrawLine(Pens.Black, start, stop);
            start.X = scaleAsInt;
            start.Y = iAsInt;
            stop.X = w-scaleAsInt;
            stop.Y = iAsInt;
            g.DrawLine(Pens.Black, start, stop);
            // Note: this looks like more work, but it is actually less
            // your code still has to make all the assignments in addition to 
            // newing up the points (and later having to garbage collect them)
        }
        else
        {
            // TODO: reuse the start/stop points here
            g.DrawLine(bp, new Point(iAsInt, scaleAsInt), new Point(iAsInt, w - scaleAsInt);
            g.DrawLine(bp, new Point(scaleAsInt, iAsInt), new Point(w - scaleAsInt, iAsInt));
        }
    }

Чтобы специально остановить перерисовку ваших линий, посмотрите на член ClipRectangle PaintEventArgs. Если часть вашей линии попадает в область прямоугольника отсечения, перерисуйте ее.

person µBio    schedule 09.08.2011
comment
Простите, а как это предотвращает многократное рисование линий? Как это сокращает количество вызовов ничьей? - person soandos; 10.08.2011
comment
В моем первоначальном ответе этого не было, но я пытался помочь с вашей проблемой задержки. Я добавил немного в конце, конкретно касающийся вашей проблемы с перерисовкой. - person µBio; 10.08.2011
comment
Но это то же количество команд. Просто математика проще. - person soandos; 10.08.2011