Иллюстрированный самоучитель по Visual Studio .NET

Визуальное редактирование данных

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

Современным подходом к редактированию данных является использование таблиц (grids) типа Excel, в которых отражены данные открытого документа и которые позволяют редактировать их, мгновенно получая обратную связь в виде изменившейся геометрии устройства. Таблицы удобно разместить на одной из панелей расщепленного окна с регулируемой перегородкой (split bar).

К сожалению, в MFC нет классов, поддерживающих функционирование таблиц. Реализация их в виде внедряемых СОМ-объектов обладает рядом недостатков. Во-первых, существующие grid-элементы обладают весьма ограниченными возможностями. Во-вторых, интерфейсы обмена данными между внедренной (embedded) таблицей и приложением-контейнером громоздки и неуклюжи. Самым лучшим, известным автору, решением этой проблемы является использование библиотеки классов objective Grids, разработанных компанией stingray Software. Библиотека полностью совместима с MFC. В ней есть множество классов, поддерживающих работу разнообразных элементов управления: combo box, check box, radio button, spinner, progress и др. Управление grid-элементами или окнами типа CGXGridWnd на уровне исходных кодов дает полную свободу в воплощении замыслов разработчика.

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

Изменение координат вершин полигона в диапазоне, ограниченном размерами логической области (2000x2000), можно производить простым перетаскиванием его вершин с помощью указателя мыши. Чтобы намекнуть пользователю нашего приложения о возможности произведения таких операций (вряд ли он будет читать инструкцию), мы используем стандартный прием, заключающийся в изменении формы курсора в те моменты, когда указатель мыши находится вблизи характерных точек изображения. Это те точки, которые можно перетаскивать. В нашем случае – вершины полигона. Очевидной реакцией на курсор в виде четырех перекрещенных стрелок является нажатие левой кнопки и начало перетаскивания. Заканчивают перетаскивание либо отпусканием кнопки мыши, либо повторным ее нажатием. Во втором варианте при перетаскивании не обязательно держать кнопку нажатой. Остановимся именно на нем.

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

Каждое положение перемещаемой линии рисуется дважды. Первый раз линия рисуется, второй – стирается. Этот эффект достигается благодаря предварительной настройке контекста устройства, которую производит функция SetROP2. Если вызвать ее с параметром R2_xoRPEN, то рисование будет происходить по законам логической операции XOR (исключающее ИЛИ). В булевой алгебре эта операция имеет еще одно имя – сложение по модулю два. Законы эти просты: 0+0=0; 0+1 = 1; 1+0=1; 1 + 1=0. Ситуацию повторного рисования можно представить так:

  • цвет каждого пиксела (каждой точки растра) при рисовании определяется путем суммирования цвета фона и цвета пера по законам операции XOR;
  • если перо красное (8 младших бит цвета установлены в 1), а фон белый (то есть присутствуют все 3 компонента цвета – 3 байта установлены в 1), то результатом операции XOR будет цвет Cyan, так как красный компонент исчезнет (1+1=0). Оставшиеся же компоненты, зеленый и синий, дают цвет Cyan;
  • если еще раз пройтись красной линией по тому же месту (по линии цвета Cyan), то при сложении цветов единицы попадут на нули и цвет будет белый (все 3 байта станут равны 1).

Итак, повторный проход стирает линию. В качестве упражнения повторите выкладки при условии, что перо белое (затем – черное). Такие упражнения шлифуют самое главное качество программиста – упорство. При черном пере вы должны получить что-то не то. Тем не менее мы берем черное перо, но при этом задаем стиль PS_DOT, что в принципе равносильно черно-белому перу. Белые участки работают как описано, а черные своей инертностью помогают создать довольно интересный эффект переливания пунктира или эффект натягивания и сжимания резинки. Есть еще одно значение (К2_ыот) параметра функции SetROP2, которое работает успешно, но не без эффекта резинки.

Примечание
Я думаю, что цифра 2 в имени функции означает намек на фонетическую близость английских слов "two" и "to". Если предположение верно, то имя функции SetROP2 можно прочесть как "Set Raster Operation To", что имеет смысл установки режима растровой операции в положение (значение), заданное параметром функции. Обязательно просмотрите справку по этой функции (методу класса CDC), для того чтобы узнать ваши возможности при выборе конкретного режима рисования
.

Режим перетаскивания вершин полигона готов к использованию в момент вхождения указателя мыши в область чувствительности вершины (за этим следит флаг m_bReady). Кроме данного режима мы реализуем еще один режим – режим создания нового полигона (флаг m_bNewPoints), который вступает в действие при выборе команды меню Edit › New Poly. При анализе кода обратите внимание на то, что мы получаем от системы координаты точек в аппаратной системе, а запоминать в контейнере точек должны мировые (World) координаты. Преобразование координат осуществляется в два этапа:

  • сначала из Device-пространства в пространство Page (функция DPtoLP – Device Point to Logical Point);
  • затем из Page-пространства в пространство World (наша функция MapToWorldPt).
Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.