您的位置:首页 > 其它

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++MFCUncategorized2015-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 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++
 
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);
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++
 
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);
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++
 
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;
}
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++
 
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);
   }
}
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++
 
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);
   }
}
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++
 
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());
   }
}
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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息