Создадим круглое окно c четырьмя областями, используя редактор ресурсов RCE и технологию Broken Window (ломаное окно). Для этого в редакторе ресурсов RCE создадим ресурс ломаного окна, содержащий четыре области (Рис.1). Также для успешной генерации проекта Broken Window необходимо добавить в ресурсы таблицу акселераторов и строку с заголовком главного окна.
Далее сгенерируем проект Broken Window, используя команду Проект->Генератор кода->Проект Broken Window в меню редактора ресурсов RCE (Рис.2).
Сгенерированные файлы будут помещены в указанную при генерации проекта папку (в нашем случае RndWnd3).
Наиболее важные файлы проекта:
Папка BrokenWindow | - cодержит набор классов BrokenWindow |
BWMainWnd.h, BWMainWnd.cpp | - класс ломаного окна (главного окна) |
RndWnd.cpp | - точка входа в программу |
BWLDArea.h, BWLDArea.cpp BWLUArea.h, BWLUArea.cpp BWRDArea.h, BWRDArea.cpp BWRUArea.h, BWRUArea.cpp |
- классы областей ломанного окна |
Создадим в Visual Studio проект Win32, удалим из него все файлы по умолчанию и включим сгенерированные из папки RndWnd3 (Рис.3).
При этом в свойствах файла stdafx.cpp необходимо изменить параметр "Создавать или использовать предварительно скомпилированный заголовочный файл" на "Создавать предварительно скомпилированные заголовки (/Yc)" (Рис.4).
Скомпилируем проект и запустим полученное приложение. На экране должен появиться черный квадрат, который можно перемещать мышкой. Для закрытия приложения используйте сочетание клавиш Alt+F4.
Добавим меню в приложение. Для этого в класс главного окна добавим переменную m_hMainMenu - дескриптор главного меню, а также обработчики событий: CreateEvent - создание окна, ContextMenuEvent - вывод контекстного меню, CommandEvent - команды меню. В обработчике CreateEvent создадим меню, в обработчике ContextMenuEvent выведем меню, а в обработчике DestroyEvent (уже имеется в классе главного окна) удалим меню. В CommandEvent организуем обработку команд меню. Также добавим функцию диалогового окна About для диалога "О программе", который будет вызываться одной из команд меню.
class CBWMainWnd : public CBrokenWinCore
{
public:
CBWMainWnd(void);
...
DECLARE_BWAREA_MAP()
// События
protected:
virtual int CreateEvent(HWND hBrkWin, LPCREATESTRUCT lpCS);
virtual void DestroyEvent();
virtual void CloseEvent();
virtual void ContextMenuEvent(UINT xPos, UINT yPos);
virtual void CommandEvent(int id, HWND hwndCtl, UINT codeNotify);
// Данные
private:
HMENU m_hMainMenu; // Главное меню
};
// Обработчик сообщений для окна "О программе".
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
// Конструктор
CBWMainWnd::CBWMainWnd(void)
{
m_hMainMenu = NULL;
}
// Создание главного окна
int CBWMainWnd::CreateEvent(HWND hBrkWin, LPCREATESTRUCT lpCS)
{
// Загрузить главное меню
m_hMainMenu = ::LoadMenu(g_hInstance, MAKEINTRESOURCE(IDC_RNDWND));
return 0;
}
// Удаление главного окна
void CBWMainWnd::DestroyEvent()
{
// Удалить главное меню
if (m_hMainMenu != NULL)
{
::DestroyMenu(m_hMainMenu);
m_hMainMenu = NULL;
}
// Выход из приложения
PostQuitMessage(0);
}
// Вывод контекстного меню
void CBWMainWnd::ContextMenuEvent(UINT xPos, UINT yPos)
{
// Вывести меню
::TrackPopupMenuEx(::GetSubMenu(m_hMainMenu, 0),
TPM_LEFTALIGN | TPM_TOPALIGN, xPos, yPos, m_hWnd, NULL);
}
// Обработка команды
void CBWMainWnd::CommandEvent(int id, HWND hwndCtl, UINT codeNotify)
{
switch (id)
{
case IDM_ABOUT:
DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), m_hWnd, About);
break;
case IDM_EXIT:
// Уничтожить главное окно
::DestroyWindow(m_hWnd);
break;
}
}
Теперь, если щелкнуть на окне приложения правой кнопкой мыши, то будет выведено меню.
Перейдём к прорисовке областей ломаного окна. Для начала очистим фон главного окна. Для этого добавим в класс главного окна событие прорисовки.
class CBWMainWnd : public CBrokenWinCore
{
public:
CBWMainWnd(void);
...
DECLARE_BWAREA_MAP()
// События
protected:
...
virtual void DrawEvent(HDC hDC);
...
};
// Прорисовка главного окна
void CBWMainWnd::DrawEvent(HDC hDC)
{
Graphics graph(hDC);
// Получить размер главного окна
RECT ClRect;
::GetClientRect(m_hWnd, &ClRect);
// Очистить фон
Color BkColor;
BkColor.SetFromCOLORREF(TRANSPARENT_COLOR);
SolidBrush brush(BkColor);
graph.FillRectangle(&brush, 0, 0, ClRect.right, ClRect.bottom);
}
Добавим в класс главного окна переменную m_AreaState, определяющую состояние областей ломаного окна, а также методы для получения/установки данной переменной.
enum AREA_STATE
{
NONE,
LEFT_UP,
RIGHT_UP,
LEFT_DOWN,
RIGHT_DOWN
};
class CBWMainWnd : public CBrokenWinCore
{
public:
CBWMainWnd(void);
...
// Методы
public:
AREA_STATE GetAreaState();
void SetAreaState(AREA_STATE State);
// Данные
private:
AREA_STATE m_AreaState; // Состояние областей главного окна
...
};
// Конструктор
CBWMainWnd::CBWMainWnd(void)
{
...
m_AreaState = NONE;
}
// Получить состояние областей главного окна
AREA_STATE CBWMainWnd::GetAreaState()
{
return m_AreaState;
}
// Установить состояние областей главного окна
void CBWMainWnd::SetAreaState(AREA_STATE State)
{
m_AreaState = State;
}
Прорисуем одну из областей ломаного окна, например, левую нижнюю (класс CBWLDArea). Для этого добавим в класс CBWLDArea переменную m_pPath, в которой будет храниться контур области, а также обработчики событий: InitEvent - инициализация области, DeInitEvent - деинициализация области, DrawEvent - прорисовка области. В обработчике InitEvent создадим контур области, который потом будет закрашиваться в обработчике DrawEvent. В обработчике DeInitEvent произведём освобождение всех задействованных ресурсов.
class CBWLDArea : public CBrkWinArea
{
public:
CBWLDArea(WORD AreaID, POINT AreaPos, SIZE AreaSize, CBrokenWinCore* pBrokenWin);
...
// События
protected:
virtual void InitEvent();
virtual void DeInitEvent();
virtual void DrawEvent(HDC hDC);
// Данные
GraphicsPath* m_pPath; // Контур области
};
// Конструктор
CBWLDArea::CBWLDArea(WORD AreaID, POINT AreaPos, SIZE AreaSize, CBrokenWinCore* pBrokenWin)
: CBrkWinArea(AreaID,AreaPos,AreaSize,pBrokenWin)
{
m_pPath = NULL;
}
// Инициализация области
void CBWLDArea::InitEvent()
{
// Вычислить параметры области
Rect AreaRect(0, -m_Size.cy, 2 * m_Size.cx, 2 * m_Size.cy);
// Создать контур области
m_pPath = new GraphicsPath();
m_pPath->AddArc(AreaRect, 90, 90);
m_pPath->AddLine(0, 0, m_Size.cx - 1, 0);
m_pPath->CloseFigure();
}
// Деинициализация области
void CBWLDArea::DeInitEvent()
{
// Удалить контур
if (m_pPath != NULL)
{
delete m_pPath;
m_pPath = NULL;
}
}
// Прорисовка области
void CBWLDArea::DrawEvent(HDC hDC)
{
Graphics graph(hDC);
// Получить главное окно
CBWMainWnd* pMainWnd = (CBWMainWnd*)m_pBrokenWin;
_ASSERT(pMainWnd != NULL);
// Получить состояние областей главного окна
AREA_STATE AreaState = pMainWnd->GetAreaState();
SolidBrush LeftPathBrush(Color(200, 0, 200));
if (AreaState == LEFT_DOWN)
LeftPathBrush.SetColor(Color(255, 0, 255));
graph.FillPath(&LeftPathBrush, m_pPath);
}
Добавим реагирование области на вход курсора в её пределы. Для этого добавим MouseInArea - обработчик события входа курсора в область и MouseOutArea - обработчик события выхода курсора из области.
class CBWLDArea : public CBrkWinArea
{
public:
CBWLDArea(WORD AreaID, POINT AreaPos, SIZE AreaSize, CBrokenWinCore* pBrokenWin);
...
// События
protected:
...
virtual void MouseInArea(POINT point);
virtual void MouseOutArea();
// Данные
...
};
// Мышь вошла в область
void CBWLDArea::MouseInArea(POINT point)
{
// Получить главное окно
CBWMainWnd* pMainWnd = (CBWMainWnd*)m_pBrokenWin;
_ASSERT(pMainWnd != NULL);
pMainWnd->SetAreaState(LEFT_DOWN);
pMainWnd->ReDrawBW();
}
// Мышь вышла из области
void CBWLDArea::MouseOutArea()
{
// Получить главное окно
CBWMainWnd* pMainWnd = (CBWMainWnd*)m_pBrokenWin;
_ASSERT(pMainWnd != NULL);
pMainWnd->SetAreaState(NONE);
pMainWnd->ReDrawBW();
}
Прорисовка остальных областей осуществляется аналогичным образом.
Полный исходный код примера можно загрузить здесь