CMFCPropertySheet "Страница" ресурсы не меняются с динамическим макетом - PullRequest
0 голосов
/ 08 ноября 2018

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

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

#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, чтобы вместо него вызывал страницу свойств. Размеры самого листа:

Sheet

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

Обновление

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

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

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

Resized

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

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() был пуст. Так что он все равно не прочитал это правильно.

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

1 Ответ

0 голосов
/ 08 ноября 2018

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

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

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

CPropertySheet требует EnableDynamicLayout и pManager->Create. Таблица свойств не может быть разработана в диалоговом редакторе, поэтому 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();
...