Связывание классов кнопок и окон - PullRequest
0 голосов
/ 25 февраля 2012

Я работаю над собственной персонализированной оболочкой для winapi. Мой желаемый синтаксис такой:

// #define wndproc(name) void name (Window & hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
// #define buttonproc(name) void name (Button & hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

wndproc (rightClick) { //evaluates to function to handle window message
    ::msg ("You right clicked the window. Closing window...");
    hwnd.close(); //close() is implemented in my Window class
}

buttonproc (buttonClick) { //same thing basically
    ::msg ("You clicked this button. I'm going to hide the other one...");

    //if text on this button is "One button", find the one belonging to parent
    //with the text "Other button" and hide it, or vice-versa
    hwnd.text == "One button"
    ? hwnd.parent().button ("Other button").hide();
    : hwnd.parent().button ("One button").hide();
}

int main() {
    Window win; //create default window
    win.addmsg (WM_LBUTTONDOWN, rightClick); //look for l-click message and call that

    Button b1 (win, "One button", 100, 100, 50, 20, buttonClick); //parent, coords, size, clicked
    Button b2 (win, "Other button", 200, 100, 50, 20, buttonClick);  

    return messageLoop(); //should be self-explanatory
}  

Дело в том, что в wndproc, hwnd - это Window &, а в buttonproc, hwnd - это Button &. Я мог бы сойти с рук с высказыванием:

msgproc (Window, rightClick){...} 
msgproc (Button, buttonClick){...}

Проблема в том, что я должен вызвать эти процедуры и дать им право hwnd. Моя процедура главного окна реализована в моем классе Window. Он получает четыре нормальных аргумента. Если мне нужно передать сообщение WM_COMMAND правой кнопке, я бы хотел передать ему соответствующий объект Button.

Так, как сейчас, я передаю указатель на суперкласс Window и Button. Конечно, это создает сложный код, такой как:

((Window *)hwnd)->operator()() //get HWND of the Window

В любом случае, это не очень хорошо работает. К сожалению, на данный момент я могу думать только о том, чтобы сохранить список всех созданных Button и вытащить правильный. Я мог бы даже распространить это на всех возможных получателей.

Преимущество такого способа состоит в том, что мой класс Button имеет статическую оконную процедуру, которая вызывается каждый раз, когда обнаруживается сообщение WM_COMMAND. Я не добавил другие элементы управления, но он предназначен для работы, проверяя идентификатор с существующими и вызывая процедуру, указанную при создании кнопки, если она совпадает. Дело в том, что, когда это будет сделано, будут вызываться любые другие вещи (например, флажки), которые добавляют обработчик WM_COMMAND.

Я думал о том, чтобы сохранить в Window список каждого потомка HWND и его соответствующего объекта. Таким образом, я могу просто обнулить дополнительные процедуры в каждом классе, например Button, что приведет к большой дополнительной обработке, и заменить proc [i] ((BaseWindow *)hwnd, msg, wParam, lParam) чем-то вроде proc [i] (control [loword(wParam)], msg, wParam, lParam) для WM_COMMAND, используя lParam для посмотрите, является ли это контролем.

Похоже, мне не хватает чего-то большого, хотя. Скорее всего, я начну реализовывать это, а затем столкнусь с большой проблемой. Есть ли лучший способ сделать все это?

Пока я в этом, есть ли способ сделать функцию control(), которая возвращает правильный тип объекта (Button, Checkbox ...) в зависимости от того, какой из них он находит идентификатор, а не просто массив различных объектов (что, я уверен, я видел способ сделать это)?

1 Ответ

1 голос
/ 25 февраля 2012

Вопрос заключался в том, как обрабатывать сообщения WM_COMMAND, которые доставляются родителю, а не классу кнопок (или какому-либо другому).

Самое простое решение - добавить обработчик WM_COMMAND в базовый класс окна для пересылкисообщение для элемента управления, который его сгенерировал.Сообщение будет обработано в классе элемента управления.

Код в обработчике WM_COMMAND может выглядеть примерно так:

if (lParam != 0)
{
    // lParam non zero so this is a control notification.
    if ((HWND)lParam == hWnd)
    {
        // The message has arrived at its destination
        return OnNotify(HIWORD(wParam), LOWORD(wParam));
    }
    else
    {
        // Reflect the message back to the control.
        return SendMessage((HWND)lParam, WM_COMMAND, wParam, lParam);
    }
}

Я изначально неправильно понял вопрос.Далее описывается несколько способов маршрутизации сообщений к объектам:

Способ MFC

В каждом окне используется одна и та же оконная процедура.У вас есть глобальная карта от HWND до оконного объекта.(Карта фактически для каждого потока, но в большинстве приложений это не требуется.) Когда приходит сообщение, вы ищите объект и отправляете ему сообщение.Если Button происходит от Window, тогда очень легко выполнить обработку для каждого класса.

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

Способ ATL

Каждое окно имеет свою собственную оконную процедуру (возможно, также свой собственный класс, чтобы ее было легко установитьоконная процедура изначально; я забыл).Оконная процедура является сгенерированной заглушкой, которая загружает указатель на объект * и переходит к оконной процедуре базового класса (которая является нестатической функцией-членом).(Для простоты заглушка должна перейти к невиртуальному wndproc в базовом классе, который вызывает «реальный» виртуальный wndproc.) Помимо изменения способа отображения HWND на объекты, в других отношениях это в основном то же самое, что имодель MFC.

* На x86 заглушка помещает указатель объекта в ECX, прежде чем перейти к wndproc.Это также будет работать на x64 (хотя я не знаю, работает ли он так), но указатель объекта перезапишет HWND (поэтому не виртуальный базовый класс wndproc не будет иметь параметр HWND).

...