электроника & программирование музыкальные устройства на микроконтроллерах

К оглавлению

Создание нестандартных окон в Windows

Первый способ создания круглого окна

RndWnd1

Большинство настольных графических приложений в MS Windows строятся на базе стандартных прямоугольных окон. Но Win32 API обладает средствами, которые способны превратить стандартное прямоугольное окно в нестандартное. Форма и функциональность такого окна будет зависеть только от фантазии разработчика. В данном разделе рассматривается пример создания круглого окна. Будет использоваться среда разработки Visual Studio.

Создание проекта

Первое, что нужно сделать - это создать проект Win32 в Visual Studio. Для запуска мастера создания проектов выполняем команду Файл->Создать->Проект. В мастере выбираем тип проекта Visual C++->Win32->Проект Win32, задаем имя проекта RndWnd1 и следуем дальнейшим указаниям. В результате работы мастера будут созданы начальные файлы проекта Win32.

Создание окна

Зададим диаметр окна равным 100 пикселям. Для этого в файле RndWnd1.cpp отпределим константу:

#define MAIN_WIN_SIZE 100

Обычно стандартное главное окно приложения имеет строку заголовка, границу, системное меню, кнопку минимизации, кнопку максимизации, а также строку меню. В круглом окне придется отказаться от этих элементов, так как они будут обрезаны. Для отключения строки заголовка, границы, системного меню, кнопки минимизации и кнопки максимизации определим стиль главного окна как WS_POPUP. Для этого внесем изменения в сгенерированный мастером код функции InitInstance в файле RndWnd1.cpp:

hWnd = CreateWindow(szWindowClass, szTitle, WS_POPUP, 10, 10, MAIN_WIN_SIZE, MAIN_WIN_SIZE, NULL, NULL, hInstance, NULL);

Для отключения строки меню необходимо указать, что класс главного окна не содержит меню. Внесём изменения в сгенерированный мастером код функции MyRegisterClass в файле RndWnd1.cpp:

wcex.lpszMenuName = NULL;

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

HRGN hMainWndRgn = CreateEllipticRgn(0, 0, MAIN_WIN_SIZE, MAIN_WIN_SIZE);

Теперь необходимо сделать созданный регион регионом главного окна. Для этого после создания региона добавим ешё одну строку кода:

SetWindowRgn(hWnd, hMainWndRgn, FALSE);

Теперь при запуске приложения будет выведено круглое окно. Но так как это окно не имеет никаких элементов управления, то и никакие операции с ним невозможны. Можно только его закрыть, нажав Alt+F4.

Добавление контекстного меню

Для того, чтобы программа могла выполнять какие-то команды, добавим вывод контекстного меню при щелчке правой кнопки мыши в области главного окна. Для этого в файле RndWnd1.cpp объявим глобальную переменную, которая будет хранить дескриптор меню:

HMENU hMainMenu = NULL;

В функции InitInstance загрузим меню из ресурсов программы:

hMainMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDC_RNDWND1));

При щелчке правой кнопки мыши оконная процедура получает сообщение WM_CONTEXTMENU. Нам нужно, чтобы обработчик этого сообщения содержал код, который будет выводить меню. Для этого в оконную процедуру WndProc главного окна вставим следующий фрагмент:

case WM_CONTEXTMENU:
   {
      // Получить позицию мыши
      int xPos = GET_X_LPARAM(lParam);
      int yPos = GET_Y_LPARAM(lParam);

      // Вывести меню
      TrackPopupMenuEx(GetSubMenu(hMainMenu, 0), TPM_LEFTALIGN | TPM_TOPALIGN, xPos, yPos, hWnd, NULL);
   }
   return 0;

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

Перемещение окна на экране

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

static int iMouseCapture_xPos;
static int iMouseCapture_yPos;

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

case WM_LBUTTONDOWN:
   {
      // Получить позицию мыши
      int xPos = GET_X_LPARAM(lParam);
      int yPos = GET_Y_LPARAM(lParam);

      // Захватить мышь
      iMouseCapture_xPos = xPos;
      iMouseCapture_yPos = yPos;
      SetCapture(hWnd);
   }
   return 0;

В обработчике WM_LBUTTONDOWN запоминаются координаты мыши и производится захват мыши. Захват мыши выполняется для того, чтобы сообщения от мыши передавались окну, которое захватило мышь, даже если курсор находится вне этого окна.
Следующее сообщение, которое нам нужно обработать - это WM_MOUSEMOVE. Оно передается при перемещении курсора на экране. Вставим следующий фрагмент в оконную процедуру для обработки этого сообщения:

case WM_MOUSEMOVE:
   if (GetCapture() == hWnd)
   {// Мышь захвачена
      // Получить позицию мыши
      int xPos = GET_X_LPARAM(lParam);
      int yPos = GET_Y_LPARAM(lParam);

      // Переместить окно
      RECT WndRect;
      GetWindowRect(hWnd, &WndRect);
      int iOffset_xPos = xPos - iMouseCapture_xPos;
      int iOffset_yPos = yPos - iMouseCapture_yPos;
      SetWindowPos(hWnd, NULL, WndRect.left + iOffset_xPos, WndRect.top + iOffset_yPos, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
   }
   return 0;

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

case WM_LBUTTONUP:
   if (GetCapture() == hWnd)
   {// Мышь захвачена
      // Освободить мышь
      ReleaseCapture();
   }
   return 0;

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

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

Полный исходный код примера можно загрузить здесь