I am really confused. :(
Here is a new property sheet:
#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);
}
It is derived from CResizingMFCPropertySheet. This is the source for that class:
https://www.dropbox.com/s/fzpfo4c3dpt6l51/ResizingMFCPropertySheet.cpp?dl=0
Now, I have two pages in this window. Here is one for the definitions:
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
It is correctly set up as a page and I have initially set the control data via the 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
I have adjusted my CDialog application to invoke the property sheet instead. The sheet itself sizes:
Why is the sheet control not automatically resizing? I just don't get it. My other application uses the same base class and yet all those property pages correctly resizing the controls etc using the dynamic layout features.
Update
I added this to one of my pages:
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"));
}
}
It confirms that the "page" does not actually have a dynamic layout manager. Only the sheet does. So I think the problem is the fact that we can't use dynamic layout mechanism.
Update 2
I made some progress. Example:
It turns out that the property page doesn't seem to load the dynamic layout resources like it does for a dialog. I started to create it manually:
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
}
As you can see, the controls move now so it is progress. But the problem now is that I have a lot of IDC_STATIC controls on these pages and I don't want to change the ID numbers. This is because the application already has translations for localization and if I change the ID values I blow up the translations. So I am wondering if I can use the [CMFCDynamicLayout::LoadResource][3] method to load the complete settings from the RC file. But I can't work out how to call LoadResource here. I am sure it would be the answer to this question.
Update 3
I just traced the code and if you look here:
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;
}
It does not seem to actually work with the layout properly.
I tried to use:
LoadDynamicLayoutResource(m_lpszTemplateName);
And I traced it. It eventually ended up here:
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;
}
It failed on the ApplyLayoutDataTo call, on the first if statement:
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() was empty. So it hadn't read it in properly anyway.
I think I have no choice but to assign IDs to all my controls, even the static ones and manually build the dynamic layout up. Unless you have other ideas.
Dynamic layout is already be enabled for all classes derived from CDialog which call the default CDialog::OnInitDialog, which in turn uses CMFCDynamicLayout::LoadResource to read resizing information for child controls.
That include CMFCPropertyPage. The information is already loaded, so if you call EnableDynamicLayout it deletes the existing object and creates a new one. Just remove the call to EnableDynamicLayout.
This way pManager->Create(this); won't be necessary, but you can keep it in there. It won't do anything because pManager already created and the method knows not to create twice.
CPropertySheet does require EnableDynamicLayout and pManager->Create. PropertySheet cannot be designed in dialog editor, so MFC ignores resizing for its child windows. Dynamic resizing has to be implemented manually.
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();
I am trying to create a button dynamically. I have read some other resource and make the following code:
BEGIN_MESSAGE_MAP(Cdynamic_button_sdiView, CView)
// Standard printing commands
ON_BN_CLICKED(MYBUTTONID, OnMyBN_Click)
END_MESSAGE_MAP()
void Cdynamic_button_sdiView::OnInitialUpdate()
{
CView::OnInitialUpdate();
m_Button.Create(_T("Rearrange"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, CRect(0, 0, 128, 32), this, MYBUTTONID); // here will create a button
}
I can make a button successfully when I start the MFC application. The problem is that when I try to open a new document by clicking:
I get an error and my app crashed at m_Button.Create(_T("Rearrange"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, CRect(0, 0, 128, 32), this, MYBUTTONID);
I solved the problem with the following code:
Cdynamic_button_sdiView::Cdynamic_button_sdiView()
{
// TODO: add construction code here
m_Button = NULL;
}
Cdynamic_button_sdiView::~Cdynamic_button_sdiView()
{
if (m_Button != NULL)
delete m_Button;
}
void Cdynamic_button_sdiView::OnInitialUpdate()
{
CView::OnInitialUpdate();
if (m_Button != NULL)
delete m_Button;
m_Button = new CButton;
m_Button->Create(_T("Rearrange"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, CRect(0, 0, 128, 32), this, MYBUTTONID); // here will create a button
}
May be the problem is I should not re-create the window inside the OnInitialUpdate()
Its been a while i have stopped working on VC++ and now i have some project which i have again started my development on VC++.... I am having a strange issue right now the label of the control is not getting change at all here is my code for initialization of mfcoutlookbar
BOOL CMainFrame::CreateOutlookBar(CMFCOutlookBar& bar, UINT uiID, CMFCShellTreeCtrl& tree, CCalendarBar& calendar,CListCtrlBar &listctrl,int nInitialWidth)
{
bar.SetMode2003();
BOOL bNameValid;
CString strTemp;
bNameValid = strTemp.LoadString(IDS_SHORTCUTS);
ASSERT(bNameValid);
if (!bar.Create(strTemp, this, CRect(0, 0, nInitialWidth, 32000), uiID, WS_CHILD | WS_VISIBLE | CBRS_LEFT))
{
return FALSE; // fail to create
}
CMFCOutlookBarTabCtrl* pOutlookBar = (CMFCOutlookBarTabCtrl*)bar.GetUnderlyingWindow();
if (pOutlookBar == NULL)
{
ASSERT(FALSE);
return FALSE;
}
pOutlookBar->EnableInPlaceEdit(FALSE); //we dont want editing
static UINT uiPageID = 1;
// can float, can autohide, can resize, CAN NOT CLOSE
DWORD dwStyle = AFX_CBRS_FLOAT | AFX_CBRS_AUTOHIDE | AFX_CBRS_RESIZE;
CRect rectDummy(0, 0, 0, 0);
const DWORD dwTreeStyle = WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS;
//Create tree
tree.Create(dwTreeStyle, rectDummy, &bar, 1200);
//bNameValid = strTemp.LoadString(IDS_FOLDERS);
ASSERT(bNameValid);
pOutlookBar->AddControl(&tree, L"Folders", 2, TRUE, dwStyle);
//create calender
calendar.Create(rectDummy, &bar, 1201);
// bNameValid = strTemp.LoadString(IDS_CALENDAR);
// ASSERT(bNameValid);
pOutlookBar->AddControl(&calendar, L"CALLLLLL",1, TRUE, dwStyle);
//create list control bar
listctrl.Create(rectDummy,&bar,1202);
// bNameValid = strTemp.LoadString(IDS_DASHBOARD);//Dashboard to check the statistics and statical reports
// ASSERT(bNameValid);
pOutlookBar->AddControl(&listctrl, L"Some", 0, TRUE, dwStyle); //the digit represent icon
bar.SetPaneStyle(bar.GetPaneStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
pOutlookBar->SetImageList(theApp.m_bHiColorIcons ? IDB_PAGES_HC : IDB_PAGES, 24);
pOutlookBar->SetToolbarImageList(theApp.m_bHiColorIcons ? IDB_PAGES_SMALL_HC : IDB_PAGES_SMALL, 16);
pOutlookBar->RecalcLayout();
BOOL bAnimation = theApp.GetInt(_T("OutlookAnimation"), TRUE);
CMFCOutlookBarTabCtrl::EnableAnimation(bAnimation);
bar.SetButtonsFont(&afxGlobalData.fontBold);
return TRUE;
}
if you see i am using two different controls by providing them the text label e,,g,
pOutlookBar->AddControl(&listctrl, L"Some", 0, TRUE, dwStyle);
and the other is
pOutlookBar->AddControl(&calendar, L"CALLLLLL",1, TRUE, dwStyle);
now the label "Some" and "CALLLLLL" is not being displayed i have tried everything but i dont know why its still displaying the default text of
"Calenders" and "Folders"
I have changed all the text inside String table as well ...this code is generated by Visual Studio C++ by wizard using Office 2000.
What is the mistake i am doing???
Okay finally figured out the problem is the cache , the outlookbased bar for any application save the caption and label in registry...so cleaning your solution rebuilding it again and again will not give you the right tabs captions unless you delete the registry entries for that application.
HKEY_USERS\S-1-5-21-3153867254-3211561466-2840709754-1000\Software\Local AppWizard-Generated Applications\test2
and then delete this test2 project key ....after that run your application it will display the correct label.