制作伸展自如、收缩随意的对话框

发表于:2007-07-14来源:作者:点击数: 标签:
作者: 菡冰 Visual C++以其可视化的编程风格成为目前 Windows 程序设计与 开发 的主流开发工具。而对话框在Visual C++编程中使用的尤其多。诸如模式对话框、无模式对话框、基于对话框的应用程序等。绝大部分的VC++的书籍中都花费大量的篇幅与笔墨来讲解对话
作者: 菡冰

  Visual C++以其可视化的编程风格成为目前Windows程序设计与开发的主流开发工具。而对话框在Visual C++编程中使用的尤其多。诸如模式对话框、无模式对话框、基于对话框的应用程序等。绝大部分的VC++的书籍中都花费大量的篇幅与笔墨来讲解对话框,这充分证明了对话框在Windows应用程序中的作用。
很多人可能都用过Bitware软件,不知大家还记不记得其界面对话框就可以伸展自如。按下一个按钮,对话框就向水平方向或垂直方向扩展。再按一下按钮,对话框又回复到原来的大小。其实这并不是一个很复杂的问题,下面我们就来讲解如何制作伸展自如的对话框。
1 打开VisualC++工作台,新建工程设为aaa。

2 创建基于对话框的应用程序如下所示:
其余选择皆为缺省即可。

3 在对话框资源中增加控件资源,如下图所示:

其中,最靠右边的一排控件和最靠近下面的两排控件将在对话框伸展或收缩时显示出来或被遮盖。并且为了示例方便,我们有意将他们的值对应起来。并且我们需要通过ClassWizard给每个控件分别关联成员变量,如下所示:
参考DoDataExchange()函数我们就可以知道每个控件所关联的变量了,如下所示:
DDX_Text(pDX, IDC_HEIGHT, m_wHeight);
DDX_Text(pDX, IDC_STREAM_ID, m_wStreamID);
DDX_Text(pDX, IDC_WIDTH, m_wWidth);
DDX_Text(pDX, IDC_SEQUENCE_ORDER, m_wSequenceOrder);
DDX_Text(pDX, IDC_MAX_RATE, m_dwMaxRate);
DDX_Text(pDX, IDC_MIN_RATE, m_dwMinRate);
DDX_Text(pDX, IDC_HEIGHT2, m_wHeight2);
DDX_Text(pDX, IDC_MAX_RATE2, m_dwMaxRate2);
DDX_Text(pDX, IDC_MIN_RATE2, m_dwMinRate2);
DDX_Text(pDX, IDC_SEQUENCE_ORDER2, m_wSequenceOrder2);
DDX_Text(pDX, IDC_STREAM_ID2, m_wStreamID2);
DDX_Text(pDX, IDC_WIDTH2, m_wWidth2);
DDX_Check(pDX, IDC_HORIZONTAL, m_bHorizontal);
DDX_Check(pDX, IDC_VERTICAL, m_bVertical);
实际上,我们也可以不用ClassWizard而直接将上面的一段代码copy到DoDataExchange()函数的
//{{AFX_DATA_MAP(CAaaDlg)
......
//}}AFX_DATA_MAP
之间,(注意一定要在“//{{AFX_DATA_MAP(CAaaDlg)”与“//}}AFX_DATA_MAP”之间)。
同时在aaaDlg.h文件中,在
//{{AFX_DATA(CAaaDlg)
enum { IDD = IDD_AAA_DIALOG };
......
//}}AFX_DATA
之间增加如下变量定义即可:
(注意一定要在“//{{AFX_DATA(CAaaDlg)”与“//}}AFX_DATA”之间)
UINT m_wHeight;
UINT m_wStreamID;
UINT m_wWidth;
UINT m_wSequenceOrder;
DWORD m_dwMaxRate;
DWORD m_dwMinRate;
UINT m_wHeight2;
DWORD m_dwMaxRate2;
DWORD m_dwMinRate2;
UINT m_wSequenceOrder2;
UINT m_wStreamID2;
UINT m_wWidth2;
BOOL m_bHorizontal;
BOOL m_bVertical;

5 在完成上面的步骤后,我们就可以定义几个新的变量用来保存窗口伸展状态时的信息以及收缩状态时的信息。如下:
WORD m_wOrigrinWidth; //原始状态下的窗口宽度
WORD m_wReducedWidth; //收缩状态下的窗口宽度

WORD m_wOrigrinHeight; //原始状态下的窗口高度
WORD m_wReducedHeight; //收缩状态下的窗口高度

WORD m_screenWidth; //屏幕宽度
WORD m_screenHeight; //屏幕高度

在完成以上所有的步骤后,就可以对窗口的伸展与收缩进行随心所欲的控制了,首先我们来侃侃具体的代码,下面再进行具体的解释。代码为:
CenterWindow(NULL);

m_screenWidth = GetSystemMetrics(SM_CXSCREEN);
m_screenHeight = GetSystemMetrics(SM_CYSCREEN);

WINDOWPLACEMENT* lpwndpl=new WINDOWPLACEMENT;
GetWindowPlacement(lpwndpl);
m_wOrigrinWidth = lpwndpl->rcNormalPosition.right;
m_wOrigrinWidth -= lpwndpl->rcNormalPosition.left;
m_wOrigrinHeight = lpwndpl->rcNormalPosition.bottom;
m_wOrigrinHeight -= lpwndpl->rcNormalPosition.top;

LPRECT lpRect1,lpRect2;
lpRect1=new RECT;
lpRect2=new RECT;
GetDlgItem(IDC_PROGRESS_BAR)->GetWindowRect(lpRect1);
GetDlgItem(IDC_STREAM_ID)->GetWindowRect(lpRect2);

lpwndpl->rcNormalPosition.right=(lpRect1->right+lpRect2->left)/2;
m_wReducedWidth = lpwndpl->rcNormalPosition.right;
m_wReducedWidth -= lpwndpl->rcNormalPosition.left;

GetDlgItem(IDC_PROGRESS_BAR)->GetWindowRect(lpRect1);
GetDlgItem(IDC_SEQUENCE_ORDER2)->GetWindowRect(lpRect2);
lpwndpl->rcNormalPosition.bottom=(lpRect1->bottom+lpRect2->top)/2;
m_wReducedHeight = lpwndpl->rcNormalPosition.bottom;
m_wReducedHeight -= lpwndpl->rcNormalPosition.top;

delete lpRect1;
delete lpRect2;

if(m_bHorizontal == TRUE)
{
lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wOrigrinWidth;

lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
}
else
{
lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wReducedWidth;

lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
}

if(m_bVertical == TRUE)
{
lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wReducedWidth;

lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wOrigrinHeight;
}
else
{

lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wReducedWidth;

lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
}

SetWindowPlacement(lpwndpl);

上面这段代码首先将窗口置于屏幕中间,这可以通过函数CenterWindow(GetDesktopWindow()) 来实现,函数 CenterWindow()的用法为:
void CenterWindow( CWnd* pAlternateOwner = NULL );
其中参数pAlternateOwner指向所想居中的窗口的指针。
然后利用函数GetSystemMetrics( int nIndex )得到系统当前设置如屏幕分辨率等。
nIndexs= SM_CXSCREEN 时函数返回屏幕的宽度;返回值单位为像素点。
nIndexs= SM_CYSCREEN 时函数返回屏幕的高度;返回值单位为像素点。
函数BOOL GetWindowPlacement( WINDOWPLACEMENT* lpwndpl ) 是最重要的。他的参数为一个指向结构变量WINDOWPLACEMENT的指针(lpwndpl);其中WINDOWPLACEMENT结构变量数据结构具体为:
typedef struct tagWINDOWPLACEMENT { /* wndpl */
UINT length;
UINT flags;
UINT showCmd;
POINT ptMinPosition;
POINT ptMaxPosition;
RECT rcNormalPosition;
} WINDOWPLACEMENT;
他包含了窗口在屏幕上的定位信息。其中成员变量的含义为:
length:指结构变量的长度,单位字节。
flags: 标志值,控制窗口最小化或窗口还原的方法,可以取如下值:
WPF_SETMINPOSITION:指定窗口最小化时的x位置和y位置。
WPF_RESTORETOMAXIMIZED:指定窗口以最大化方式还原,尽管可能窗口并不是在最大化时最小化的。不改变窗口的缺省还原方式。
showCmd:指定窗口的当前显示状态。可以取值:
SW_HIDE:隐藏窗口并激活另一窗口。
SW_MINIMIZE:最小化指定窗口并激活系统窗口列表中最顶层窗口。
SW_RESTORE:激活并显示窗口,如果窗口处于最小化或最大化状态,则窗口还原到原始大小和位置。
SW_SHOW:以窗口的当前大小和位置激活并显示窗口。
SW_SHOWMAXIMIZED:以最大化方式激活并显示窗口。
SW_SHOWMINIMIZED:以图标方式激活并显示窗口。
SW_SHOWMINNOACTIVE:以图标方式窗口。 但不改变窗口的活动状态。
SW_SHOWNA:以窗口的当前状态显示窗口。
SW_SHOWNOACTIVATE:以窗口最近一次的大小和位置显示窗口。 但不改变窗口的活 动状态。
SW_SHOWNORMAL:激活并显示窗口。如果窗口被最大化或最小化,则窗口还原到原始大小和位置。
ptMinPosition:指定窗口最小化时的左伤角坐标。
ptMaxPosition:指定窗口最大化时的左伤角坐标。
rcNormalPosition:指定窗口在还原时的坐标。
通过灵活使用函数GetWindowPlacement()就可以得到窗口的配置信息。
看到这,可能有些读者已经想到了GetWindowPlacement()函数的姐妹函数SetWindowPlacement(),不用多说,其用法如下:
BOOL SetWindowPlacement( WINDOWPLACEMENT* lpwndpl );
显然,通过函数SetWindowPlacement(),再加以简单的计算,我们就可以来设置窗口的位置、大小以及状态等,从而可以自如地控制窗口显示与否以及窗口的大小、位置等。这里我们就不再多解释了。

6 利用ClassWizard对控件IDC_HORIZONTAL和IDC_VERTICAL增加消息映射BB_CLICKED,

并分别在消息映射函数中增加如下代码如下:
void CAaaDlg::OnHorizontal()
{
// TODO: Add your control notification handler code here
m_bHorizontal = !m_bHorizontal;

UpdateData(FALSE);

WINDOWPLACEMENT* lpwndpl=new WINDOWPLACEMENT;
GetWindowPlacement(lpwndpl);
if(m_bHorizontal == TRUE)
{
lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wOrigrinWidth;
/*
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
*/
}
else
{
lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wReducedWidth;
/*
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
*/
}

SetWindowPlacement(lpwndpl);
delete lpwndpl;
}

void CAaaDlg::OnVertical()
{
// TODO: Add your control notification handler code here
m_bVertical = !m_bVertical;

UpdateData(FALSE);

WINDOWPLACEMENT* lpwndpl=new WINDOWPLACEMENT;
GetWindowPlacement(lpwndpl);

if(m_bVertical == TRUE)
{
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wOrigrinHeight;
}
else
{
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
}

SetWindowPlacement(lpwndpl);
delete lpwndpl;
}

7 最后利用ClassWizard对控件IDC_BEGIN_SIMULATE增加消息映射BB_CLICKED。在这里我们模拟了一个100次循环的随机数显示程序。具体大妈如下:
void CAaaDlg::OnBeginSimulate()
{
// TODO: Add your control notification handler code here
srand((unsigned)time(NULL));
char temp[10];
SetDlgItemText(IDC_STATIC11,"Now Beginning ...");
for(int i=0;i<m_maxRange;i++)
{
m_pProgressCtrl->SetPos(i);
m_wSequenceOrder = m_wSequenceOrder2 = i;
m_wStreamID = m_wStreamID2 = rand();
m_wHeight = m_wHeight2 = rand();
m_wWidth = m_wWidth2 = rand();
m_dwMaxRate = m_dwMaxRate2 = rand();
m_dwMinRate = m_dwMinRate2 = rand();
switch(i%4)
{
case 0:
sprintf(temp,"欢 迎 使 用");
break;
case 1:
sprintf(temp,"迎 使 用 欢");
break;
case 2:
sprintf(temp,"使 用 欢 迎");
break;
case 3:
sprintf(temp,"用 欢 迎 使");
break;
}
SetDlgItemText(IDC_WELCOME,temp);
UpdateData(FALSE);
UpdateWindow();
Sleep(50);
}
SetDlgItemText(IDC_WELCOME,"欢 迎 使 用");
SetDlgItemText(IDC_STATIC11,"Now Finnished ...");
}

8 完成以上所有的步骤之后,我们就可以编译程序并运行。运行结果如下:

(a) (b)
(a): 程序启动时对话框状态
(b): 点击Horizontal框后对话框状态。

(c) (d)
(c): 点击Vertical框后对话框状态。
(d): 点击BeginSimulating按钮后系统模拟运行对话框状态。

在本程序中,我们还用到了一些其它的技巧如修改窗口标题,进程状态条的显示、动态字符串显示以及不通过ClassWizard而直接通过在.cpp和.h文件中增加代码的方法来关联控件与成员变量和消息映射等,这些都是一些很实用的技巧,读者可以参考上面的代码以及源程序细细体会,这里我们就不多说了。
程序源工程文件见aaa.zip。在VisualC++6.0下编译通过。

原文转自:http://www.ltesting.net