Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
03.11.13 / Компоненты WinForms.docx
Скачиваний:
40
Добавлен:
08.06.2015
Размер:
100.19 Кб
Скачать

Придерживайтесь стандартов

Используйте хорошо известные имена для свойств и методов. Например, GetItemAt вместо PointToItem, BackColor вместо BackgroundColor. Если в Windows Forms найдутся аналогичные методы, используйте их названия. Помните, что привычки пользователя важнее, чем синтаксическая правильность, а пользователи чаще сталкиваются со стандартными контролами, нежели с вашими.

Придерживайтесь стандартного стиля для описания делегатов:

public delegate void SomeEventHandler(object sender, SomeEventArgs e);

public class SomeEventArgs : EventArgs

{

private int _intValue;

private string _stringValue;

public SomeEventArgs(int intValue, string stringValue)

{

_intValue = intValue;

_stringValue = stringValue;

}

public int IntValue { get { return _intValue; } }

public string StringValue { get { return _stringValue; } }

}

Для инициации события используйте защищенный виртуальный метод

public event SomeEventHandler Some;

protected virtual void OnSome(SomeEventArgs e)

{

if (Some != null)

Some(this, e);

}

Взаимодействуйте с операционной системой касательно предпочтений пользователя. Используйте системные шрифты, цвета и другие настройки в качестве значений по умолчанию. При необходимости, подпишитесь на событие Microsoft.Win32.SystemEvents.UserPreferenceChanged и обновляйте кэшированные значения для Brush, Pen, Font, используя статические свойства класса SystemInformation. Перегрузите OnSystemColorsChanged для той же цели. Некоторые классы .NET Framework самостоятельно отслеживают эти вещи, например SolidBrush, созданный на базе константы, соответствующей предопределенному системному цвету, будет поддерживать себя в актуальном состоянии, а вот LinearGradientBrush не будет этого делать.

Оптимизируйте

При разработке контролов можно применять некоторые стандартные оптимизации, перечисленные ниже. Однако помните, что лучшим критерием для принятия решения об оптимизации является измерение и оценка.

Создание Brush, Pen является относительно дорогостоящей операцией, а изменение свойств контрола, влияющих на них, происходит достаточно редко. Поэтому вместо создания таких объектов прямо в OnPaint, храните соответствующие Brush, Pen и другие аналогичные данные в классе вместе с переменными, хранящими соответствующие цвет, толщину и другие параметры. Меняйте их синхронно с изменением соответствующих свойств. Не забывайте вызывать Dispose для таких объектов при смене свойств и в Dispose самого контрола. Поскольку до создания Handle контрола такие данные вряд ли понадобятся, разумно создавать эти кэширующие объекты в переопределенном методе CreateHandle. Схему работы с такими данными можно изобразить так:

public class MyControl : Control

{

private Color _someColor;

private Brush _someBrush;

public Color SomeColor

{

get { return _someColor; }

set

{

if (_someColor == value)

return;

if (IsHandleCreated)

_someBrush.Dispose();

_someColor = value;

if (IsHandleCreated)

_someBrush = new SolidBrush(_someColor);

}

}

protected override void CreateHandle()

{

base.CreateHandle();

_someBrush = new SolidBrush(_someColor);

}

protected override void Dispose(bool isDisposing)

{

if (isDisposing && IsHandleCreated)

{

_someBrush.Dispose();

}

base.Dispose(isDisposing);

}

}

Унаследованные свойства вроде BackColor можно переопределить (override), чтобы поддерживать кэш для Brush.

Не используйте двойную буферизацию, если структура вашего контрола позволяет отрисовать его таким образом, чтобы каждая точка была выставлена ровно один раз. Например, простейший ProgressBar может быть отрисован двумя прямоугольниками. В таком случае, установите стиль ControlStyles.AllPaintingInWmPaint, при этом игнорируется сообщение Windows об очистке фона окна, и метод OnPaintBackground не вызывается.

Устанавливайте соответствующие стили контрола до создания его окна, например, чтобы избавиться от мерцания в сложном контроле, можно использовать такие стили:

SetStyle(

ControlStyles.DoubleBuffer

| ControlStyles.AllPaintingInWmPaint

| ControlStyles.UserPaint, true);

Используйте специальные методы классов Graphics, ControlPaint где возможно. Например, DrawImageUnscaled быстрее простого DrawImage. Если вы рисуете элементы ImageList, используйте метод ImageList.Draw(...) вместо Graphics.DrawImage(_imageList.Images[0], ...) – это не только быстрее, но и единственный способ отрисовки иконку (Icon) без потери альфа-канала.

Если в вашем компоненте много событий, можно использовать встроенную в класс Component возможность унификации их обработки. Защищенное свойство Events поддерживает словарь событий и позволяет манипулировать событиями единообразно. Кроме того, если пользователь подписывается лишь на некоторые из них (а это самый частый вариант), то неиспользуемые события не будут занимать место в памяти. С другой стороны, на каждое использованное событие расходуется примерно в три раза больше памяти, чем если бы делегат был просто членом класса, поэтому если событий мало, или если часто используется больше трети событий, то овчинка не стоит выделки. Пример использования такой техники показан ниже:

// создаем уникальный ключ для нашего события

private readonly static object EventDataChanged = new object();

public event EventHandler DataChanged

{

add

{

// добавить обработчик в общую таблицу

Events.AddHandler(EventDataChanged, value);

}

remove

{

// удалить обработчик из общей таблицы

Events.RemoveHandler(EventDataChanged, value);

}

}

protected virtual void OnDataChanged(EventArgs e)

{

// получить обработчик из таблицы

EventHandler eventHandler = (EventHandler)Events[EventDataChanged];

// если есть подписчики, то оповестить их

if (eventHandler != null)

eventHandler(this, e);

}

Обработка действий пользователя

Пользователь – это наименее предсказуемый элемент системы, и в то же время наиглавнейший из всех. В конце концов, именно ради пользователя вы создаете свой компонент. Успех ваших трудов определяется двумя основными факторами. Первый – удобство конечного пользователя, определяющий фактор при выборе вашего контрола программистом. Второй – удобство программиста, который будет пытаться заставить ваш контрол вести себя так, как ему нужно, опять же в интересах конечного пользователя. О программистах мы поговорим в следующем разделе, посвященном поддержке дизайнера форм и редактора, а сейчас рассмотрим вопросы, связанные с действиями и ожиданиями пользователя.

В этом аспекте одним из важнейших моментов является сохранение правильного состояния контрола. Необходимо поддерживать внутренне непротиворечивое состояние при любой последовательности поступающих событий. Пользователь может нажать клавишу и, не отпуская её, кликнуть мышкой, или нажать левую кнопку мыши, затем кликнуть правой или провернуть колесико, и лишь затем отпустить левую кнопку. Во всех таких случаях ваш контрол должен среагировать некоторым непротиворечивым способом. В простейшем случае при определении «странного» действия можно сбросить все режимы и, во всяком случае, не сделать ничего плохого. В этом вам поможет информация о согласованном состоянии компонента, собранная при анализе и проектировании.

Одно из важнейших общих правил при описании взаимодействия с пользователем – это отделение обработки ввода от собственно действия. Не стоит сооружать огромных switch-case конструкций с логикой внутри OnKeyDown или OnMouseUp. В этих функциях надо лишь определиться, какое действие должно быть выполнено, и вызвать соответствующий метод, который произведет необходимые изменения. Например, при нажатии стрелочки вверх можно вызвать защищенный виртуальный метод MoveUp и всю логику перемещения курсора вверх произвести в нём. Впоследствии, можно будет изменять логику поведения контрола независимо от обработки действий пользователя. Кроме того, наследник вашего класса сможет переопределить поведение естественным образом.

Соседние файлы в папке 03.11.13