VS2015 中的MFC对话框动态布局
2017-06-12 12:38
363 查看
From: http://mariusbancila.ro/blog/2015/07/27/dynamic-dialog-layout-for-mfc-in-visual-c-2015/ DynamicDialog
Layout for MFC in Visual C++ 2015
Marius Bancila C++, MFC, Uncategorized2015-07-27
In Visual Studio 2015 MFC comes with a newfeatures (something that has rarely happen in recent years): support fordynamic dialog layout. That means library support for moving and resizingcontrols on a dialog. In this
article I will show how this feature works.
Suppose we have the following dialog:
What we want is that the controls on the dialog move (thebuttons) or resize (the group box, edit and the list) when the dialog isresized:
Resized dialog
The resource editor provides support for this, but it canalso be done programmatically. If you open the properties of a control there isa new category called Dynamic Layout that allows you to selecta moving
and a sizing type.
The options you have for both moving and resizing are: None, Horizontal Vertical,and Both. These options should be self explanatory. However, theimportant thing to notice is the value for the X and Y axes moving andresizing: these
are ratios, not dialog units or pixels, having a value between1 and 100 and defining how much a control is moved or resized when the hostdialog changes size.
Now, to enable the layout shown in the the example abovewe need to do the following:
· fullyresize (100%) the group box and list box both horizontally and vertically
· fullyresize the edit control horizontally
· completely(100%) move the OK button vertically
· completelymove the Add button horizontally
· completelymove the Clear and Cancel buttons both horizontally and vertically
It is pretty simple to put values into the dynamic layout settings for eachcontrol. When you build and run and resize the dialog box the controls move orresize accordingly.
These dynamic layout settings are put in the resourcescript (.rc file) of the application. For the example above it looks like this:
In this definition IDD_MFCDYNLAYOUTDEMO_DIALOG is the identifier of the dialog forwhich the settings are defined and the numbers
in the BEGIN-END block represent:
· thefirst line is a header containing the version number on the structure (0 inthis version)
· theconsecutive lines are the dynamic layout settings (move and size ratios) foreach control on the dialog, corresponding to the order the controls weredefined for the dialog
in the resource script file.
These settings are loaded into a CMFCDynamicLayout object (seeafxlayout.h/cpp). This is
done in the OnInitDialog method of the CDialog class as shown below:
C++
Note: for CPaneDialog, CDialogBar and CFormView on
the other hand this is done in HandleInitDialog.
This LoadDynamicLayoutResource is actually a member of CWnd which
contains other methods forworking with dynamic layouts:
· EnableDynamicLayout: enables or disables layout manager for a window
· IsDynamicLayoutEnabled: indicates if layout management is enabled for a window
· GetDynamicLayout:retrieves a pointer to layout manager
· ResizeDynamicLayout: readjust the position of the controls handled by thedynamic layout manager as a response to WM_SIZE
· InitDynamicLayout: initializes dynamic layout manager as a response to the WM_CREATE message
C++
These methods allow you to enable or disable the dynamiclayout management on the fly.
1. Initiallythe dynamic layout management is set so the controls move and resize when thedialog is resized.
2. Disablethe dynamic layout management and the child controls are no longer adjusted.
3. Re-enablethe dynamic layout management and it works again.
The catch here is that just calling CWnd::EnableDynamicLayout won’t work because this method onlydeletes and recreates the CMFCDynamicLayout instance.
C++
Just like CDialog::OnInitDialog you’d have to call CWnd::LoadDynamicLayoutResource.
Therefore, the correct code for enablingand disabling dynamic layout management should look like this:
C++
As mentioned earlier, setting the move and size valuesfor dynamic layout management can be done programmatically using the CMFCDynamicLayout class.
This is importantwhen the controls are created dynamically and not in the resource template.What you have to do is:
· createthe CMFCDynamicLayout object
· storethe host window (the dialog) in that object
· add thechild controls with their move and size settings
The following code provides the same dynamic layoutfunctionality as shown earlier except that all is set from code. Note that youmust call EnableDynamicLayoutHelper from OnInitDialog.
C++
Actually the same code as above can be expresseddifferently with the help of several static methods from CMFCDynamicLayout that
create instances of MoveSettings and SizeSettings.
C++
One important thing to notice here is that this code doesnot call CWnd::LoadDynamicLayoutResource because there are no settings
in theresource script file. All these settings are only provided programmatically inthis case.
When controls have to move or resizecompletely (100%) across one or both axes, setting the right values for thelayout is straight forward. It gets complicated though when controls are notpositioned sideways or need
to move or resize with more complicated rules.Let’s take an example where the OK and Cancel buttons are positioned at thebottom vertically and centered horizontally. When the dialog resizes theyshould retain the original size, but they should always remain
at the centerbottom.
In this case the Y ratio for move is again 100. But whatis the move ratio on the X axis? To determine you need a paper and pen.Basically we need to find how much do the buttons move on X when the widthincreases by
100 units. That is the ratio we have to set.
Initially the dialog has 251 units, that means two halvesof 125 and 126 units. We want to keep the buttons apart by 10 units. What meansthe OK button is left aligned at 70 units and the Cancel button is left alightat
130 units.
Then we increase the size of the dialog by 100 units. It’s now 351 and thehaves have 175 and 176. The buttons are still 10 units apart and their with isstill 50 units each. That means the OK button is now left aligned at 120 units,and the Cancel button is left
aligned at 180 units.
The conclusion is their both left margin has moved 50 units, and that is thevalue we need to set for the X ratio of their move setting. (Remember, thevalue is a ratio, but 50 units out of 100 units is also 50%.)
What if the OK and Cancel buttons shouldboth be aligned at the center of their each half on the X axis and always preservethe margins? In other words they should change like this:
In this example, initially, the dialog has 231 units andthat means two halves of 115 and 116 units. The buttons have both 60 unitswidth, so they are aligned at 27 or 28 units to the margins.
When the width of the dialog increases by 100 units to 331 units, the twohalves have 165 and 166 units. The buttons preserve their margins, so their newwidth is 110 units.
(Notice that the image above is stretched and the margins may be misleading.)
The conclusion is that:
· The OKbutton did not move horizontally, but it increased its width from 60 units to110 units, that means 50%.
· TheCancel button moved horizontally and is now left aligned at 193 units insteadof the original 143 units. That means it moved by 50% horizontally. Its sizedincreased from
60 units to 110 units, that also means 50%.
With those values set the buttons are resized andpositioned as intended.
For more information see MFC Dynamic Dialog Layout.
Demo source code:
MFC Dynamic Layout Management - demo 1 (1114)
MFC Dynamic Layout Management - demo 2 (975)
MFC Dynamic Layout Management - demo 3 (813)
Layout for MFC in Visual C++ 2015
Marius Bancila C++, MFC, Uncategorized2015-07-27
In Visual Studio 2015 MFC comes with a newfeatures (something that has rarely happen in recent years): support fordynamic dialog layout. That means library support for moving and resizingcontrols on a dialog. In this
article I will show how this feature works.
Suppose we have the following dialog:
What we want is that the controls on the dialog move (thebuttons) or resize (the group box, edit and the list) when the dialog isresized:
Resized dialog
The resource editor provides support for this, but it canalso be done programmatically. If you open the properties of a control there isa new category called Dynamic Layout that allows you to selecta moving
and a sizing type.
The options you have for both moving and resizing are: None, Horizontal Vertical,and Both. These options should be self explanatory. However, theimportant thing to notice is the value for the X and Y axes moving andresizing: these
are ratios, not dialog units or pixels, having a value between1 and 100 and defining how much a control is moved or resized when the hostdialog changes size.
Now, to enable the layout shown in the the example abovewe need to do the following:
· fullyresize (100%) the group box and list box both horizontally and vertically
· fullyresize the edit control horizontally
· completely(100%) move the OK button vertically
· completelymove the Add button horizontally
· completelymove the Clear and Cancel buttons both horizontally and vertically
It is pretty simple to put values into the dynamic layout settings for eachcontrol. When you build and run and resize the dialog box the controls move orresize accordingly.
These dynamic layout settings are put in the resourcescript (.rc file) of the application. For the example above it looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ///////////////////////////////////////////////////////////////////////////// // // AFX_DIALOG_LAYOUT // IDD_MFCDYNLAYOUTDEMO_DIALOG AFX_DIALOG_LAYOUT BEGIN 0, 0, 100, 0, 0, 100, 100, 0, 0, 0, 0, 100, 100, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 100, 100, 100, 100, 0, 0, 100, 0, 0, 0 END |
in the BEGIN-END block represent:
· thefirst line is a header containing the version number on the structure (0 inthis version)
· theconsecutive lines are the dynamic layout settings (move and size ratios) foreach control on the dialog, corresponding to the order the controls weredefined for the dialog
in the resource script file.
These settings are loaded into a CMFCDynamicLayout object (seeafxlayout.h/cpp). This is
done in the OnInitDialog method of the CDialog class as shown below:
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | BOOL CDialog::OnInitDialog() { // execute dialog RT_DLGINIT resource BOOL bDlgInit; if (m_lpDialogInit != NULL) bDlgInit = ExecuteDlgInit(m_lpDialogInit); else bDlgInit = ExecuteDlgInit(m_lpszTemplateName); if (!bDlgInit) { TRACE(traceAppMsg, 0, "Warning: ExecuteDlgInit failed during dialog init.\n"); EndDialog(-1); return FALSE; } LoadDynamicLayoutResource(m_lpszTemplateName); |
the other hand this is done in HandleInitDialog.
This LoadDynamicLayoutResource is actually a member of CWnd which
contains other methods forworking with dynamic layouts:
· EnableDynamicLayout: enables or disables layout manager for a window
· IsDynamicLayoutEnabled: indicates if layout management is enabled for a window
· GetDynamicLayout:retrieves a pointer to layout manager
· ResizeDynamicLayout: readjust the position of the controls handled by thedynamic layout manager as a response to WM_SIZE
· InitDynamicLayout: initializes dynamic layout manager as a response to the WM_CREATE message
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // controls dynamic layout: /// <summary> /// Enables or disables layout manager for a window.</summary> /// <param name="bEnable"> TRUE - enable layout management, FALSE - disable layout management.</param> void EnableDynamicLayout(BOOL bEnable = TRUE); /// <summary> /// This function returns TRUE, if layout management is enabled for a window; otherwise FALSE.</summary> /// <remarks> /// Call EnableDynamicLayout in order to enable or disable layout management for a window.</remarks> /// <returns> /// TRUE, if layout management is enabled for a window; otherwise FALSE.</returns> BOOL IsDynamicLayoutEnabled() const { return m_pDynamicLayout != NULL; } /// <summary> /// Call this function to retrieve a pointer to layout manager.</summary> /// <remarks> /// Call EnableDynamicLayout in order to enable or disable layout management for a window.</remarks> /// <returns> /// Returns a pointer to the window layout manager or NULL if layout wasn't enabled.</returns> CMFCDynamicLayout* GetDynamicLayout() { return m_pDynamicLayout; } /// <summary> /// The method is called to adjust positions of child controls. /// It recalculates positions of child controls if layout management is enabled for a window.</summary> virtual void ResizeDynamicLayout(); void InitDynamicLayout(); BOOL LoadDynamicLayoutResource(LPCTSTR lpszResourceName); |
1. Initiallythe dynamic layout management is set so the controls move and resize when thedialog is resized.
2. Disablethe dynamic layout management and the child controls are no longer adjusted.
3. Re-enablethe dynamic layout management and it works again.
The catch here is that just calling CWnd::EnableDynamicLayout won’t work because this method onlydeletes and recreates the CMFCDynamicLayout instance.
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void CWnd::EnableDynamicLayout(BOOL bEnable) { if (m_pDynamicLayout != NULL) { delete m_pDynamicLayout; m_pDynamicLayout = NULL; } if (!bEnable) { return; } m_pDynamicLayout = new CMFCDynamicLayout; } |
Therefore, the correct code for enablingand disabling dynamic layout management should look like this:
C++
1 2 3 4 5 6 7 8 9 10 11 12 | void CMFCDynLayoutDemoDlg::EnableDynamicLayoutHelper(bool const enable) { if (enable && this->IsDynamicLayoutEnabled()) return; this->EnableDynamicLayout(enable ? TRUE : FALSE); if (enable) { this->LoadDynamicLayoutResource(m_lpszTemplateName); } } |
This is importantwhen the controls are created dynamically and not in the resource template.What you have to do is:
· createthe CMFCDynamicLayout object
· storethe host window (the dialog) in that object
· add thechild controls with their move and size settings
The following code provides the same dynamic layoutfunctionality as shown earlier except that all is set from code. Note that youmust call EnableDynamicLayoutHelper from OnInitDialog.
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | void CMFCDynLayoutDemoDlg::EnableDynamicLayoutHelper(bool const enable) { if (enable && this->IsDynamicLayoutEnabled()) return; this->EnableDynamicLayout(enable ? TRUE : FALSE); if (enable) { SetupDynamicLayout(); } } void CMFCDynLayoutDemoDlg::SetupDynamicLayout() { auto manager = this->GetDynamicLayout(); if (manager != nullptr) { auto movenone = CMFCDynamicLayout::MoveSettings{}; auto moveboth100 = CMFCDynamicLayout::MoveSettings {}; moveboth100.m_nXRatio = 100; moveboth100.m_nYRatio = 100; auto movex100 = CMFCDynamicLayout::MoveSettings {}; movex100.m_nXRatio = 100; auto movey100 = CMFCDynamicLayout::MoveSettings {}; movey100.m_nYRatio = 100; auto sizenone = CMFCDynamicLayout::SizeSettings{}; auto sizeboth100 = CMFCDynamicLayout::SizeSettings{}; sizeboth100.m_nXRatio = 100; sizeboth100.m_nYRatio = 100; auto sizex100 = CMFCDynamicLayout::SizeSettings{}; sizex100.m_nXRatio = 100; manager->Create(this); manager->AddItem(IDC_STATIC_GROUPBOX, movenone, sizeboth100); manager->AddItem(IDC_LIST1, movenone, sizeboth100); manager->AddItem(IDC_EDIT1, movenone, sizex100); manager->AddItem(IDC_BUTTON_ADD, movex100, sizenone); manager->AddItem(IDC_BUTTON_CLEAR, moveboth100, sizenone); manager->AddItem(IDOK, movey100, sizenone); manager->AddItem(IDCANCEL, moveboth100, sizenone); } } |
create instances of MoveSettings and SizeSettings.
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | void CMFCDynLayoutDemoDlg::SetupDynamicLayout() { auto manager = this->GetDynamicLayout(); if (manager != nullptr) { manager->Create(this); manager->AddItem(IDC_STATIC_GROUPBOX, CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontal(100)); manager->AddItem(IDC_LIST1, CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontalAndVertical(100, 100)); manager->AddItem(IDC_EDIT1, CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontal(100)); manager->AddItem(IDC_BUTTON_ADD, CMFCDynamicLayout::MoveHorizontal(100), CMFCDynamicLayout::SizeNone()); manager->AddItem(IDC_BUTTON_CLEAR, CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100), CMFCDynamicLayout::SizeNone()); manager->AddItem(IDOK, CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeNone()); manager->AddItem(IDCANCEL, CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100), CMFCDynamicLayout::SizeNone()); } } |
in theresource script file. All these settings are only provided programmatically inthis case.
When controls have to move or resizecompletely (100%) across one or both axes, setting the right values for thelayout is straight forward. It gets complicated though when controls are notpositioned sideways or need
to move or resize with more complicated rules.Let’s take an example where the OK and Cancel buttons are positioned at thebottom vertically and centered horizontally. When the dialog resizes theyshould retain the original size, but they should always remain
at the centerbottom.
100 units. That is the ratio we have to set.
Initially the dialog has 251 units, that means two halvesof 125 and 126 units. We want to keep the buttons apart by 10 units. What meansthe OK button is left aligned at 70 units and the Cancel button is left alightat
130 units.
Then we increase the size of the dialog by 100 units. It’s now 351 and thehaves have 175 and 176. The buttons are still 10 units apart and their with isstill 50 units each. That means the OK button is now left aligned at 120 units,and the Cancel button is left
aligned at 180 units.
The conclusion is their both left margin has moved 50 units, and that is thevalue we need to set for the X ratio of their move setting. (Remember, thevalue is a ratio, but 50 units out of 100 units is also 50%.)
What if the OK and Cancel buttons shouldboth be aligned at the center of their each half on the X axis and always preservethe margins? In other words they should change like this:
When the width of the dialog increases by 100 units to 331 units, the twohalves have 165 and 166 units. The buttons preserve their margins, so their newwidth is 110 units.
(Notice that the image above is stretched and the margins may be misleading.)
The conclusion is that:
· The OKbutton did not move horizontally, but it increased its width from 60 units to110 units, that means 50%.
· TheCancel button moved horizontally and is now left aligned at 193 units insteadof the original 143 units. That means it moved by 50% horizontally. Its sizedincreased from
60 units to 110 units, that also means 50%.
With those values set the buttons are resized andpositioned as intended.
For more information see MFC Dynamic Dialog Layout.
Demo source code:
MFC Dynamic Layout Management - demo 1 (1114)
MFC Dynamic Layout Management - demo 2 (975)
MFC Dynamic Layout Management - demo 3 (813)
相关文章推荐
- VS2015 VS2017 MFC 动态布局的改进
- VS2013MFC对话框工程学习笔记二 - 了结布局和一些基本的窗口组件
- VS2013MFC对话框工程学习笔记十一 - 动态改变一个控件标题
- vs2015 MFC中动态显示图片
- 【VS开发】VS2010 MFC中控件、对话框等背景颜色动态修改的方法
- 【VS开发】MFC动态设置对话框属性 弹出或嵌入
- VS2015对话框工程,MFC图标的设置
- 【初级】VS2015/MFC动态创建工具栏1(使用图标创建工具栏)
- VS2015编写的MFC上位机,波特率可调,可动态显示曲线,可显示三维
- MFC改变对话框的大小,以及子控件的动态改变
- 动态切换采用 CSplitterWnd 静态划分的视图布局(MFC)
- 重拾MFC小细节之动态更新对话框背景图片
- MFC 对话框中动态创建N级菜单以及响应事件
- [ MFC ] 对话框动态控件的创建 在Picture Control控件上显示图片 [大三TJB_708]
- VS2012 基于对话框 MFC应用程序 重置工具箱
- MFC小程序003------MFC使用WebBrowser组件,在对话框中创建滚动视图,动态创建一个静态文本控件并设置鼠标单击的消息响应
- MFC 动态修改对话框的标题
- MFC中控件大小随对话框大小动态变化
- <MFC笔记> VS2013动态库文件的创建及其使用详解
- MFC 对话框中控件动态生成与删除