Как правильно использовать OnPaint в приложениях .Net?

Иногда мне нужен пользовательский внешний вид элемента управления. Или сделать много заказной живописи. Я знаю, что могу сделать это с помощью OnPaint (см.: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.onpaint.aspx)

Каков правильный способ пользовательского рисования в вашем приложении .net с помощью OnPaint? Существуют ли какие-либо правила, которые я должен учитывать, чтобы оптимизировать приложение и свести к минимуму время рендеринга?

Примечание. Время от времени я видел и сталкивался с множеством неэффективных способов использования OnPaint, поэтому я создал эти вопросы и ответы.


person Mike de Klerk    schedule 30.11.2012    source источник
comment
Я вижу, что многие люди делают это неправильно. Зачем закрывать этот вопрос?   -  person Mike de Klerk    schedule 01.12.2012


Ответы (1)


Чтобы эффективно использовать OnPaint, вам нужно знать пару вещей:

  • OnPaint элемента управления, например. из Form1, выполняется каждый раз, когда элемент управления рисуется (духх...)
  • The OnPaint of Form1 is executed everytime a child control of Form1 is drawn. e.g. If you draw a dot in the upper right corner of Form1 using the OnPaint of Form1, while you have 150 child controls on Form1, the dot will be drawn at least 150 times! It increases render time drastically. Especially if you do alot of custom drawing and calculations in the OnPaint.
    • So as a rule you must never have any logic in the OnPaint of a control, when that control has one or more child controls. Instead you should make a custom control, which holds no more child controls on it, that does the paint job. And place that as a child control on the parent control on the location where the custom drawing is needed.
    • Всякий раз, когда элемент управления добавляется к родителю, родитель будет перерисовываться. Если бы вы разместили много элементов управления на другом элементе управления, например. большой набор результатов с флажками на Form1, вы должны использовать Form1.SuspendLayout() (см.: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.suspendlayout.aspx), прежде чем добавлять дочерние элементы управления. И Form1.ResumeLayout(), когда вы закончите добавлять элементы управления. Это временно подавляет событие OnPaint и уменьшает время рендеринга.
    • Прозрачность всегда увеличивает время рендеринга.
    • Размещение компонентов таким образом, чтобы между ними не было фона, уменьшает количество OnPaint событий в родительском элементе управления. Например. поместите 4 текстовых поля друг под другом так, чтобы они касались друг друга. Таким образом, между ними нет фона, и все элементы управления рисуются в одном событии OnPaint вместо 4 событий OnPaint. Конечно, это не всегда возможно, так как вы не хотите склеивать все компоненты рядом. Но это стоит сделать, если производительность важнее внешнего вида, например, в какой-то большой пользовательской «сетке данных».
    • НИКОГДА не меняйте местоположение или размер элемента управления в событии OnPaint, так как это вызывает новые события OnPaint. Если вам необходимо переместить/изменить размер элементов управления, вам придется добавить это где-то еще в вашем коде до того, как будет вызван OnPaint. Например, поместите код перемещения/изменения размера в OnLayout или OnResize или подобные события. Если вы все еще думаете, что должны поместить этот код перемещения/изменения размера в событие OnPaint, чтобы ваше приложение работало, то вы ошибаетесь, и вам нужно пересмотреть логику вашего кода.
    • Подумайте дважды о System.Math.Pow(2, 2), прежде чем вызывать Refresh() для элемента управления за пределами своего собственного класса. Если у вас есть желание вызвать Refresh, вам, вероятно, нужны новые события и обработчики событий, чтобы синхронизироваться с тем, что вы хотите отобразить. Это также относится к Invalidate().
    • Чтобы проверить, эффективно ли вы рисуете, вы можете сделать следующее. 1. Откройте ваше приложение. 2. Поместите точку останова на OnPaint в верхней части родительского элемента. 3. Разверните окно, чтобы оно закрывало ваше приложение. 4. Снова сверните окно, и ваше приложение будет перерисовывать элемент за элементом. Если что-то нарисовано двояко, вы допустили ошибку в логике своего приложения.

Ну, я думаю, это все, если что-то придет в голову, что я забыл, я обновлю этот Q&A. Если я что-то забыл или ошибся, буду рад принять это к сведению!

Надеюсь, это даст кому-то преимущество в использовании пользовательских материалов для рисования в .Net, так как я искал эту информацию некоторое время назад.

person Mike de Klerk    schedule 30.11.2012