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

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



菡冰

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下编译通过。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 要使 ListView 像树一样可以自由伸展收缩,您可以使用带有分层结构的布局管理器,例如 ExpandableListView。ExpandableListView 是一种特殊的 ListView,它允许您在列表中展示分层结构数据,并且用户可以展开或折叠每个分组。 要使用 ExpandableListView,您需要创建一个继承自 ExpandableListAdapter 的自定义适配器,并将该适配器设置为 ExpandableListView 的适配器。您还需要提供数据源以及如何在列表中呈现该数据的视图。最后,您可以使用 ExpandableListView 的 setOnGroupClickListener() 和 setOnChildClickListener() 方法来处理用户单击分组或子项时的事件。 这是一个简单的示例,展示了如何使用 ExpandableListView 创建一个可以自由伸展收缩的列表: ``` // 创建自定义适配器 class MyExpandableListAdapter extends BaseExpandableListAdapter { // 提供数据源和呈现数据的视图 // 省略其他代码 } // 在布局文件中声明 ExpandableListView <ExpandableListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> // 在 Activity 中设置适配器并处理事件 ExpandableListView listView = findViewById(R.id.list_view); MyExpandableListAdapter adapter = new MyExpandableListAdapter(data); listView.setAdapter(adapter); listView.setOnGroupClickListener(new OnGroupClickListener ### 回答2: 如果需要让ListView像树一样可以自由伸展收缩,可以使用ExpandableListView来实现。ExpandableListView是Android提供的一种可展开和折叠子列表的视图组件。 首先,在布局文件中,将原本的ListView替换为ExpandableListView,并为其指定唯一的id,例如: ``` <ExpandableListView android:id="@+id/expandableListView" ...> </ExpandableListView> ``` 然后,在代码中,创建一个适配器Adapter,继承BaseExpandableListAdapter,并重写相关的方法。这些方法包括getGroupCount()、getChildrenCount()、getGroup()、getChild()等。在这些方法里,可以根据需要编写逻辑来返回父项和子项的数据。 接着,设置适配器给ExpandableListView。例如: ``` ExpandableListView expandableListView = findViewById(R.id.expandableListView); expandableListView.setAdapter(adapter); ``` 最后,可以通过设置监听器来实现展开和折叠的功能。例如,可以为ExpandableListView设置OnGroupClickListener和OnChildClickListener来监听父项和子项的点击事件,然后在相应的回调方法中编写展开和折叠的代码。 总结起来,使用ExpandableListView可以实现ListView像树一样自由伸展收缩的效果。通过自定义适配器来管理父项和子项的数据,并使用监听器来监控点击事件以展开和折叠列表项。这样,用户就可以像操作树形结构一样,自由地展开和收缩ListView的项。 ### 回答3: 要实现一个可以自由伸展收缩的ListView效果,可以使用ExpandableListView来达到这个目的。ExpandableListView是Android提供的一个可以展开和折叠的列表视图组件,可以让列表项像树一样展开和收缩。 首先,需要在布局文件中将ListView替换为ExpandableListView组件。然后,创建一个ExpandableListAdapter来管理列表项的展开和收缩状态。 在实现ExpandableListAdapter时,需要重写以下几个方法: 1. getGroupCount()方法用于返回分组项的数量。 2. getChildrenCount()方法用于返回指定分组项下的子项数量。 3. getGroupView()方法用于获取分组项视图。 4. getChildView()方法用于获取子项视图。 在getGroupView()方法中,可以设置一个点击事件监听器,当用户点击分组项时,根据当前的状态展开或收缩子项。可以使用ExpandableListView的expandGroup()和collapseGroup()方法分别展开和收缩指定位置的分组。 通过以上步骤,就可以实现一个可以自由伸展收缩的ExpandableListView。用户可以点击分组项来展开或收缩子项,从而实现类似树结构的效果。 总结:使用ExpandableListView可以很方便地实现一个类似树的列表视图,用户可以自由地展开和收缩分组项以及其对应的子项。这样可以提供更好的用户体验,并方便用户查看和管理大量的列表数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蝈蝈俊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值