Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Visual C++/MFC/WTL > Ресайзинг диалога и перепозиционирование контролов


Автор: Dreamer_0x01 1.11.2005, 21:01
Столкнулся с такой проблемой.
Нужно обрабатывать изменение пользователем размеров диалога.
Диалог содержит в себе контролы, которые тоже надо репозиционировать.
Для этого я обрабатываю событие WM_SIZING.

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

Проблем в том, что координаты прямоугольника, передающиеся обработчику этого сообщения, соответствуют координатам прямоугольника ВСЕГО окна.
(как например, если бы мы пользовались функцией GetWindowRect()).
Но часть площади окна занято на заголовок окна(системное меню), а также на рисование объемной рамочки вокруг окна.
Оставшееся "рабочее" пространство окна можно узнать с помощью функции
GetClientRec(). Но проблема в том, что если ее вызвать непосредственно в обработчике данного события, то она вернет координаты прямоугольника, соответствующего "старым" границам окна, существовавшего до момента начала изменения его размеров.

Мне пришлось вычислять место, занимаемое системным меню и объемной рамочкой, и в обработчике события его учитывать.
Все работает.
Но у менятакое чувство, что можно было все сделать проще.
МОЖНО ИЛИ НЕТ?

вот код, аналогичный моему:
Код

// test_resizeDlg.h : header file
#pragma once

class Ctest_resizeDlg : public CDialog
{
public:
    Ctest_resizeDlg(CWnd* pParent = NULL);    // standard constructor
    enum { IDD = IDD_TEST_RESIZE_DIALOG };

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    HICON m_hIcon;
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
private:
    CButton *cb[4]; //мои элементы, например, кнопочки
    int v_menu_size;//вертикальный размер,занимаемый системным меню и бордером
    int g_border_size;//горизонтальный размер, занимаемый бордером
public:
    virtual BOOL DestroyWindow();
    afx_msg void OnSizing(UINT fwSide, LPRECT pRect);
};






Код

// test_resizeDlg.cpp : implementation file

BEGIN_MESSAGE_MAP(Ctest_resizeDlg, CDialog)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    //}}AFX_MSG_MAP
    ON_WM_SIZING()
END_MESSAGE_MAP()

BOOL Ctest_resizeDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    SetIcon(m_hIcon, TRUE);            // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    CRect cr;//С помощью него узнаем размеры системного меню и бордеров
    this->GetWindowRect(cr);//сначала узнаем размеры окна
    this->v_menu_size=cr.Height();
    this->g_border_size=cr.Width();
    this->GetClientRect(cr);//и потом вычитаем из него клиентские размеры
    this->v_menu_size-=cr.Height();
    this->g_border_size-=cr.Width();
    for(int i=0;i<4;i++)//для примера делаем четыре кнопки, располагаем их 2х2
    {
        int l,t,r,b,ls,hs;
        CString str;
        this->cb[i]=new CButton();
        ls=cr.Width()/2;hs=cr.Height()/2;
        l=(i%2)*ls;
        r=l+ls;
        t=(i/2)*hs;
        b=t+hs;
        str.Format("Кнопка %d",i);
        cb[i]->Create(str,BS_PUSHBUTTON|WS_CHILD|WS_VISIBLE,CRect(l,t,r,b),this,i+1000);
    }
    return TRUE;  
}

BOOL Ctest_resizeDlg::DestroyWindow()
{
    for(int i=0;i<4;i++)
    {
        this->cb[i]->DestroyWindow();
        delete cb[i];
    }

    return CDialog::DestroyWindow();
}

void Ctest_resizeDlg::OnSizing(UINT fwSide, LPRECT pRect)
{
    CDialog::OnSizing(fwSide, pRect);
    //в прямоугольнике pRect хранятся координаты "внешних" границ
    //а нам нужны клиентские координаты
    //поэтому в OnInitDialog() вычислено смещение
    /*если здесь вызвать GetClientRec(....),
    то результат будет содержать "старые" координаты */
    for(int i=0;i<4;i++)//ресайз контролов (в нашем случае - наших кнопок)
    {
        /*расстановка кнопок - такая же, как в OnInitDialog(),
        но приходится учитывать смещение размеров 
        заголовка окна и объемных границ окна*/
        int l,t,r,b,ls,hs;
        ls=(pRect->right-pRect->left-this->g_border_size)/2;//вот здесь учтем смещение
        hs=(pRect->bottom-pRect->top-this->v_menu_size)/2;//и здесь
        l=(i%2)*ls;
        r=l+ls;
        t=(i/2)*hs;
        b=t+hs;
        cb[i]->SetWindowPos(this,l,t,r-l,b-t,SWP_NOZORDER);
    }
}

Автор: nikf 1.11.2005, 21:22
WM_SIZING lParam - pointer to RECT

Автор: Dreamer_0x01 1.11.2005, 22:33
ну да, но поинтер он именно на ПОЛНЫЙ RECT, который занимает ВСЕ ОКНО.
А мне нужен КЛИЕНТСИЙ!

Автор: Earnest 2.11.2005, 14:32
Цитата(Dreamer_0x01 @ 1.11.2005, 21:01)
Но у менятакое чувство, что можно было все сделать проще.

Боюсь, нет. Вот наоборот - можно, т.е. по заданному клиенту вычислить размер всего окна (AdjustWindowRectEx), а вот по всему окну вычислить клиента - не знаю такой функции.

Однако я бы тебе предложила другой подход: создай мэп(контрол->позиция относительно WindowRect), а потом при изменени размеров окна все рисуй согласно полученному ректу, и все.

Автор: Coocky 2.11.2005, 17:59
Цитата
Но проблема в том, что если ее вызвать непосредственно в обработчике данного события, то она вернет координаты прямоугольника, соответствующего "старым" границам окна, существовавшего до момента начала изменения его размеров

Вставлю свою ламерскую морду.
Может подойдет WM_WINDOWPOSCHANGING ?

Автор: Earnest 2.11.2005, 19:25
Нет, WM_WINDOWPOSCHANGING не подойдет. Во-первых, оно содержит тот же RECT всего окна, а во-вторых - приходит только один раз, непосредственно перед установкой размеров окна, а не как нужно Терминатору - во время ресайзинга.

Появилась дикая мысль - послать окну запрос WM_NCCALCSIZE с параметрами wParam=false, lParam=&(WindowRect). Судя по описанию, окно должно пересчитать этот рект в клиенский:

Цитата

If wParam is FALSE, lParam points to a RECT structure. On entry, the structure contains the proposed window rectangle for the window. On exit, the structure should contain the screen coordinates of the corresponding window client area.


Но как это будет себя вести во время обработки WM_SIZING - не могу сказать.

Автор: Coocky 2.11.2005, 19:27
Цитата(Earnest @ 2.11.2005, 19:25)
Нет, WM_WINDOWPOSCHANGING не подойдет. Во-первых, оно содержит тот же RECT всего окна, а во-вторых - приходит только один раз, непосредственно перед установкой размеров окна, а не как нужно Терминатору - во время ресайзинга.

Да я уже понял..(попробывал smile )

Цитата(Earnest @ 2.11.2005, 19:25)
Появилась дикая мысль - послать окну запрос WM_NCCALCSIZE с параметрами wParam=false, lParam=&(WindowRect). Судя по описанию, окно должно пересчитать этот рект в клиенский:

Незнаю, в жизни не использовал никогда.. smile

Автор: Dreamer_0x01 2.11.2005, 20:38
Цитата(Earnest @ 2.11.2005, 14:32)
днако я бы тебе предложила другой подход: создай мэп(контрол->позиция относительно WindowRect), а потом при изменени размеров окна все рисуй согласно полученному ректу, и все.


В принципе, суть метода та же.
мне при размещении контролов придется учитывать смещение ClientRect относительно WindowRect, чтобы контролы не вылезли за пределы клиентской области. И тоскать за собой уже не смещение, а этот мэп.
Все равно что называется "в два действия", как и у меня.



Цитата(Earnest @ 2.11.2005, 19:25)
а не как нужно Терминатору

Эт хто? Я Dreamer!
smile


Цитата(Earnest @ 2.11.2005, 19:25)
Появилась дикая мысль - послать окну запрос WM_NCCALCSIZE с параметрами wParam=false, lParam=&(WindowRect). Судя по описанию, окно должно пересчитать этот рект в клиенский:


Цитата 

If wParam is FALSE, lParam points to a RECT structure. On entry, the structure contains the proposed window rectangle for the window. On exit, the structure should contain the screen coordinates of the corresponding window client area.


Но как это будет себя вести во время обработки WM_SIZING - не могу сказать.


А вот фигово ведет. Структуру заполняет левыми значениями, правильные они получаются только тогда, когда окно перересуется. (то есть отработает WM_SIZE, когда пользователь отпустит мышку). Во время WM_SIZING оно по-видимому определять эти прямоугольники не умеет.

Автор: Dreamer_0x01 3.11.2005, 16:54
Coocky
Earnest

Где же вы???

Автор: Coocky 3.11.2005, 17:12
Dreamer_0x01
Я здесь, но на данный момент помочь ничем не могу..
Смотри, я сам вопросы задаю, дел по горло, некогда даже потренироваться на твоем коде.. smile

Автор: Earnest 3.11.2005, 18:59
Цитата(Dreamer_0x01 @ 2.11.2005, 20:38)
А вот фигово ведет.

Ну, я и не обещала, что будет хорошо. Но я не сомневалась, что ты не применешь поэкспериментировать (и всем расскажешь). За что спасибо. smile

Цитата(Dreamer_0x01 @ 2.11.2005, 20:38)
Все равно что называется "в два действия", как и у меня.

Да, количество действий не экономится, верно. НО! Подход более универсален, т.к. не зависит от внешнего окна. Т.е. можно написать базовый код, и использовать как фичу в разных окнах. Нужно только задать, какие контролы позиционируются вместе с окном. Можно пойти дальше и задать как именно позиционируется каждый контрол (какое смещение остается постоянным - от правой или левой рамки, сверху или снизу) ...

И какая тебе разница, что там будет храниться в базовом классе? Хоть 10 мэпов. Главное, чтобы основную логику конкретного окна не застило.

Автор: Dreamer_0x01 6.11.2005, 13:37
Цитата(Earnest @ 3.11.2005, 18:59)
Ну, я и не обещала, что будет хорошо. Но я не сомневалась, что ты не применешь поэкспериментировать (и всем расскажешь). За что спасибо.


Это вот тебе спасибо за участие и дельные мысли.

Цитата(Earnest @ 3.11.2005, 18:59)
И какая тебе разница, что там будет храниться в базовом классе? Хоть 10 мэпов.


а как же увеличение размера кода? Ведь моя программа будет вывешена на сайте нашего предприятия, чтобы клиенты могли скачивать ее для настройки наших приборов. И хоть я понимаю, что использование MFC в обмен на простоту использования дает увеличение кода и снижение скорости работы исполняемого кода, но все-таки злоупотреблять такими фактами не стоит. Не каждому понравится выкачивать многометровые файлы с сайта. Опять же, к использованию ресурсов мне тоже следует относиться осторожно, так как у многих заказчиков все еще стоят Win95 на первых пентиумах....

Автор: AlexPro 13.11.2005, 10:27
Видимо, я чего-то не улавливаю, но мне непонятно, к чему такие сложности. Чем не угодил WM_SIZE? Вся обработка ведется в OnSize. Получаешь размеры клиентской области функцией GetClientRect() для диалогового окна и двигаешь или меняешь размеры своих контролов как тебе понравится. Все происходит в режиме реального времени. Или этот путь черезчур прост? smile

Автор: Dreamer_0x01 13.11.2005, 23:37
Цитата(AlexPro @ 13.11.2005, 10:27)
Все происходит в режиме реального времени


не-а. в том-то и дело, что WM_SIZE происходит только когда, когда изменение размеров окна уже закончилось, то есть пользователь уже отпустил кнопку мышки. Мне же нужно, чтобы во время таскания уголка окна перерисовывалось и его содержимое, а не только внешняя рамка.

Автор: The Thing 14.11.2005, 00:33
Dreamer_0x01
Юзай сообщения ON_WM_GETMINMAXINFO()
Сюда приходит сообщение раньше всего...

Код

MainFrame.h
afx_msg void OnGetMinMaxInfo(MINMAXINFO *lpMMI);

MainFrame.cpp
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
                ...
    ON_WM_GETMINMAXINFO()
    ON_WM_SIZE()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

Добавлено @ 00:35
забыл...
Код

void CMainFrame::OnGetMinMaxInfo(MINMAXINFO *lpMMI)
{
//не забудь
    CFrameWnd::OnGetMinMaxInfo(lpMMI);
}

Добавлено @ 00:42
Да, забыл объяснить.. сюда как раз и приходят сообщения которые пытается поймать Dreamer_0x01... т.е. сообщения приходят при изменение размеры.. правдо может возникнуть некоторая проблема с отрисовкой контролов.. но ето все решаемо.. и вопрос №2.

Автор: AlexPro 14.11.2005, 01:23
Цитата(Dreamer_0x01 @ 13.11.2005, 23:37)
не-а. в том-то и дело, что WM_SIZE происходит только когда, когда изменение размеров окна уже закончилось, то есть пользователь уже отпустил кнопку мышки. Мне же нужно, чтобы во время таскания уголка окна перерисовывалось и его содержимое, а не только внешняя рамка

Странно, у меня все работает... smile Что я делаю неправильно? smile Вероятно, у юзера отлючена опция "Отображать содержимое окна при перетаскивании". Пускай включит - и будет все замечательно, причем поведение твоего окна будет строго в соответствии с настройками винды, а не выбиваться из общего строя.

Автор: Dreamer_0x01 14.11.2005, 14:22
Цитата(AlexPro @ 14.11.2005, 01:23)
Вероятно, у юзера отлючена опция "Отображать содержимое окна при перетаскивании". Пускай включит - и будет все замечательно, причем поведение твоего окна будет строго в соответствии с настройками винды, а не выбиваться из общего строя.


Пока что данный юзер - это я. И у меня действительно эи настройки отключены.
А у заказчика этих настроек может и не быть. Во всяком случае, в Windows 95 и Windows 98 я не помню, чтоб такая опция была.

Автор: Dreamer_0x01 14.11.2005, 15:11
Цитата(The @ 14.11.2005, 00:33)
Юзай сообщения ON_WM_GETMINMAXINFO()
Сюда приходит сообщение раньше всего...


А как его юзать?
Структура MINMAXINFO, указатель на которую передается в этом сообщении, содержит 5 полей:
ptReserved ,
ptMaxSize ,
ptMaxPosition ,
ptMinTrackSize ,
ptMaxTrackSize .

пробовал юзать все параметры - ни в одном не содержится ни текущей длины, ни ширины окна.




Автор: AlexPro 14.11.2005, 17:22
Цитата(Dreamer_0x01 @ 14.11.2005, 14:22)
А у заказчика этих настроек может и не быть.

Не может. В 98 есть однозначно (смотрел только-что), в Win95 - на 95% smile (проверить негде, а памяти своей не доверяю). Но стоит ли блюсти совместимость с 95? Мне думается, сейчас найти пользователя с Win95 - требуется большое везенье. Я таких не знаю. Если не секрет, проект коммерческий? Если да, то что можно получить от человека (или организации), не имеющих денег на приличный компьютер и использующих безнадежно устаревшую операционку?

Автор: Dreamer_0x01 14.11.2005, 19:16
Цитата(AlexPro @ 14.11.2005, 17:22)
Если не секрет, проект коммерческий? Е


да

Цитата(AlexPro @ 14.11.2005, 17:22)
Если да, то что можно получить от человека (или организации), не имеющих денег на приличный компьютер и использующих безнадежно устаревшую операционку?


Получить прежде всего можно деньги и репутацию.
Среди наших клиентов, имеющих компы с операционкой Win95 - некоторые котельные, где все сделано по принципу "лучшее - враг хорошего, работает, вот и не будем трогать". Кстати, не очень плохой принцип, следует сказать.
Ну и еще одно место, где точно есть - Нововоронежская АЭС и вроде бы Калиниская АЭС.
У этих хоть и стоит везде оборудование 70х-80х-90х годов, но чтобы его поменять - нужно проводить очень много мероприятий. Хотя, они признают, что надо менять, но пока времени на это у них нет.

Автор: Dreamer_0x01 14.11.2005, 19:47
А вообще, я тут подумал...Спасибо. Так и напишу в инструкции - кому охота видеть содержимое окна при ресайзинге, включите в виндах эту опцию, а в виндоус95...напишу, что эта опция будет недоступна, от этого программа функциональности не потеряет.
Будет вполне нормально.

Автор: The Thing 14.11.2005, 21:34
Цитата(Dreamer_0x01 @ 14.11.2005, 15:11)
Цитата(The @ 14.11.2005, 00:33)
Юзай сообщения ON_WM_GETMINMAXINFO()
Сюда приходит сообщение раньше всего...


А как его юзать?
Структура MINMAXINFO, указатель на которую передается в этом сообщении, содержит 5 полей:
ptReserved ,
ptMaxSize ,
ptMaxPosition ,
ptMinTrackSize ,
ptMaxTrackSize .

пробовал юзать все параметры - ни в одном не содержится ни текущей длины, ни ширины окна.

И в чем тута проблема, у меня все работает, все сообщения ловятся?
Код

...
void CMainFrame::OnGetMinMaxInfo(MINMAXINFO* pMMI)
{
    CFrameWnd::OnGetMinMaxInfo(pMMI);
    static UINT uistep=0;
    CRect rc;
    GetClientRect(&rc);
    if(rc.right > (100+(uistep*50) )  ){
        AfxMessageBox(rc.right);
        uistep++;
    }
}

Добавлено @ 21:36
Ширину получаешь как обычно...

Автор: Dreamer_0x01 15.11.2005, 12:30
Цитата(The @ 14.11.2005, 21:34)
GetClientRect(&rc);


Я же в самом начале поста объяснял, что эта функция работает только после окончания ресайзинга, иначе она вернет параметры окна до момента начала ресайзинга.
(имеется в виду при отключенной опции "отображать содержимое окна при перетаскивании" в настройках Виндоус).

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