Класс рамки выделения графических объектов CAxTracker

Автор: Александр Н. Костарев
R-Style Softlab
Опубликовано: 22.10.2008
Версия текста: 1.1

Цели создания и основные возможности
Концепция использования
Основные методы
Конструктор
Draw
GetEraseRegion
SetCursor
Track
События
OnChangingRect
OnChangedRect
OnGetDrawingRect
OnDrawTrackerRect
Множественное выделение
Особенности использования в ATL/WTL – проектах
Примеры промышленного использования

Исходные тексты
Демонстрационный проект

Цели создания и основные возможности

Необходимость в создании класса рамки возникла в процессе разработки дизайнера форм и отчетов поставляемого компанией “R-Style Softlab” инструментального комплекса RS-Forms, а точнее, лежащего в его основе контейнера управляющих элементов ActiveX. Рамка является основным инструментом задания положения и размеров внедряемых в ActiveX-контейнер управляющих элементов (отсюда и название класса – CAxTracker). Проект дизайнера разрабатывался (и продолжает разрабатываться) на основе библиотек ATL/WTL, что не позволяло непосредственно использовать входящий в состав MFC класс CRectTracker. К тому же хотелось, чтобы рамка выглядела более привлекательно; предоставляла возможность управления множеством активных областей – манипуляторов, определяющих набор действий над выделенным объектом; позволяла, при необходимости, легко добавлять новые стили отображения; и, главное, способствовала удобной реализации различных стратегий выравнивания элементов.


Рис. 1. Стили отображения рамки, контур рамки, TrackLines

В конечном итоге класс CAxTracker можно использовать как в ATL/WTL – проектах, так и в проектах на основе MFC; он обеспечивает возможность реализации эффекта ожидаемогоприлипания контура рамки к узлам сетки либо другим элементам при его перемещении; поддерживает несколько стилей отображения рамки (см. рис. 1), концепцию TrackLines, а также режимы учета отступов и перекрытий.

ПРИМЕЧАНИЕ

Область применения класса не ограничивается задачей размещения управляющих элементов в ActiveX-контейнере – связь с технологией ActiveX состоит исключительно в названии класса и носит исторический характер.

Ряд идей, в частности, реализация функции Track с использованием цикла выборки сообщений, были позаимствованы из упомянутого выше класса CRectTracker.

Под “ожидаемым прилипанием” подразумевается такой способ выбора точек прилипания, при котором не происходит смещения (скачков) контура рамки в сторону противоположную его движению. Без дополнительного внимания такое смещение легко может возникнуть в случае обеспечения прилипания не только верхней, левой границ, но и правой, нижней.

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

TrackLines – это линии, отражающие в процессе позиционирования контура рамки факт совпадения координат какой-либо из его границ с координатами границ графических объектов, находящихся в непосредственной близости от контура (см. рис. 1).

Режимы учета отступов и перекрытий позволяют, прозрачно для реализующего алгоритм расчета точек прилипания клиента класса, осуществлять выравнивание элементов на определенном расстоянии друг от друга.


Рис. 2. Выравнивание элементов друг относительно друга с учетом отступов

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

Режим учета перекрытий функционирует аналогично с той лишь разницей, что в нем используется не внешний, а внутренний контур. Этот режим позволяет, в частности, легко и быстро создавать аналоги табличных структур с предопределенным видом и типом границ ячеек из простых управляющих элементов (таких как Label) – например, при разработке печатных форм платежных поручений.

Включение режимов осуществляется путем нажатия и удержания соответствующих клавиш.

Концепция использования

В большинстве случаев, объект класса рамки является полем класса окна, отвечающего за отображение графических элементов и управление их расположением. Многие настройки, регулирующие обычно не требующие изменения аспекты поведения и отображения рамки, задаются через параметры конструктора. Для взаимодействия рамки с внешним окружением определен исходящий интерфейс – интерфейс событий рамки. Этот интерфейс реализуется содержащим рамку классом окна, указатель на него передается в конструктор объекта-рамки.

Множество поддерживаемых рамкой методов можно условно разделить на две группы: сервисные методы получения и задания атрибутов рамки и методы – обработчики windows-сообщений родительского класса окна, в той их части, что касается рамки. Отображение рамки, установка курсоров для активных областей и пр. организуется посредством вызова подходящих методов рамки в обработчиках соответствующих событий класса окна. Информация о размерах графического элемента, вокруг которого необходимо отобразить рамку, запрашивается объектом-рамкой через метод исходящего интерфейса.

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

В процессе позиционирования контура рамки для каждого промежуточного положения посылается специальное событие, позволяющее это положение скорректировать (и таким образом реализовать функцию выравнивания); по окончании процесса посылается еще одно событие, предназначенное для фиксации нового расположения выделенного элемента.

Основные методы

Конструктор

CAxTracker (IAxTrackerEvents* pEv = NULL, unsignedchar width = 6, unsignedshort delay = 300,
            int vkHorz = 'Q', int vkVert = 'W', int vkInner = 'E');
pEv указатель на исходящий интерфейс (см. далее)
width ширина рамки
delay задержка (в миллисекундах) первого появления контура рамки при старте процесса позиционирования
vkHorz, vkVert, vkInner виртуальные коды клавиш перехода в режим учета отступов по горизонтали, режим учета отступов по вертикали и переключения в режим учета перекрытий

Обработчик событий может быть задан и после конструирования объекта рамки – для этого предусмотрен метод IAxTrackerEvents* SetEvHandler (IAxTrackerEvents* pEv).

По умолчанию, все манипуляторы рамки являются активными, стиль отображения – TSF_APPEALBORDER (см. рис. 1). Для изменения стилей отображения и коррекции множества манипуляторов предназначен метод void SetStyle (unsigned short uStyle).

Draw

        void Draw (CDC& dc);

Метод рисует рамку в заданных стилях в контексте dc; предназначен для использования в обработчике сообщения WM_PAINT (в функции OnDraw). Размеры рамки определяются через событие void OnGetDrawingRect (/*[out]*/ CRect& rc) (см. далее).

GetEraseRegion

        void GetEraseRegion (/*[out]*/ HRGN& hRgn, unsignedshort rt = rtFull) const;

В общем случае метод возвращает занимаемый (затираемый) рамкой регион. По завершении работы с регионом его необходимо освободить посредством вызова функции Win32 API DeleteObject (HGDIOBJ hRgn).

SetCursor

        bool SetCursor (HWND hWnd, UINT hitTest) const;
bool SetCursor (TrackerHit hitTest) const;
hWnd описатель содержащего рамку окна
hitTest в первом случае – hit-test код, приходящий в сообщение WM_SETCURSOR; во втором – идентификатор области рамки

Методы предназначены для сопоставления областям рамки различных курсоров мыши; используются в обработчике сообщения WM_SETCURSOR.

Для определения области рамки, соответствующей заданным координатам, предусмотрена функция TrackerHit HitTest (const CPoint& pt) const; множество областей определяется перечислением TrackerHit, во многом повторяющим одноименное перечисление, используемое в классе CRectTracker.

Track

        bool Track (HWND hWnd, const CPoint& pt, bool bRubberBand = false,
unsignedshort OuterIndentX = 0, unsignedshort OuterIndentY = 0,
unsignedshort InnerIndentX = 0, unsignedshort InnerIndentY = 0,
bool bDraw = true) const;
hWnd описатель содержащего рамку окна
pt текущее положение курсора мыши в клиентских координатах
bRubberBand требуется отображение резинового контура без привязки к текущему положению рамки
OuterIndentX, OuterIndentY отступы внешнего track-контура по ширине и высоте
InnerIndentX, InnerIndentY отступы внутреннего track-контура по ширине и высоте
bDraw требуется ли отображение контура в процессе его позиционирования

В этом методе инкапсулирована вся работа с контуром рамки: первичное, возможно отложенное, появление контура, изменение его положения или размеров в зависимости от области, которой соответствует точка pt, переход в режим учета отступов или перекрытий.

Метод осуществляет захват мыши, запускает собственный цикл выборки сообщений, является генератором большинства событий класса; обычно используется в обработчике события WM_LBUTTONDOWN.

События

OnChangingRect

        void OnChangingRect (/*[in][out]*/ CRect& rc, CAxTracker::TrackerHit hitTest, const CRect& rcPrev,
bool bMoveLeft, bool bMoveTop, long IndentX, long IndentY,
 CAxTracker::ITrackLines* lpTrackLines);
rc новое (следующее) положение контура рамки
hitTest идентификатор области рамки
rcPrev предыдущее положение контура рамки
bMoveLeft, bMoveTop направление движения по горизонтали, по вертикали
IndentX, IndentY величины текущих отступов
lpTrackLines указатель на коллекцию TrackLines

Событие предназначено для коррекции нового положения контура рамки перед его перемещением в это положение. Именно оно обеспечивает возможность реализации эффекта прилипания контура к границам графических объектов или узлам сетки, а также формирования множества линий, показывающих равенство координат границ контура и графических элементов.

Все то множество параметров, которое передается в этот метод, служит цели более точного расчета точки прилипания в условиях наличия достаточно большого количества таких точек.

OnChangedRect

        void OnChangedRect (const CRect& rcNew);

Событие генерируется по завершении процесса позиционирования контура; предназначено для фиксации нового положения выделенного посредством рамки объекта.

OnGetDrawingRect

        void OnGetDrawingRect (/*[out]*/ CRect& rc);

Вызывается из методов Draw и Track для определения размеров графического объекта, вокруг которого необходимо отобразить рамку.

OnDrawTrackerRect

        void OnDrawTrackerRect (CDC& dc, const CRect& rc);

Генерируется перед отображением/стиранием контура рамки.

Множественное выделение

Реализовать выделение группы графических элементов (см. рис. 1) можно посредством сопоставления (через объект некоторого промежуточного класса) каждому элементу этой группы собственного ассоциированного с ним (через метод исходящего интерфейса OnGetDrawingRect) объекта класса CAxTracker.

Кроме того, можно поддержать синхронное отображение контуров рамок в процессе позиционирования одной из них – специально для этого предусмотрены событие OnDrawTrackerRect и метод DrawTrackerRect (CDC& dc, const CRect& rc).

Особенности использования в ATL/WTL – проектах

ATL/WTL – проекты, в которых предполагается использование класса CAxTracker, требуют дополнительной настройки. Необходимо:

  1. в установках препроцессора проекта объявить символ RSF_TRACKER_ATL, явно указывающий на то, что класс используется в ATL/WTL окружении;
  2. в stdafx.h подключить заголовочные файлы atlmisc.h и atlgdi.h, определяющие классы CRect, CPoint, CDC и пр., используемые в реализации CAxTracker;
  3. в ресурсы проекта добавить курсоры для манипуляторов рамки; идентификаторы курсоров должны быть определены в файле resource.h. Список идентификаторов, а также файл ресурсов с курсорами, используемыми MFC-классом CRectTracker, можно найти в проекте демонстрационного приложения.

Примеры промышленного использования

Все аспекты использования класса CAxTracker и достигаемые при этом результаты иллюстрируются дизайнером форм и отчетов, входящим в состав инструментального комплекса RS-Forms – продукта компании “R-Style Softlab”, предназначенного для создания расширяемых распределенных приложений с графическим интерфейсом пользователя. Демо-версия RS-Forms доступна по адресу: members.softlab.ru/kubrin/rsforms.htm.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.