15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Использование Окон.

Создание главного окна

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

Для создания главного окна большинство приложений используют стиль WS_OVERLAPPEDWINDOW. Этот стиль даёт окну заголовок (title bar), оконное меню, рамку, позволяющую изменять размеры окна, и кнопки минимизации и максимизации. Функция CreateWindowEx возвращает уникальный дескриптор, который идентифицирует окно в системе.

Следующий пример создаёт главное окно, принадлежащее оконному классу, определённому в приложении. В заголовке будет отражено имя окна Main Window. Смешав стили WS_VSCROLL и WS_HSCROLL со стилем WS_OVERLAPPEDWINDOW мы получим вертикальный и горизонтальные скролы в окне. Четыре подряд константы CW_USEDEFAULT устанавливают изначальный размер и расположение окна в значения, определённые в системе по умолчанию. Указав NULL вместо дескриптора меню, мы тем самым указываем, чтобы использовалось меню, определённое для оконного класса.

HINSTANCE hinst;
HWND hwndMain;

// Создаём главное окно.

hwndMain = CreateWindowEx( 
    0,                      // дополнительных стилей не будет
    "MainWClass",           // имя класса
    "Main Window",          // имя окна
    WS_OVERLAPPEDWINDOW |   // основной набор стилей для окна
             WS_HSCROLL |   // горизонтальная полоска прокрутки
             WS_VSCROLL,    // вертикальная полоска прокрутки
    CW_USEDEFAULT,          // горизонтальная координата по умолчанию
    CW_USEDEFAULT,          // вертикальная координата по умолчанию
    CW_USEDEFAULT,          // ширина по умолчанию
    CW_USEDEFAULT,          // высота по умолчанию
    (HWND) NULL,            // родительского окна нет
    (HMENU) NULL,           // будет использоваться меню класса
    hinstance,              // дескриптор экземпляра приложения
    NULL);                 

if (!hwndMain)
    return FALSE;

// Показываем окно с флагом, который используется при создании
// приложения и посылаем приложению сообщение WM_PAINT.

ShowWindow(hwndMain, SW_SHOWDEFAULT);
UpdateWindow(hwndMain);

Обратите внимание, что в этом примере функция ShowWindow вызывается после создания главного окна. Это связано с тем, что система, не показывает автоматически главное окно после его создания. Флаг SW_SHOWDEFAULT в функции ShowWindow, позволяет изначально показать главное окно с теми параметрами, которые передала ему вызвавшая программа. Функция UpdateWindow посылает окну его первое сообщение WM_PAINT.

Создание, перечисление и изменение размеров дочерних окон

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

Ниже приведён пример, который делит клиентскую область главного окна на три части, путём создания дочерних окон одинакового размера. Каждое дочернее окно имеет такю же высоту как и главное окно, при этом ширина составляет одну треть от главного. Главное окно создаёт дочерние в ответ на сообщение WM_CREATE, которое оно получает в процессе своего собственного создания. Так как каждое окно имеет стиль WS_BORDER, то все они будут с тонкой рамкой. Стиль WS_VISIBLE не указан, поэтому все три дочерних окна будет изначально скрыты. Обратите внимание, что с каждым окном связан идентификатор дочернего окна.

Главное окно изменяет размер и расположение дочерних окон в ответ на сообщение WM_SIZE, которое оно получает когда его собственный размер изменяется. В ответ на WM_SIZE, главное окно при помощи функции GetClientRect узнаёт изменение своей клиентской области и передаёт эти изменения в функцию EnumChildWindows. EnumChildWindows передаёт дескриптор каждого дочернего окна в цикле в callback функцию EnumChildProc. Эта функция изменяет размеры и расположение каждого дочернего окна при помощи MoveWindow. В заключение, EnumChildProc вызывает функцию ShowWindow, чтобы показать окно.

#define ID_FIRSTCHILD  100
#define ID_SECONDCHILD 101
#define ID_THIRDCHILD  102

LONG APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
                                                      LPARAM lParam)
{
    RECT rcClient;
    int i;

    switch(uMsg)
    {
        case WM_CREATE: // создание главного окна

            // Создание трёх невидимых дочерних окон.

            for (i = 0; i < 3; i++)
            {
                CreateWindowEx(0,
                               "ChildWClass",
                               (LPCTSTR) NULL,
                               WS_CHILD | WS_BORDER,
                               0,0,0,0,
                               hwnd,
                               (HMENU) (int) (ID_FIRSTCHILD + i),
                               hinst,
                               NULL);
            }

            return 0;

        case WM_SIZE:   // размер основного окна изменён 

            // Узнаём клиентской области главного окна
            // и получаем все дочерние окна. В процессе получения
            // передаём изменения в дочерние окна.

            GetClientRect(hwnd, &rcClient);
            EnumChildWindows(hwnd, EnumChildProc, (LPARAM) &rcClient);
            return 0;

        // Обрабатываем другие сообщения.
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam)
{
    LPRECT rcParent;
    int i, idChild;

    // Получаем идентификатор дочернего окна. Используем его,
    // чтобы установить расположение дочернего окна.

    idChild = GetWindowLong(hwndChild, GWL_ID);

    if (idChild == ID_FIRSTCHILD)
        i = 0;
    else if (idChild == ID_SECONDCHILD)
        i = 1;
    else
        i = 2;

    // Размер и расположение дочернего окна.

    rcParent = (LPRECT) lParam;
    MoveWindow(hwndChild,
               (rcParent->right / 3) * i,
               0,
               rcParent->right / 3,
               rcParent->bottom,
               TRUE);

    // Показываем дочернее окно.

    ShowWindow(hwndChild, SW_SHOW);

    return TRUE;
}

Уничтожение окна

Для удаления окна можно воспользоваться функцией DestroyWindow. Обычно, перед уничтожением, приложение посылает сообщение WM_CLOSE данному окну, давая ему возможность попросить пользователя подтвердить уничтожение окна, перед тем, как оно будет удалено. Если окно имеет меню, то при выборе пункта Close, окно так же получит сообщение WM_CLOSE. Если пользователь подтверждает удаление окна, то приложение должно вызвать DestroyWindow. После удаления окна с экрана, система посылает окну сообщение WM_DESTROY. В ответ на сообщение WM_DESTROY, окно должно сожранить свои данные и освободить выделенные ему ресурсы. В главном окне обработка сообщения WM_DESTROY заключается в вызове функции PostQuitMessage для завершения приложения.

Следующий пример показывает как попросить пользователя подтвердить уничтожение окна. В ответ на WM_CLOSE, появляется диалоговое окошко с кнопками Yes, OK, и Cancel. Если пользователь нажимает Yes, товызывается DestroyWindow; иначе окно не уничтожается. Так как уничтожаемое окно является главным, то в ответ на WM_DESTROY, вызывается PostQuitMessage.

case WM_CLOSE:

    // Создаём диалоговое окошко с сообщением. Если пользователь
    // нажимает Yes, то уничтожаем главное окно.

    if (MessageBox(hwnd, szConfirm, szAppName, MB_YESNOCANCEL) == IDYES)
        DestroyWindow(hwndMain);
    else
        return 0;

case WM_DESTROY:

    // Посылаем сообщение WM_QUIT, чтобы
    // приложение завершилось.

    PostQuitMessage(0);
    return 0;

Прозрачные окна

Чтобы сделать диалоговое окно прозрачным, сперва необходимо создать обычный диалог. А затем, в обработчике сообщения WM_INITDIALOG, установить бит прозрачности в дополнительных свойствах окна, и вызвать SetLayeredWindowAttributes с желаемым значением альфы. Программно это выглядит так:

// Устанавливаем WS_EX_LAYERED на это окно
SetWindowLong(hwnd,
              GWL_EXSTYLE,
              GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);

// Делаем это окно на 70% прозрачным
SetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA);

Обратите внимание, что третий параметр SetLayeredWindowAttributes имеет значение в пределах от 0 до 255. При этом 0 делает окно полностью прозрачным, а 255 полностью НЕ прозрачным. Этот параметр очень похож на BLENDFUNCTION в функции AlphaBlend.

Чтобы сделать окно обратно не прозрачным, вызовите функцию  SetWindowLong без WS_EX_LAYERED, а затем  заставьте окно перерисоваться. Удаление этого бита очень желательно, чтобы система смогла освободить выделенную для прозрачности память:

// Удаляем стиль WS_EX_LAYERED
SetWindowLong(hwnd,
              GWL_EXSTYLE,
              GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);

// Перерисовываем окно и его дочерние окна
RedrawWindow(hwnd,
             NULL,
             NULL,
             RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);