Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Класс окна на основе диалога.


Автор: mekhanikus 2.4.2011, 04:29
Попытаюсь обьяснить, чего я хочу.

Имеем диалог из ресурса:
Код

MonthWnd DIALOGEX 114,100,250,146
FONT 10,"Arial",400,0,204
STYLE 0x00C80840
EXSTYLE 0x00000080
BEGIN
  CONTROL "11",W01_MON,"DateWnd",0x50000301,31,32,13,10
  ...
  CONTROL "67",W06_SUN,"DateWnd",0x50000301,106,98,13,10
END

Где "DateWnd" - свой класс окна.

Хочу создать диалог:
Код

YearWnd DIALOGEX 114,100,250,146
FONT 10,"Arial",400,0,204
STYLE 0x00C80840
EXSTYLE 0x00000080
BEGIN
  CONTROL "01",JAN,"MonthWnd",0x50000301,31,32,13,10
  ...
  CONTROL "12",DEC,"MothWnd",0x50000301,106,98,13,10
END

Где "MonthWnd" - диалог из предыдущего примера.

Пока пытаюсь осмыслить метод, предложенный GremlinProg в http://forum.vingrad.ru/topic-268171.html#st_0_view_0.

Автор: GremlinProg 4.4.2011, 06:45
а я пока не понял проблемы,
что мешает создать диалог на основе второго листинга?

это же обычный ресурс диалога

Добавлено @ 06:46
а, начинаю понимать, т.е. надо первый диалог разместить на втором

Добавлено @ 06:53
так не получится,
нужно у второго перехватить событие WM_INITDIALOG и вызвать CreateDialog с параметрами первого

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

Автор: mekhanikus 4.4.2011, 14:57
Для начала сделал так:
Регистрирую класс окна "MonthWnd"
и в его WM_CREATE создаю дочерний диалог:
Код

LRESULT CALLBACK MONTHWND::MonthProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    MONTHWND* temp = (MONTHWND*)GetWindowLong(hwnd,GWL_USERDATA);
    switch (message)
    {
        case WM_CREATE:
        {
            CREATESTRUCT* cs = (CREATESTRUCT*)lParam;
            temp = new MONTHWND(cs);
            SetWindowLong(hwnd,GWL_USERDATA,(unsigned long)temp);
            temp->MonthDlg = CreateDialogParam(cs->hInstance,
                                               "MonthDlg",
                                               hwnd,
                                               (DLGPROC)&MONTHWND::MonthDlgProc,
                                               (LPARAM)temp->month);


            break;
        }
        case WM_DESTROY:
            delete temp;
        default:
            return DefWindowProc (hwnd, message, wParam, lParam);
    }
    return 0;
}


Чувствую - на тернистый путь встал.

Автор: mekhanikus 10.4.2011, 13:45
Регистрирую класс окна:

Код

char MONTHWND::szClassName[] = "MonthWnd";

ATOM MONTHWND::RegMonthWnd()
{
    WNDCLASSEX wc;
    ZeroMemory(&wc,sizeof(wc));
    wc.cbSize = sizeof(WNDCLASSEX);

    GetClassInfoEx(NULL,"#32770",&wc);

    HINSTANCE hi = GetModuleHandle(NULL);

    wc.hInstance = hi;
    wc.lpszClassName = szClassName;
    wc.lpfnWndProc = (WNDPROC)InitMonthDlg;

    return RegisterClassEx (&wc);
}


В процедуре инициализации переназначаю процедуру окна, прописываю USERDATA:

Код

LRESULT CALLBACK MONTHWND::InitMonthDlg(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
    MONTHWND* temp = new MONTHWND();

    SetWindowLong(hwnd,GWL_WNDPROC,(LONG)MONTHWND::MonthDlgProc);
    SetWindowLong(hwnd,GWL_USERDATA,(unsigned long)temp);

    return CallWindowProc(MONTHWND::MonthDlgProc,hwnd,msg,wParam,lParam);
}


В основной процедуре считываю USERDATA, и вызываю DefDlgProc.

Код

LRESULT CALLBACK MONTHWND::MonthDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
    MONTHWND* temp = (MONTHWND*)GetWindowLong(hwnd,GWL_USERDATA);
    return CallWindowProc(DefDlgProc,hwnd,msg,wParam,lParam);//
}


Ресурсы как в первом топике:

Код

MonthWnd DIALOGEX 114,100,250,146
CLASS "MonthWnd"
FONT 10,"Arial",400,0,204
STYLE 0x00C80840
EXSTYLE 0x00000080
BEGIN
  CONTROL "11",W01_MON,"DateWnd",0x50000301,31,32,13,10
  ...
  CONTROL "67",W06_SUN,"DateWnd",0x50000301,106,98,13,10
END


Код

YearWnd DIALOGEX 114,100,250,146
FONT 10,"Arial",400,0,204
STYLE 0x00C80840
EXSTYLE 0x00000080
BEGIN
  CONTROL "01",JAN,"MonthWnd",0x50000301,31,32,13,10
  ...
  CONTROL "12",DEC,"MothWnd",0x50000301,106,98,13,10
END


Диалог "YearWnd" запускается как обычно:

Код

DialogBox(GetModuleHandle(NULL),"YearDlg",hwndDlg,YearDlgProc);


На месте "MonthWnd" рисуются пустые квадратики без контролов.

Что я делаю не так?

Автор: GremlinProg 10.4.2011, 19:57
Цитата(mekhanikus @  10.4.2011,  15:45 Найти цитируемый пост)
Что я делаю не так?

Цитата(GremlinProg @  4.4.2011,  08:45 Найти цитируемый пост)
так не получится

Автор: mekhanikus 11.4.2011, 11:37
Цитата(GremlinProg @ 4.4.2011,  06:45)
так не получится

Просто, понятно, лаконично...  smile 

Пойдем другим путем.

Регистрируем класс окна:
Код

char MONTHWND::szClassName[] = "MonthWnd";

ATOM MONTHWND::RegMonthWnd()
{
    WNDCLASSEX wc;
    ZeroMemory(&wc,sizeof(wc));
    wc.cbSize = sizeof(WNDCLASSEX);

    HINSTANCE hi = GetModuleHandle(NULL);

    if(!GetClassInfoEx(hi,szClassName,&wc))
    {
        wc.cbSize = sizeof (WNDCLASSEX);
        wc.hInstance = hi;
        wc.lpszClassName = szClassName;
        wc.lpfnWndProc = (WNDPROC)InitMonthProc;
        wc.style = CS_DBLCLKS;
        wc.hIcon = NULL;
        wc.hIconSm = NULL;
        wc.hCursor = NULL;
        wc.lpszMenuName = NULL;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hbrBackground = NULL
        return RegisterClassEx (&wc);
    }
    return 0;
}

В момент создания окна делаем следующее:
Код

LRESULT CALLBACK MONTHWND::InitMonthProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_CREATE:
        {
            CREATESTRUCT* cs = (CREATESTRUCT*)lParam;

            //создаем диалог из ресурса
            HWND hDlg = CreateDialogParam(cs->hInstance,
                                               "MonthDlg",
                                               hwnd,
                                               (DLGPROC)&MONTHWND::MonthDlgProc,
                                               (LPARAM)0);

            RECT r;
            GetWindowRect(hDlg,&r);
            MapWindowPoints( HWND_DESKTOP, hDlg, (POINT*)&r, 2 );

            LONG PosX = (cs->cx - r.right)/2;
            LONG PosY = (cs->cy - r.bottom)/2;

            //делаем родителем MonthDlg основное окно диалога YearDlg 
            SetParent(hDlg,GetParent(hwnd));

            //ставим диалог в нужную позицию
            SetWindowPos(hDlg,HWND_TOP,PosX + cs->x,PosY + cs->y,0,0,SWP_NOSIZE);

            return (LRESULT)0;
        }
        default:
            return DefWindowProc (hwnd, message, wParam, lParam);
    }
    return DefWindowProc (hwnd, message, wParam, lParam);
}


Так работает, диалоги появляются.

Теперь надо убить окно инициализации диалога (желательно самоубийство  smile ) и назначить созданый диалог Item'ом YearDlg.

Первый пункт, думаю, освою сам.

А, вот со вторым пунктом пока проблемы, надо листать букварь.

Автор: mekhanikus 12.4.2011, 10:53
Со вторым пунктом разобрался:
Код

SetWindowLong(hDlg,GWL_ID,GetWindowLong(hwnd,GWL_ID));

Окно инициализации убиваю на очистке фона:
Код

case WM_ERASEBKGND:
        {
            DestroyWindow(hwnd);
            return (LRESULT)1;
        }

Может быть это правильней делать где-нибудь в другом месте?

Пока все устраивает. smile 

Автор: GremlinProg 12.4.2011, 11:31
Цитата(mekhanikus @  12.4.2011,  12:53 Найти цитируемый пост)
Может быть это правильней делать где-нибудь в другом месте?

если я правильно понял вопрос,
вот такая обработка WM_CREATE просто не создаст окно,
Цитата(mekhanikus @  11.4.2011,  13:37 Найти цитируемый пост)
return –1;

и не придется его удалять

а место и в самом деле неподходящее, не надо так делать

Автор: mekhanikus 12.4.2011, 11:40
Попробовал сделать
Код

return -1;

Приложение сразу закрывается.
Цитата(GremlinProg @  12.4.2011,  11:31 Найти цитируемый пост)
не надо так делать 

А как надо?
Интересует именно самоуничтожение.

Автор: GremlinProg 12.4.2011, 12:02
Цитата(mekhanikus @  12.4.2011,  13:40 Найти цитируемый пост)
Приложение сразу закрывается.

а в какой процедуре обрабатывается WM_ERASEBKGND?
закрывается, значит одно из двух:
1. это главное окно и у него в WM_DESTROY стоит PostQuitMessage,
который завершает цикл обработки сообщений,
т.е. в таком случае надо пересмотреть логику программы,
либо перенести PostQuitMessage в то окно, закрытие которого действительно должно завершить приложение

2. при создании окна, после CreateWindow/Ex стоит проверка на валидность возвращенного дескриптора,
т.е. в таком случае эту проверку надо убрать
Цитата(mekhanikus @  12.4.2011,  13:40 Найти цитируемый пост)
А как надо?Интересует именно самоуничтожение.

например "отложенная" посылка (PostMessage) самому себе сообщения закрытия, типа WM_CLOSE

Автор: mekhanikus 12.4.2011, 13:57
Цитата(GremlinProg @  12.4.2011,  12:02 Найти цитируемый пост)
1. это главное окно и у него в WM_DESTROY стоит PostQuitMessage,
который завершает цикл обработки сообщений,

Нет, это окно инициализации диалога MonthDlg.
Дочернее от диалога YearWnd - главного окна.
Все сообщения, кроме WM_CREATE и WM_ERASEBKGND, передаются в DefWindowProc.

Цитата(GremlinProg @  12.4.2011,  12:02 Найти цитируемый пост)
2. при создании окна, после CreateWindow/Ex стоит проверка на валидность возвращенного дескриптора,
т.е. в таком случае эту проверку надо убрать

Наверно такая проверка спрятана в процедуре CreateDialogParam.
Типа если один контрол не получился, значит - все ф топку.

Цитата(GremlinProg @  12.4.2011,  12:02 Найти цитируемый пост)
например "отложенная" посылка (PostMessage) самому себе сообщения закрытия, типа WM_CLOSE

Попробую. Когда её посылать?

Автор: GremlinProg 13.4.2011, 06:39
Цитата(mekhanikus @  12.4.2011,  15:57 Найти цитируемый пост)
Попробую. Когда её посылать?

ну, опять же, если я правильно понял, то самоуничтожиться должен дочерний диалог

вот на счет дочерних окон, не уверен, удалит ли WM_CLOSE такой диалог (по документации на DefWindowProc - да, а вот на DefDlgProc - по моему, все таки нет),
хотя всегда можно перехватить в процедуре этого дочернего диалога WM_CLOSE и вызвать из него DestroyWindow,
тогда точно удалит

либо придумать какое-нить другое сообщение для удаления

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

Автор: mekhanikus 14.4.2011, 14:31
Поставил PostMessage в конце обработки WM_CREATE в InitMonthProc.

Вроде бы все работает как надо: диалог создается, лишнее окно убирается.

Респект GremlinProg.

Вопрос можно считать решенным!

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)