标签:
前面讲过了两种风格的文件夹选择对话框的实现方法,见文章《Win7界面的和API实现的老界面文件夹选择对话框代码实现详细讲解》。而老界面的文件夹选择对话框很小,也导致选择很不方便,所以很多人就不喜欢这种对话框了。但是今天讲的定制这种文件夹选择对话框的样式和大小,或许可以大大提升用户体验吧。嘿嘿。
首先来看看实现截图吧,看图说话,有图有真相!
第一张图是横向的,第二张图是纵向的,第三张图是横向纵向都扩大了。对话框中,还增加了编辑框,可以直接输入文件夹路径。选择了文件夹后,会自动更新选中的文件夹路径到编辑框中。看看,效果还不错,下面来看看如何定制,摆脱原始的小小的选择框吧。
实现的原理,其实很简单,就是提供了一个回调函数来进行定制。为什么回调函数容易实现定制,请看文章《函数调用与回调函数的设计原理的深入对比分析》
的详细分析。这里就不对回调函数机制详细讨论了。使用回调函数,就是给你有机会定制界面,所以回调函数是一个非常不错的东西哦。而在回调函数中,是对消息
BFFM_INITIALIZED进行响应,然后对文件夹选择对话框中的所有子控件进行遍历,然后调整大小和位置,进而实现定制。
下面看看实现的代码:
要使用回调函数定制,那么就要提供回调函数,在文件夹选择对话框的结构体中的成员lpfn传递回调函数地址即可。启动文件夹选择对话框的代码如下:
const TCHAR* lpPath=_T("C:\\Program Files\\"); TCHAR lpTemp[MAX_PATH]={0};// - 存储选中的文件夹路径 BROWSEINFO bi; memset(&bi, 0, sizeof(bi)); LPITEMIDLIST pItemList; bi.hwndOwner = m_hWnd;// - 设置拥有者窗口句柄,如果为NULL,相当于非模态窗口 bi.lpszTitle = _T("★C++技术网 提醒★ ?请选择文件夹?");// - 选择文件夹中的提示文字 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_EDITBOX; bi.lpfn = BrowseCallbackProc;// - 回调函数,用于定制文件夹选择对话框 bi.lParam=(LPARAM)lpPath;// - 传入默认选中的文件夹 pItemList = SHBrowseForFolder(&bi); if (pItemList!=NULL) { SHGetPathFromIDList(pItemList,lpTemp);// - 得到选中的文件夹路径 }
代码说明:
代码中的注释已经写的很清楚了,每一步基本都标清楚了,所以不多解释了。要说一下的就是BROWSEINFO
结构体。此结构体用于对文件夹选择对话框传递各种参数,包括定制界面的回调函数。结构体的成员ulFlags
可以使用多个标识组合起来使用,实现对界面的定制。BIF_EDITBOX就是多出来的编辑框。BIF_RETURNONLYFSDIRS表示只返回文件
夹。这些标识有很多,具体的就要自己查MSDN了,里面说的很清楚,我就不重复解释,只是告诉你方法,你要自己动手去查去学习。
SHBrowseForFolder就是启动文件夹选择对话框的Shell函数。执行完后,然后判断是否成功,然后调用
SHGetPathFromIDList得到文件夹列表中选择的文件夹,存入lpTemp中。
我们要提供一个回调函数,首先要声明这个回调函数,声明如下:
static int __stdcall BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);
必须是类静态的,所以要加上static
关键字。其他的格式,都是API规定的格式。在实现回调函数之前,我自定义了一个结构体,用来存储子控件的样式的,存储左右上下边距,宽度和高度。用结构
体形式整体存储这些信息,可以简化代码,让逻辑更加清楚。并使用typedef重定义了结构体类型,让使用更加方便。
结构体定义如下:
typedef struct ControlLayout { int iWidth; // - 宽度 int iHeight; // - 高度 int iMargin_Left; // - 左边距 int iMargin_Right; // - 右边距 int iMargin_Top; // - 上边距 int iMargin_Bottom;// - 下边距 }CTRLLAYOUT;
回调函数的代码实现如下:
int CALLBACK CfolderDlg::BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lParam,LPARAM lpData) { CWnd* pMainWnd; CRect OrgMainClientRect;// - 调整前文件夹对话框客户区矩形 CRect NewMainWndRect;// - 调整后文件夹对话框整个窗口矩形 CWnd* pChildWnd;// - 子窗口指针 TCHAR szClassName[MAX_PATH];// - 控件Windows窗口类类名 CRect TreeRect; // -树控件矩形区域 CRect ButtonRect;// - 按钮控件矩形区域 CTRLLAYOUT TreeLayout={0};// - 树控件样式 CTRLLAYOUT ButtonLayout[2]={0};// - 树控件样式 int iIndex = 0;// - 对按钮数组计数 switch (uMsg) { case BFFM_INITIALIZED: pMainWnd = CWnd::FromHandle(hwnd);// - 根据窗口句柄,获取文件夹选择对话框指针 pMainWnd->GetClientRect(OrgMainClientRect);// - 获取主窗口客户区的大小,即控件放置的区域 pChildWnd = pMainWnd->GetWindow(GW_CHILD); // - 获取子窗口的指针 /* - 循环便利文件夹选择对话框的控件,并存储控件的位置信息 - */ while (pChildWnd != NULL) { GetClassName(pChildWnd->m_hWnd, szClassName, MAX_PATH); if (_tcscmp(_T("SysTreeView32"), szClassName) == 0) { /* - 存储树控件样式 - */ pChildWnd->GetWindowRect(TreeRect); pMainWnd->ScreenToClient(TreeRect); TreeLayout.iMargin_Left = TreeRect.left; TreeLayout.iMargin_Top = TreeRect.top; TreeLayout.iMargin_Right = OrgMainClientRect.right - TreeRect.right; TreeLayout.iMargin_Bottom = OrgMainClientRect.bottom - TreeRect.bottom; } if (_tcscmp(_T("Button"), szClassName) == 0) { /* - 存储按钮控件样式 - */ pChildWnd->GetWindowRect(ButtonRect); pMainWnd->ScreenToClient(ButtonRect); ButtonLayout[iIndex].iWidth = ButtonRect.Width(); ButtonLayout[iIndex].iHeight = ButtonRect.Height(); ButtonLayout[iIndex].iMargin_Right = OrgMainClientRect.right - ButtonRect.right; ButtonLayout[iIndex].iMargin_Bottom = OrgMainClientRect.bottom - ButtonRect.bottom; iIndex++; } pChildWnd = pChildWnd->GetNextWindow(); } /* - 增大文件夹对话框和对话框中的客户区 - */ pMainWnd->GetWindowRect(NewMainWndRect); OrgMainClientRect.InflateRect(0, 0, 240, 300); NewMainWndRect.InflateRect(0, 0, 240, 300); pMainWnd->MoveWindow(NewMainWndRect); /* - 修改控件大小和位置 - */ iIndex = 0; pChildWnd = pMainWnd->GetWindow(GW_CHILD);// - 移动主窗口后获取子窗口指针 while (pChildWnd != NULL) { GetClassName(pChildWnd->m_hWnd, szClassName, MAX_PATH); if (_tcscmp(_T("SysTreeView32"), szClassName) == 0) { TreeRect.left = TreeLayout.iMargin_Left; TreeRect.top = TreeLayout.iMargin_Top; TreeRect.right = OrgMainClientRect.right - TreeLayout.iMargin_Right; TreeRect.bottom = OrgMainClientRect.bottom - TreeLayout.iMargin_Bottom; pChildWnd->MoveWindow(TreeRect); } if (_tcscmp(_T("Button"), szClassName) == 0) { ButtonRect.right = OrgMainClientRect.right - ButtonLayout[iIndex].iMargin_Right; ButtonRect.bottom = OrgMainClientRect.bottom - ButtonLayout[iIndex].iMargin_Bottom; ButtonRect.left = ButtonRect.right - ButtonLayout[iIndex].iWidth; ButtonRect.top = ButtonRect.bottom - ButtonLayout[iIndex].iHeight; pChildWnd->MoveWindow(ButtonRect); iIndex++; } pChildWnd = pChildWnd->GetNextWindow(); } pMainWnd->SendMessage(BFFM_SETSELECTION, TRUE, lpData);// - 设置默认选中的文件夹 break; default: break; } return 0; }
代码说明:
每一句代码的注释基本很清楚。这里说一下整个的流程思路原理。回调函数只对BFFM_INITIALIZED消息进行响应,也就是只在初始化文件夹选择对话框时对对话框进行定制即可,其他消息就不用管了。
使用TreeLayout存储文件夹树大小和布局(相对位置),使用ButtonLayout数组存储两个按钮的大小和布局。使用传进来的文件夹选择对话
框句柄得到主窗口指针,然后获取子窗口和客户区。凤凰娱乐平台官网文件夹选择对话框的结构是这样的:整个窗口作为父窗口,窗口中的每一个控件作为子窗口。要对这些子窗口进
行修改,则要对子窗口进行遍历,调用GetWindow(GW_CHILD)获取第一个子窗口,然后调用GetNextWindow()获取后面的子窗
口,直到GetNextWindow()获取的子窗口指针为NULL为止。根据对应的控件类,存储对应的布局。伯乐娱乐平台再遍历一次,对控件进行调整,当然,如果要
图省事,也可以一次性修改好,也是可以的。诺亚娱乐平台这里只是给出参考代码,可以在此基础上优化。InflateRect()函数是对矩形进行扩展,参数为正,则扩
大矩形,为负数则缩小矩形。具体的请查看MSDN的解释。最后发送一个消息设置默认选中的文件夹。
标签:
原文地址:http://www.cnblogs.com/bmxuhang/p/4352565.html