Управление растровым изображением. Структура Windows-приложения. Развитие стартовой заготовки, страница 4

Затем добавьте обработку события создания окна (WM_CREATE) и трех мышиных событий.

case WM_CREATE: // Реакция на создание окна

{

  hpenDot = CreatePen (PS_DOT, 1, RGB(0, 0, 0));// Пунктирное перо для рисования динамического прямоугольника

  break;

}

case WM_LBUTTONDOWN: // Реакция на нажатие левой кнопки

  ClipCursor(&rClient); // Ограничиваем возможности перемещения курсора только клиентской областью окна

  ptOld.x = LOWORD (lParam); // Запоминаем текущие координаты курсора

  ptOld.y = HIWORD (lParam);

  break;

case WM_MOUSEMOVE:    // Реакция на движение курсора

{

  if (!(wParam & MK_LBUTTON))   // Если не нажата левая кнопка мыши, уходим

   return 0;

  short x = LOWORD(lParam),  y = HIWORD(lParam); // Текущие координаты курсора

  hdc = GetDC(hWnd);

   HPEN pOld = (HPEN)SelectObject (hdc, hpenDot); // Выбираем пунктирное перо в контекст устройства

  HBRUSH bOld = (HBRUSH)SelectObject (hdc, GetStockObject(NULL_BRUSH));// Выбираем прозрачную кисть

  SetROP2 (hdc, R2_XORPEN); // Инверсный режим смешивания цвета. Повторное рисование линии стирает старую линию

      if (!IsRectEmpty (&rTarget)) // Сначала стираем старый прямоугольник мишени

   Rectangle (hdc, rTarget.left, rTarget.top, rTarget.right, rTarget.bottom);

      SetRect (&rTarget, ptOld.x, ptOld.y, x, y); // Вычисляем координаты нового прямоугольника - мишени

  if (ptOld.x > x)

  {

   rTarget.left = x;

   rTarget.right = ptOld.x;

  }

  if (ptOld.y > y)

  {

   rTarget.top = y;

   rTarget.bottom = ptOld.y;

  }

      Rectangle (hdc, rTarget.left, rTarget.top, rTarget.right, rTarget.bottom); // Новая мишень

  SelectObject (hdc, pOld);     // Возвращем перо в контекст устройства

  SelectObject (hdc, bOld);     // Возвращем кисть

  ReleaseDC(hWnd, hdc);

  break;

}

case WM_LBUTTONUP:    // Реакция на отпускание левой кнопки

ClipCursor (0);                 // Освобождаем курсор

break;

При закрытии окна следует освобождать динамические ресурсы Windows:

case WM_DESTROY:             // Освобождаем ресурсы

  DeleteObject (hpenDot);

  PostQuitMessage (0);

  break;

Функция ClipCursor работает с экранными координатами прямоугольника, который изменяется как при перетаскивании окна, так и при изменении его размеров. Введите код определения и коррекции его координат.

  case WM_MOVE:

  case WM_SIZE:  // Узнаем и преобразовываем координаты клиентской области окна

   GetClientRect(hWnd, &rClient);

   break;

Запустите приложение и тщательно проверьте его работу.

¨  Объясните поведение прямоугольника rTarget.

¨  Временно закомментируйте один из вызовов функции Rectangle и объясните поведение прямоугольника.

¨  Временно закомментируйте установку нулевой кисти (и восстановление старой) и объясните поведение.

¨  Пронаблюдайте эффект ограничения движения курсора при нажатой кнопке мыши.

¨  Измените размеры и положение окна на экране и вновь проверьте функционирование как ограничителя курсора, так и динамического прямоугольника.

Объяснения

¨  Поведение динамического прямоугольника мы объяснили ранее. Работает алгоритм: рисуй-стирай.

¨  Отмена нулевой кисти включает кисть по-умолчанию. До этой отмены нулевая кисть не позволяла закрашивать внутренность прямоугольника кистью по-умолчанию. Кисть по-умолчанию—белая, но так как во время закраски действует растровая операция XOR, результат ее применения выглядит черным.

¨  Ограничитель курсора перестает правильно работать потому, что мы не учитываем изменений экранных координат (при изменении позиции всего окна или его размеров). Для коррекции этого недостатка добавьте следующие исправления. Их нужно поместить в общую ветвь обработки сообщений WM_MOVE и WM_SIZE. После этого проверьте работу ограничителя.

   POINT ptLT = { rClient.left, rClient.top };

   POINT ptRB = { rClient.right, rClient.bottom };

   ClientToScreen (hWnd, &ptLT);

   ClientToScreen (hWnd, &ptRB);

   SetRect (&rClient, ptLT.x, ptLT.y, ptRB.x, ptRB.y);