Ресурсы страницы CMFCPropertySheet не изменяются в размере с динамическим макетом

Я действительно смущен. :(

Вот новый лист свойств:

#include "stdafx.h"
#include "resource.h"
#include "VisitsRotaMFCPropertySheet.h"


CVisitsRotaMFCPropertySheet::CVisitsRotaMFCPropertySheet()
    :CResizingMFCPropertySheet(_T("VisitsRota"), AFX_IDS_APP_TITLE, nullptr, 0)
{
    ConstructSheet();
}

CVisitsRotaMFCPropertySheet::~CVisitsRotaMFCPropertySheet()
{
}


BOOL CVisitsRotaMFCPropertySheet::OnInitDialog()
{
    BOOL bResult = CResizingMFCPropertySheet::OnInitDialog();

    m_Menu.LoadMenu(IDR_MENU);
    SetMenu(&m_Menu);

    return bResult;
}


void CVisitsRotaMFCPropertySheet::ConstructSheet()
{
    m_psh.dwFlags |= PSH_NOAPPLYNOW;

    AddPage(&m_ElderlyInfirmPage);
    AddPage(&m_ShepherdingPage);
}

Оно образовано от CResizingMFCPropertySheet. Это источник для этого класса:

https://www.dropbox.com/s/fzpfo4c3dpt6l51/ResizingMFCPropertySheet.cpp?dl=0

Теперь у меня есть две страницы в этом окне. Вот один из определений:

IDD_PAGE_ELDERLY_INFIRM DIALOGEX 0, 0, 420, 202
STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_CAPTION
CAPTION "Elderly && Infirm"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    GROUPBOX        "Elders ...",IDC_STATIC,6,7,132,188
    LISTBOX         IDC_LIST_BOOKSTUDY,12,18,120,147,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
    PUSHBUTTON      "Add",IDC_BUTTON_ADD_GROUP,12,172,35,18
    PUSHBUTTON      "Edit",IDC_BUTTON_EDIT_ELDER,55,172,35,18
    PUSHBUTTON      "Delete",IDC_BUTTON_DELETE_GROUP,97,172,35,18
    GROUPBOX        "Publishers ...",IDC_STATIC,144,7,132,188
    LISTBOX         IDC_LIST_ELDERY_INFIRM,150,18,120,147,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
    PUSHBUTTON      "Add",IDC_BUTTON_ADD_ELDERLY,150,172,35,18
    PUSHBUTTON      "Edit",IDC_BUTTON_EDIT_ELDERLY,193,172,35,18
    PUSHBUTTON      "Delete",IDC_BUTTON_DELETE_ELDERLY,235,172,35,18
    GROUPBOX        "Report Settings ...",IDC_STATIC,281,7,132,188
    LTEXT           "Starting month:",IDC_STATIC,286,18,120,8
    COMBOBOX        IDC_COMBO_MONTH,286,31,120,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
    LTEXT           "Number of months:",IDC_STATIC,286,49,78,12
    COMBOBOX        IDC_COMBO_NUM_MONTHS,376,49,30,96,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
    LTEXT           "Number of publishers to visit each month:",IDC_STATIC_NUM_PUB,286,65,84,18
    COMBOBOX        IDC_COMBO_PUB_PER_MONTH,376,66,30,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
    LTEXT           "Starting publisher:",IDC_STATIC,286,90,120,8
    COMBOBOX        IDC_COMBO_PUBLISHER,286,103,120,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
END

Он правильно настроен как страница, и я изначально установил данные управления через IDE:

IDD_PAGE_ELDERLY_INFIRM AFX_DIALOG_LAYOUT
BEGIN
    0,
    0, 0, 0, 100,
    0, 0, 0, 100,
    0, 100, 0, 0,
    0, 100, 0, 0,
    0, 100, 0, 0,
    0, 0, 0, 100,
    0, 0, 0, 100,
    0, 100, 0, 0,
    0, 100, 0, 0,
    0, 100, 0, 0,
    100, 0, 0, 100,
    100, 0, 0, 0,
    100, 0, 0, 0,
    100, 0, 0, 0,
    100, 0, 0, 0,
    100, 0, 0, 0,
    100, 0, 0, 0,
    100, 0, 0, 0,
    100, 0, 0, 0
END

Я настроил свое приложение CDialog так, чтобы оно вместо этого вызывало лист свойств. Размеры самого листа:

Лист

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

Обновлять

Я добавил это на одну из своих страниц:

void CElderlyInfirmPage::OnSize(UINT nType, int cx, int cy)
{
    CMFCPropertyPage::OnSize(nType, cx, cy);

    AfxMessageBox(_T("Size"));

    // TODO: Add your message handler code here
    auto pManager = GetDynamicLayout();
    if (pManager != nullptr)
    {
        AfxMessageBox(_T("Valid"));
    }
}

Это подтверждает, что на странице на самом деле нет диспетчера динамической компоновки. Делает только лист. Поэтому я думаю, что проблема в том, что мы не можем использовать механизм динамической компоновки.

Обновление 2

Я добился некоторого прогресса. Пример:

Изменен размер

Оказывается, страница свойств не загружает ресурсы динамического макета, как для диалога. Я начал создавать его вручную:

BOOL CElderlyInfirmPage::OnInitDialog()
{
    CMFCPropertyPage::OnInitDialog();

    // TODO:  Add extra initialization here
    ReadSettings();
    InitMonthCombo();

    // Init to THIS month
    COleDateTime    datNow = COleDateTime::GetCurrentTime();
    m_cbMonth.SetCurSel(datNow.GetMonth()-1);

    EnableDynamicLayout(TRUE);
    auto pManager = GetDynamicLayout();
    if (pManager != nullptr)
    {
        pManager->Create(this);
        pManager->AddItem(IDC_COMBO_MONTH, CMFCDynamicLayout::MoveHorizontal(100), CMFCDynamicLayout::SizeNone());
        pManager->AddItem(IDC_COMBO_NUM_MONTHS, CMFCDynamicLayout::MoveHorizontal(100), CMFCDynamicLayout::SizeNone());
        pManager->AddItem(IDC_COMBO_PUB_PER_MONTH, CMFCDynamicLayout::MoveHorizontal(100), CMFCDynamicLayout::SizeNone());
        pManager->AddItem(IDC_COMBO_PUBLISHER, CMFCDynamicLayout::MoveHorizontal(100), CMFCDynamicLayout::SizeNone());
    }

    return TRUE;  // return TRUE unless you set the focus to a control
    // EXCEPTION: OCX Property Pages should return FALSE
}

Как вы можете видеть, элементы управления теперь перемещаются, так что это прогресс. Но теперь проблема в том, что у меня много IDC_STATIC элементов управления на этих страницах, и я не хочу менять идентификационные номера. Это связано с тем, что в приложении уже есть переводы для локализации, и если я изменяю значения идентификатора, я взрываю переводы. Поэтому мне интересно, могу ли я использовать метод [CMFCDynamicLayout::LoadResource][3] для загрузки полных настроек из файла RC. Но я не могу понять, как вызвать здесь LoadResource. Я уверен, что это был бы ответ на этот вопрос.

Обновление 3

Я просто проследил код, и если вы посмотрите здесь:

LRESULT CPropertySheet::HandleInitDialog(WPARAM, LPARAM)
{
    LRESULT lResult = OnInitDialog();

    CMFCDynamicLayout* pDynamicLayout = GetDynamicLayout();
    if (pDynamicLayout != NULL)
    {
        CRect rectWindow;
        GetWindowRect(rectWindow);
        m_sizeMin = rectWindow.Size();

        for (CWnd *pChild = GetWindow(GW_CHILD); pChild->GetSafeHwnd() != NULL; pChild = pChild->GetWindow(GW_HWNDNEXT))
        {
            HWND hwndChild = pChild->GetSafeHwnd();
            if (!pDynamicLayout->HasItem(hwndChild))
            {
                if (pChild->SendMessage(WM_GETDLGCODE) & DLGC_BUTTON)
                {
                    pDynamicLayout->AddItem(hwndChild, CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100), CMFCDynamicLayout::SizeNone());
                }
                else if (IsLeftNavigationPane(hwndChild))
                {
                    pDynamicLayout->AddItem(hwndChild, CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeVertical(100));
                }
                else if (DYNAMIC_DOWNCAST(CPropertyPage, pChild) == NULL || CanAddPageToDynamicLayout())
                {
                    pDynamicLayout->AddItem(hwndChild, CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontalAndVertical(100, 100));
                }
            }
        }
    }

    return lResult;
}

Кажется, что на самом деле он не работает с макетом должным образом.

Я пытался использовать:

LoadDynamicLayoutResource(m_lpszTemplateName);

И я это проследил. В итоге это оказалось здесь:

BOOL CMFCDynamicLayout::LoadResource(CWnd* pHostWnd, LPVOID lpResource, DWORD dwSize)
{
    if (pHostWnd->GetSafeHwnd() == NULL || !::IsWindow(pHostWnd->GetSafeHwnd()) || lpResource == NULL)
    {
        return FALSE;
    }

    CMFCDynamicLayoutData layoutData;
    BOOL bResult = layoutData.ReadResource(lpResource, (UINT)dwSize);
    layoutData.ApplyLayoutDataTo(pHostWnd, FALSE);

    return bResult;
}

Ошибка при вызове ApplyLayoutDataTo при первом операторе if:

BOOL CMFCDynamicLayoutData::ApplyLayoutDataTo(CWnd* pHostWnd, BOOL bUpdate)
{
    if (pHostWnd->GetSafeHwnd() == NULL || m_listCtrls.IsEmpty())
    {
        return FALSE;
    }

    ASSERT_VALID(pHostWnd);

    pHostWnd->EnableDynamicLayout(FALSE);
    pHostWnd->EnableDynamicLayout();

m_listCtrls.IsEmpty() был пуст. Так что он все равно не прочитал его должным образом.

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


person Andrew Truckle    schedule 08.11.2018    source источник


Ответы (1)


Динамический макет уже включен для всех классов, производных от CDialog, которые вызывают класс по умолчанию CDialog::OnInitDialog, который, в свою очередь, использует CMFCDynamicLayout::LoadResource для чтения информации об изменении размера дочерних элементов управления.

В том числе CMFCPropertyPage. Информация уже загружена, поэтому, если вы вызовете EnableDynamicLayout, он удалит существующий объект и создаст новый. Просто удалите вызов EnableDynamicLayout.

Таким образом, pManager->Create(this); не понадобится, но вы можете оставить его там. Он ничего не сделает, потому что pManager уже создан, а метод знает, что нельзя создавать дважды.

CPropertySheet требует EnableDynamicLayout и pManager->Create. PropertySheet нельзя создать в диалоговом редакторе, поэтому MFC игнорирует изменение размера дочерних окон. Динамическое изменение размера должно быть реализовано вручную.

MCVE:

class CMyPage : public CMFCPropertyPage
{
    CButton bn;
    BOOL OnInitDialog()
    {
        CMFCPropertyPage::OnInitDialog();

        //add test button dynamically
        bn.Create(L"Test", WS_CHILD | WS_VISIBLE, CRect(0, 0, 100, 30), this, 301);

        auto pManager = GetDynamicLayout();
        if(pManager != nullptr)
        {
            pManager->AddItem(bn.GetDlgCtrlID(),
              CMFCDynamicLayout::MoveHorizontal(100),
              CMFCDynamicLayout::SizeNone());
        }
        return TRUE;
    }
};

class CMySheet :public CMFCPropertySheet
{
public:
    CMyPage Page1;
    CMySheet()
    {
        Page1.Construct(IDD_PAGE1);
        AddPage(&Page1);
    }

    static int CALLBACK XmnPropSheetCallback(HWND hWnd, UINT message, LPARAM lParam)
    {
        extern int CALLBACK AfxPropSheetCallback(HWND, UINT message, LPARAM lParam);
        // XMN: Call MFC's callback
        int nRes = AfxPropSheetCallback(hWnd, message, lParam);
        if (message == PSCB_PRECREATE)
            ((LPDLGTEMPLATE)lParam)->style |= (DS_3DLOOK | DS_SETFONT
                | WS_THICKFRAME | WS_SYSMENU | WS_POPUP | WS_VISIBLE | WS_CAPTION);
        return nRes;
    }

    BOOL OnInitDialog()
    {
        BOOL res = CMFCPropertySheet::OnInitDialog();
        EnableDynamicLayout(TRUE);//required for propertysheet
        auto pManager = GetDynamicLayout();
        if(pManager)
        {
            pManager->Create(this);
            for(CWnd *child = GetWindow(GW_CHILD); 
              child; child = child->GetWindow(GW_HWNDNEXT))
            {
                if(child->SendMessage(WM_GETDLGCODE) & DLGC_BUTTON)
                    pManager->AddItem(*child, 
                      CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100),
                      CMFCDynamicLayout::SizeNone());
                else
                    pManager->AddItem(*child, 
                      CMFCDynamicLayout::MoveNone(),
                      CMFCDynamicLayout::SizeHorizontalAndVertical(100, 100));
            }
        }

        return res;
    }

    INT_PTR DoModal()
    {
        // Hook into property sheet creation code
        m_psh.dwFlags |= PSH_USECALLBACK;
        m_psh.pfnCallback = XmnPropSheetCallback;
        return CMFCPropertySheet::DoModal();
    }
};

...
CMySheet sh;
sh.DoModal();
person Barmak Shemirani    schedule 08.11.2018
comment
Я попробую. Как насчет использования LoadResource? - person Andrew Truckle; 09.11.2018
comment
Мне все еще нужно было позвонить EnableDynamicLayout(TRUE);, иначе менеджером будет null. Я переместил его перед строкой, которую вы указали. Но мне все еще приходится вручную добавлять элементы управления. Поэтому я хотел бы использовать LoadResource, если это возможно. - person Andrew Truckle; 09.11.2018
comment
Мне все еще приходилось использовать вызов Create, иначе я получил утверждение. Я должен объяснить, что этому проекту уже несколько лет, и я только начал его использовать в 2017 году. Но я не вижу других строк кода, которые могут мне понадобиться. - person Andrew Truckle; 09.11.2018
comment
Это модальный или немодальный лист свойств? Обратите внимание, что вызов EnableDynamicLayout требуется в CPropertySheet, но не требуется в CPropertyPage, на самом деле вам следует избегать его вызова после OnInitDialog. См. версию MSVE в редакции. - person Barmak Shemirani; 09.11.2018
comment
Это модель. Лист — это мое главное окно. Если я не вызываю его на странице, то у меня нет менеджера и ничего не движется. - person Andrew Truckle; 09.11.2018
comment
Ах. Я заметил, что вы звоните Construct. Я этого не делал. Я попробую позже. ???? - person Andrew Truckle; 09.11.2018
comment
Вам может не понадобиться Construct. Существуют разные способы открытия листа свойств, ваш способ, очевидно, работает. Просто попробуйте весь пример, чтобы увидеть, работает ли изменение размера. - person Barmak Shemirani; 09.11.2018
comment
Я попробую позже сегодня утром. У меня первая рабочая смена через минуту. - person Andrew Truckle; 09.11.2018
comment
Могу я просто спросить ... если вы добавите элемент управления на свою страницу через ресурсы ide, а не динамически, он все еще будет работать? - person Andrew Truckle; 09.11.2018
comment
Да, это работает в моем примере. CMFCDynamicLayout::LoadResource вызывается автоматически в CMFCPropertyPage::OnInitDialog() - person Barmak Shemirani; 09.11.2018
comment
Кажется, это мой базовый класс. Я начал новый проект, использовал ваш базовый класс (с небольшими изменениями) и скопировал свои ресурсы, и они работают! Так что мне просто нужно перенести все в новый проект. По крайней мере, переводы должны быть в порядке, поскольку я использовал исходный файл resource.h. Спасибо. - person Andrew Truckle; 09.11.2018