I have a multi-purpose CDialog that supports resizing. It can display content in 3 variations.
Variation 1:
Variation 2:
Variation 3:
The dialogue controls are using the dynamic layout settings from the resource editor.
Variation 1 is fine and need no changes.
Variation 2 does not display the combo and date button. As a result I would like the "Text will ..." label to be down at the bottom and the "edit" box to be taller.
Variation 3 has a similar issue where the date button should move to the bottom and the edit box be taller.
Can this be achieved by changing the dynamic layout in code?
Update
I tried this in OnInitDialog:
if (!m_bShowWeekCombo)
{
CRect rctCombo;
m_cbWeek.GetWindowRect(rctCombo);
ScreenToClient(rctCombo);
CRect rctNote;
m_staticInfo.GetWindowRect(rctNote);
ScreenToClient(rctNote);
m_staticInfo.MoveWindow(rctCombo.left, rctCombo.top, rctNote.Width(), rctNote.Height());
}
At first I thought it was working:
The note is now at the bottom. But as soon as I resize the window:
The note has reverted to the original position.
I know I have this answer to a similar issue but do I really have to re-build the whole layout?
Update 2
if (!m_bShowWeekCombo)
{
CRect rctEdit;
m_editText.GetWindowRect(rctEdit);
ScreenToClient(rctEdit);
CRect rctCombo;
m_cbWeek.GetWindowRect(rctCombo);
ScreenToClient(rctCombo);
CRect rctNote;
m_staticInfo.GetWindowRect(rctNote);
ScreenToClient(rctNote);
//m_staticInfo.MoveWindow(rctCombo.left, rctCombo.top, rctNote.Width(), rctNote.Height());
m_staticInfo.SetWindowPos(NULL, rctCombo.left, rctCombo.top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
m_editText.SetWindowPos(NULL, 0, 0, rctEdit.Width(), rctEdit.Height() + (rctCombo.top - rctNote.top),
SWP_NOMOVE | SWP_NOZORDER);
if (m_pDynamicLayout)
{
if (!m_pDynamicLayout->HasItem(m_staticInfo.m_hWnd))
{
m_pDynamicLayout->AddItem(m_staticInfo.m_hWnd,
CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeHorizontal(100));
}
else
{
TRACE(L"item already has dynamic move/size\n");
}
if (!m_pDynamicLayout->HasItem(m_editText.m_hWnd))
{
m_pDynamicLayout->AddItem(m_editText.m_hWnd,
CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontalAndVertical(100, 100));
}
else
{
TRACE(L"item already has dynamic move/size\n");
}
}
}
When I try the above the control width is the original width, even though the dialog had restored to wider dialog width.
CMFCDynamicLayout reads the dialog resource, it stores the coordinates for the child controls as well as their dynamic resize/move properties.
This is all done in CDialog::OnInitDialog. If you move the child control, example, m_staticInfo then CMFCDynamicLayout doesn't know you moved/resized the control. So upon the next dialog resize request, CMFCDynamicLayout uses the old values.
You can add dynamic resize/move for all controls expcept m_staticInfo and other controls which you intend to move manually. Then add m_staticInfo separately:
BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
CRect rctCombo;
m_cbWeek.GetWindowRect(rctCombo);
ScreenToClient(rctCombo);
m_staticInfo.SetWindowPos(NULL, rctCombo.left, rctCombo.top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
if(m_pDynamicLayout)
{
if(!m_pDynamicLayout->HasItem(m_staticInfo.m_hWnd))
{
m_pDynamicLayout->AddItem(m_staticInfo.m_hWnd,
CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeNone());
}
else
{
TRACE(L"item already has dynamic move/size\n");
AfxDebugBreak(0);
}
}
return 1;
}
Internally, MFC calls LoadDynamicLayoutResource(m_lpszTemplateName) to initialize dynamic size/move. But documentation says not to use this method directly.
Clarification
If you are using a dialog that supports resizing then you must remember to calculate the new width and height when you move the control to the new position. You would then use one of the appropriate Size calls. For example:
// The EDIT control height now needs increasing
iNewEditHeight = rctButton.top - iTextMarginY - rctEdit.top;
m_editText.SetWindowPos(nullptr, 0, 0, iNewWidth, iNewEditHeight, SWP_NOMOVE | SWP_NOZORDER);
It is up to you to workout how you want your control initially re-sized.
Then, in OnInitDialog I called a new method:
void CEditTextDlg::SetupDynamicLayout()
{
if (m_pDynamicLayout != nullptr)
{
m_pDynamicLayout->AddItem(IDC_BUTTON_INSERT_DATE,
CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100), CMFCDynamicLayout::SizeNone());
m_pDynamicLayout->AddItem(IDC_STATIC_INFO,
CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeHorizontal(100));
m_pDynamicLayout->AddItem(IDC_EDIT_TEXT,
CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontalAndVertical(100, 100));
}
}
If you don't set the width correctly when using SetWindowPos and only use SizeNone() it will not resize correctly.
Related
Added Update 2 on 3/29/2022
My MFC app has the menubar, toolbar, and status bar all working correctly. I am at the point I'm adding creature features...one that really annoys me is the choice of permanently locking them or allow them to float at runtime...I've looked high and low and I've yet to see an example where a user can allow this feature to be dynamically changed once the app is up and running. The first function is from the app which works correctly. NOTE: I have the items allowing docking commented out as this was my test code..I know that those are the lines that enable or disable docking at run time....-> // enable docking
int CMainFrame::OnCreate(LPCREATESTRUCT pptCreate)
{
CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerVS2005));
if ( -1 == CMDIFrameWndEx::OnCreate(pptCreate) )
return -1;
// create menu bar
if ( !m_wndMenuBar.Create(this) )
return -1;
m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);
// prevent the menu bar from taking the focus on activation
CMFCPopupMenu::SetForceMenuFocus(FALSE);
// set the visual manager and style based on persisted value
theApp.m_nAppLook = theApp.GetInt(_T("ApplicationLook"), ID_VIEW_APPLOOK_VS_2008);
OnApplicationLook(theApp.m_nAppLook);
// create tool bar
if ( !m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_GRIPPER|CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME) )
return -1;
// create status bar
if ( !m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(miIndicators, sizeof(miIndicators)/sizeof(UINT)) )
return -1;
// enable docking
//m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
//m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
m_wndToolBar.EnableToolTips(TRUE);
EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndMenuBar);
DockPane(&m_wndToolBar);
// enable Visual Studio 2005 style docking window behavior
CDockingManager::SetDockingMode(DT_SMART);
// enable Visual Studio 2005 style docking window auto-hide behavior
EnableAutoHidePanes(CBRS_ALIGN_ANY);
// create docking windows
if (!CreateDockingWindows())
{
TRACE0("Failed to create docking windows\n");
return -1;
}
m_wndOutput.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndOutput);
return 0;
}
So I've created a menu item that toggles a flag to turn "docking" on and off. The variable switches correctly.. but the command execution of turning the dock "3 dots" on the left side of the menubar and toolbar always remain...i.e. never goes from Float to Dock mode and vice versa. The logic of the toggle works as I can see in my debugging status window the entry points of TRUE and FALSE are seen each time I click the menu. So I guess the question is, can these items be dynamically turned from FLOAT to DOCK without destroying the window and trying to reinit it which would be a visual disaster?
void CMainFrame::UserDockingBarsOption()
{
CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd();
pMainFrame->m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--CMainFrame::UserDockingBarsOption()"));
if (UserDockingFlag == TRUE)
{
m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
UserDockingFlag = FALSE;
pMainFrame->m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--Inside True"));
}
else
{
m_wndMenuBar.EnableDocking(FALSE);
m_wndToolBar.EnableDocking(FALSE);
EnableDocking(FALSE);
UserDockingFlag = TRUE;
pMainFrame->m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--Inside False"));
}
}
Update 1:
So like my other posts, I'm back after a family matter. I revisited this and because I'm using:
CMFCMenuBar m_wndMenuBar;
CMFCToolBar m_wndToolBar;
and the only member I can find is IsFloat as a question, not an execute. So it was mentioned I can float or dock it via:
https://learn.microsoft.com/en-us/cpp/mfc/docking-and-floating-toolbars?view=msvc-160
Every example that I see is done within the OnCreate which does not help me as I can't recall it after it's already the child is already constructed. So my thought was to invalidate the toolbar, destroy the toolbar, recreate the tool bar with the user switch of dock or float, and then reorganize the toolbars. I have "most" of this working except for the actual switch control aspect to rebuild the menu with the user option of floating the toolbar or docking it.
What I did in my property sheet is created two buttons for two seperate functions to see if I can get each piece of this to work. One button is destroy the toolbar, the other button is create i.e. recreate the toobar....and both of those "work".
This is all being done in the MainFrame.cpp for simplicity of the member variables, I may move it later if I can get it working.
There are the two functions:
Destroy Function:
void CMainFrame::OnToolBarDestroy()
{
m_wndToolBar.Invalidate();
m_wndToolBar.DestroyWindow();
RecalcLayout();
//OnToolBarCreate();
//m_wndToolBar.ShowPane(FALSE, FALSE, FALSE); // Hide toolbar
}
Recreate Toolbar outside of OnCreate
int CMainFrame::OnToolBarCreate()
{
// TODO: Add your implementation code here.
if (m_wndToolBar)
{
m_wndOutput.AddStringStatusTab(_T("Error: Icon toolbar is already active, action cancelled"));
m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--Error: Icon toolbar is already active, action cancelled"));
return -1;
}
// Create ToolBar toolbar
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to Create Dialog ToolBar\n");
return -1;
}
CRect rcClientOld;
CRect rcClientNew;
GetClientRect(rcClientOld);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0, reposQuery, rcClientNew);
//m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
//DockPane(&m_wndToolBar);
RecalcLayout();
//m_wndToolBar.ShowPane(TRUE, FALSE, FALSE); // Show toolbar
return -1;
}
So what is commented out that would make it "work" assuming following the OnCreate code order up to this point:
//m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
//DockPane(&m_wndToolBar);
What happening is the successful destroy, and successful rebuild of the menu.
What I need for switch control is to either have the line active for floating or commented out for docked which I have also done in OnCreate but since this is for the rebuilt menu, I need this to follow that method switching in or out this line: //m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); ...but when I do....the toolbar gets all garbled up / missing all icons and I have to nuke the registry keys for the workspace to "try again"...
Any ideas on how I can make this work? I feel like I'm This close to getting it...I'm just unsure what I may be missing here to get across he goal line.
Update 2
So I got the toolbar to come out of docked to float with modified code below without restarting the app. But there are issues, it seems to be drawing "ghost" bars underneath. If I double click the 3 dots, it will detach and the bar will float....GREAT! But, it leave a "mirror" behind....if I double click the floating menu, it redocks to the frame.
I know I'm close, I'm just missing a piece to finish this off. I've added the code below and a few screenshots of wha I'm seeing.....the point is I is "working", but I'm missing something...can anyone help?
OnDestroy method updated:
int CMainFrame::OnToolBarDestroy()
{
//if (!m_wndToolBar)
//{
// m_wndOutput.AddStringStatusTab(_T("Error: Icon toolbar is already removed, action cancelled"));
// m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--Error: Icon toolbar is already removed, action cancelled"));
// return -1;
//}
//m_wndToolBar.Invalidate();
//m_wndMenuBar.DestroyWindow();
m_wndToolBar.DestroyWindow();
//m_wndToolBar.AdjustDockingLayout();
//RecalcLayout();
//OnToolBarCreate();
//return 0;
CRect rcClientOld;
CRect rcClientNew;
GetClientRect(rcClientOld);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0, reposQuery, rcClientNew);
//m_wndToolBar.ShowPane(FALSE, FALSE, TRUE); // Hide toolbar
RecalcLayout();
return 0;
}
OnCreate method updated:
int CMainFrame::OnToolBarCreate()
{
enter code here// TODO: Add your implementation code here.
if (m_wndToolBar)
{
m_wndOutput.AddStringStatusTab(_T("Error: Icon toolbar is already active, action cancelled"));
m_wndOutput.AddStringDebugTab(_T("Debug: MainFrame--Error: Icon toolbar is already active, action cancelled"));
return -1;
}
CMFCPopupMenu::SetForceMenuFocus(FALSE);
//CMDIChildWndEx::m_bEnableFloatingBars = TRUE;
// Create ToolBar toolbar
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to Create Dialog ToolBar\n");
return -1;
}
m_wndMenuBar.EnableDocking(FALSE);
m_wndToolBar.EnableDocking(FALSE);
m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndMenuBar);
DockPane(&m_wndToolBar);
// DockPaneLeftOf(&m_wndToolBar);
CDockingManager::SetDockingMode(DT_SMART);
EnableAutoHidePanes(CBRS_ALIGN_ANY);
CRect rcClientOld;
CRect rcClientNew;
GetClientRect(rcClientOld);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0, reposQuery, rcClientNew);
m_wndOutput.AddStringStatusTab(_T("I'm created 1 times"));
//m_wndToolBar.ResetAll();
RecalcLayout();
//m_wndToolBar.ShowPane(TRUE, FALSE, TRUE); // Show toolbar
return 0;
}
The images below:
Start from OnCreate with the panes properly "docked" mode
Destroyed ToolBar
ToolBar is switched from dock to float mode with Update 2 code. Ghost bars shown in the image. Trying to solve that issue.
Soooooo....anyone have any ideas on how I can solve this riddle?
Thanks!
Chris
I thought it be nice to add a Status Bar with percentage and other information to a CDialogEx that is used for viewing an image. But it doesn't seem that you can simply use a CMFCStatusBar or a CStatusBar and have it just work.
I found various samples, but none of them have the statusbar outside the client area and moves as resized? The different methods simply create a statusbar and it ends up hidden under a horizontal scrollbar and if you resize the window, the statusbar is sitting there in the middle of the dialog.
Is there an easy way or full example of having a statusbar on a CDialogEx that can be resized like a normal window?
Is there an easy way or full example of having a statusbar on a CDialogEx that can be resized like a normal window?
Yes! Once you have created the status bar you can add it to the dynamic layout for resizing:
//This is where we actually draw it on the screen
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST,
ID_INDICATOR_MEETING_TYPE);
GetDynamicLayout()->AddItem(m_StatusBar.GetSafeHwnd(),
CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeHorizontal(100));
I have a status bar (not CMFCStatusBar as it will not work, but CStatusBar is OK) on two dialogs in my application.
When Dynamic Layout is not automatically enabled
Here is an updated example for when Dynamic Layout is not automatically enabled for you (CDialogEx with no controls):
BOOL CMyDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
if (!m_StatusBar.Create(this)) {
TRACE0("Failed to create status bar\n");
return -1;
}
m_StatusBar.SetIndicators(indicators, _countof(indicators));
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);
EnableDynamicLayout();
auto pdlmanager=GetDynamicLayout();
if (pdlmanager) {
if (pdlmanager->Create(this)) {
pdlmanager->AddItem(m_StatusBar.GetSafeHwnd(), CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeHorizontal(100));
}
}
// return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
return TRUE;
}
Catering for horizontal scroll bars
NIf you have a horizontal scrollbar the StatusBar will end up above it; therefore you may have to create separate CWnd and add it to the dynamic layout (it would also be the nIDLeftOver of the RepositionBars()).
Here's how you can add the a "view" window for the contents so scrollbars can be contained within the view area:
BOOL CMyDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
if (!m_StatusBar.Create(this)) {
TRACE0("Failed to create status bar\n");
return -1;
}
m_StatusBar.SetIndicators(indicators, _countof(indicators));
CRect rc;
GetClientRect(&rc);
CString clsname=AfxRegisterWndClass(0);
m_ImageView.Create(clsname, _T(""), WS_CHILD | WS_VISIBLE, rc, this, IDC_MY_VIEW);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, IDC_MY_VIEW);
EnableDynamicLayout();
auto pdlmanager=GetDynamicLayout();
if (pdlmanager) {
if (pdlmanager->Create(this)) {
pdlmanager->AddItem(m_StatusBar.GetSafeHwnd(), CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeHorizontal(100));
pdlmanager->AddItem(m_ImageView.GetSafeHwnd(), CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontalAndVertical(100, 100));
}
}
// return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
return TRUE;
}
So, I have a CDialog resource that I have had for a long time and I decided to add a statusbar to it. Here is the resource:
All controls fit nicely in the dialog. Now, at runtime this is what it looks like:
The tutorial I followed was here and for the most part it works. Here is my setup code:
///////////////////////////////
m_StatusBar.Create(this); //We create the status bar
m_StatusBar.SetIndicators(indicators, 2); //Set the number of panes
CRect rect;
GetClientRect(&rect);
//Size the two panes
m_StatusBar.SetPaneInfo(0, ID_INDICATOR_DATE,
SBPS_NORMAL, 200);
m_StatusBar.SetPaneInfo(1, ID_INDICATOR_MEETING_TYPE, SBPS_STRETCH, 0);
//This is where we actually draw it on the screen
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST,
ID_INDICATOR_DATE);
GetDynamicLayout()->AddItem(m_StatusBar.GetSafeHwnd(),
CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeHorizontal(100));
///////////////////////////////
I have tried without WindowsBlinds and the issues are still there.
So my issues are:
1/ The controls are overlapping the status bar. How do I accurately set these controls in the resource editor so this issue won't happen? How should it be resolved? Hit n miss?
2/ My dialog supports resizing by using the dynamic layouts and it has the OBM_SIZE in the bottom right:
void CResizingDialog::InitialiseResizeIcon(CBitmap& rBmpResize, CStatic& rLblResize, CWnd* pDialog)
{
CRect rcIcon, rcClient;
if (pDialog != nullptr)
{
rBmpResize.LoadOEMBitmap(OBM_SIZE);
rLblResize.Create(nullptr, WS_CHILD | WS_VISIBLE | SS_BITMAP,
CRect(0, 0, 16, 16), pDialog, IDC_STATIC_RESIZE);
rLblResize.SetBitmap(rBmpResize);
pDialog->GetClientRect(rcClient);
rLblResize.GetClientRect(rcIcon);
rLblResize.SetWindowPos(&CWnd::wndTop,
rcClient.right - rcIcon.Width(),
rcClient.bottom - rcIcon.Height(), 0, 0, SWP_NOSIZE);
CMFCDynamicLayout *pDynamicLayout = pDialog->GetDynamicLayout();
if (pDynamicLayout != nullptr)
{
CMFCDynamicLayout::MoveSettings moveSettings = CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100);
CMFCDynamicLayout::SizeSettings sizeSettings = CMFCDynamicLayout::SizeNone();
pDynamicLayout->AddItem(rLblResize.GetSafeHwnd(), moveSettings, sizeSettings);
}
}
}
How do I avoid the issue that you can see there now in the bottom right?
Update
It looked like I should use CreateEx And use this style SBARS_SIZEGRIP. Then stop creating my own resize icon. I assume the two grippers will look the same. So this might be one of the answers.
I tried using the above flag but unfortunately I can't use it:
This gripper is not consistent with the other one I am using so I need to keep my original one instead.
Update 2
I now realise that the gripper is always created anyway, so I had two grippers there! I have now derived my own statusbar class and switched off the default gripper:
BOOL CCreateReportStatusBar::PreCreateWindow(CREATESTRUCT& cs)
{
BOOL bRet = CStatusBar::PreCreateWindow(cs);
cs.style &= ~SBARS_SIZEGRIP;
return bRet;
}
So now I only have the one gripper. But my two issues still remain.
Update 3
I stumbled over this. In theory if I override this DrawGripper method I should be able to render my own gripper instead. Doesn't work. The method is never called.
Update 4
I decided not to fight the system. I have let the status bardraw the themes gripper and I have adjusted my resizing dialog class to also draw the themes gripper. So all is good.
I'm writing an MFC dialog with multiple controls. I have currently have a CWnd that is placed on the right half of the dialog. Upon clicking an edit button, the child CWnd is resized to take up a larger portion of the dialog.
However, now when I try to resize the window, the child CWnd jumps back to where it was originally. I cannot seem to figure out how to keep it in it's new position when resizing.
Relevant code:
OnInit() {
//the grouper rectangle
CRect rectHTMLGrouper;
m_grpHTMLbox.GetWindowRect(&rectHTMLGrouper);
ScreenToClient(&rectHTMLGrouper);
//the new rectangle to use for positioning
CRect rectHtml;
rectHtml.left = rectHTMLGrouper.left + PREVIEW_EDITOR_LEFT;
rectHtml.right = rectHTMLGrouper.right - PREVIEW_EDITOR_RIGHT;
rectHtml.top = rectHTMLGrouper.top + PREVIEW_EDITOR_TOP;
rectHtml.bottom = rectHTMLGrouper.bottom - PREVIEW_EDITOR_BOTTOM;
//this inits my editor and sets the position
m_wHtmlEditor.CreateHtmlEditor(rectHTMLGrouper, this, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN);
//CodeJock - XTREMEToolkit Call for SetResize Logic
SetResize(m_wHtmlEditor.GetDlgCtrlID(), LEFT_PANE_RESIZE, 0, 1, 1);
m_wHtmlEditor.SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOMOVE);
}
OnEditMode() {
//enlarge the editor to take up the full dialog
CRect parentClientRect;
m_wHtmlEditor.GetParent()->GetClientRect(&parentClientRect);
m_wHtmlEditor.SetWindowPos(&CWnd::wndTop, parentClientRect.left + edgePadding, parentClientRect.top + editorTopPadding, parentClientRect.right - (edgePadding * 2), parentClientRect.bottom - bottomPadding, SWP_NOOWNERZORDER);
return;
}
Upon clicking an edit button, the child CWnd is resized to take up a
larger portion of the dialog.
You have to handle that same resize in your OnSize() (ON_WM_SIZE()) message handler (using some kind of BOOL member to keep track of the child window's status).
OnSize() is called repeatedly while resizing the dialog.
Example:
// .h
BOOL m_bIsEditMode;
// .cpp
// keep track of m_bIsEditMode
void CMyDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
if (m_bIsEditMode) {
//enlarge the editor to take up the full dialog
m_wHtmlEditor.MoveWindow (0, 0, cx, cy);
}
}
I have a resizable dialog that contains a CTabCtrl, the tab control has 4 tabs that when clicked on displays one of four different CTreeCtrls.
I have derived a class from CTabCtrl, which keeps track of its "child" controls like so:
...
class Container: public CTabCtrl {
vector<CWnd*> _children;
....
int Container::AddTab(CWnd* Child) {
CString txt;Child->GetWindowText(txt);
_children.push_back(Child);
int idx = this->InsertItem(this->GetItemCount(), txt, 0);
if(idx == 0) {
CRect c;
this->GetWindowRect(&c);
GetParent()->ScreenToClient(&c);
this->AdjustRect(FALSE, c);
Child->SetWindowPos(&wndTop, c.left, c.top, c.Width(), c.Height(), SWP_SHOWWINDOW);
this->SetCurSel(idx);
} else Child->ShowWindow(SW_HIDE);
return idx;
}
And I attempt to draw the child controls like so:
void Container::OnTabChanging(NMHDR*, LRESULT* pResult) { // hide the changed from tab
int selected = this->GetCurSel();
if(selected != -1)
{
// move old window to bottom of the zorder and hide
_children[selected]->SetWindowPos(&wndBottom, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_HIDEWINDOW);
ASSERT(!_children[selected]->IsWindowVisible());
}
*pResult = 0;
}
// show the child for the tab being changed to
void CNodeContainer::OnTabChanged(NMHDR* pNMHDR, LRESULT* pResult) {
int selected = this->GetCurSel();
ASSERT(selected!=-1);
CRect c;
this->GetWindowRect(&c);
GetParent()->ScreenToClient(&c);
this->AdjustRect(FALSE, c);
_children[selected]->SetWindowPos(&wndTop, c.left, c.top, c.Width(), c.Height(), SWP_SHOWWINDOW|SWP_FRAMECHANGED);
*pResult = 0;
}
However the child controls, whilst they appear, don't always draw correctly, they sort of mix up their content together and only show the right content when i click on them (the actual tree controls).
Is this the best way of drawing and moving windows around in the zorder, what am I missing?
Many thanks
bg
Instead of just changing the z-order of your children, completely hide every child except the top one. I use the same approach in a custom CTabCtrl and it works fine.
Its fixed now - the problem came from the fact that the in the resize code for the tabctrl, I was using movewindow to move the child windows into place - This was changing the zorder of the child windows.
This could solve the problem after your window or tab apears. Try to use
this->RedrawWindow();
In OnTabChanging() function before it returns.