在PocketPC设备上,当启动Pocket Word或Pocket Excel等标准应用程序时,通常会显示一个文件列表,用户可以通过左上角的下拉菜单选择要查看的文件夹。这类似于桌面环境中的“文件打开”对话框。本文将展示如何自定义这个文件夹列表,例如,只显示名称中包含字母"L"的文件夹,并隐藏“所有文件夹”和“添加/删除文件夹”的选项。对于始终在SD卡上保存文档的应用程序,可以限制只显示SD卡文件夹。或者从列表中移除不必要的“模板”和“注释”文件夹。
DocList 控件在PocketPC中存在一些缺陷。首先,尽管它显示了每个存储卡上存在的文件夹以及"My Documents"下的文件夹,但它不允许用户从第一张存储卡以外的任何地方选择文件夹。这在像Mitac这样的系统上是个问题,它们将内置的备份闪存盘视为主要存储,而将SD卡视为次要存储。为了对用户友好,可以隐藏所有在次要存储卡上的文件夹。其次,如果在多个位置有同名的文件夹(例如"My Documents\Lu"和"Storage Card\Lu"),下拉文件夹列表会将它们合并在一起。但如果一个是用不同的大小写书写的,那么它们在选择时会合并,但在显示时不会合并。
本文的代码使用了标准模板库(STL)。每个人都应该使用STL。下载STL,由Giuseppe Govi移植到eVC++。将其解压缩到项目的一个子目录中,命名为stl_eVC。接下来,在“项目 > 设置 > 编译器 > 预处理器”下,将目录stl_eVC添加到搜索路径列表中。
以下是需要包含的库。已经在STL中禁用了警告4018,因为它是虚假的。同时,在“项目 > 设置 > 链接器 > 输入”下,链接到doclist.lib和note_prj.lib。第一个是用于DocList控件,第二个是用于枚举文件夹。
#include <windows.h>
#include <doclist.h>
#include <projects.h>
#pragma warning( push )
#pragma warning( disable: 4018 )
#include <string>
#include <list>
#pragma warning( pop )
using namespace std;
全局变量hdlc是DocList窗口。
第一个问题是,尽管可以操作DocList的下拉文件夹列表,但无法检索项目名称。解决方案是构建认为DocList最终会包含的自己的“假”列表,并且以相同的顺序。这样就不必盲目地操作DocList了。
struct TProjectInfo {
wstring fn;
bool valid;
int id;
bool operator<(const TProjectInfo &b) const {
return _wcsicmp(fn.c_str(), b.fn.c_str()) < 0;
}
};
list<TProjectInfo> dlcprojects;
全局变量dlcprojects维护假列表。注意DocList对文件夹排序是不区分大小写的;必须做同样的事情,因此使用了比较运算符。变量id稍后将在子类化DocList窗口时使用...
以下函数填充列表。(它还返回列表中有效条目的名称。)
wstring BuildProjectsList() {
wstring validfn;
dlcprojects.clear();
EnumProjectsEx(EnumCallback, 0, PRJ_ENUM_ALL_DEVICES, 0);
dlcprojects.sort();
// The DocList's drop-down has some extra items:
TProjectInfo pi; pi.valid = false;
pi.fn = L"All Folders"; dlcprojects.push_front(pi);
pi.fn = L"---"; dlcprojects.push_back(pi);
pi.fn = L"Add/Delete..."; dlcprojects.push_back(pi);
// Now we "unique" the list: if a folder exists in My Documents and also in a flash card, we only show it once.
for (list<TProjectInfo>::iterator i = dlcprojects.begin(); i != dlcprojects.end(); i++) {
list<TProjectInfo>::iterator j = i; j++;
while (j != dlcprojects.end() && (*i).fn == (*j).fn) {
if ((*j).valid && !(*i).valid) *i = *j;
j = dlcprojects.erase(j);
}
if (validfn == L"" && (*i).valid) validfn = (*i).fn;
}
return validfn;
}
注意函数EnumProjectsEx。在PocketPC术语中,“项目”是"My Documents"的任何直接(非嵌套)子文件夹,或者是任何存储卡的任何直接子文件夹。它使用以下回调:
BOOL CALLBACK EnumCallback(PAstruct *pa, LPARAM lp) {
wchar_t *fn;
if (pa->m_IDtype != FILE_ID_TYPE_OID) fn = pa->m_szPathname;
else {
CEOIDINFO cinf; CeOidGetInfo(pa->m_fileOID, &cinf);
fn = cinf.infDirectory.szDirName;
}
TProjectInfo pi;
// Get the "display-name" (ie. without the path)
wchar_t *c = fn, *lastslash = c;
while (*c != 0) {
if (*c == '\\') lastslash = c + 1; c++;
}
pi.fn = lastslash;
// and our arbitrary 'validity' criterion:
pi.valid = (wcschr(lastslash, 'l') != 0);
pi.valid |= (wcschr(lastslash, 'L') != 0);
dlcprojects.push_back(pi);
return TRUE;
}
接下来,将子类化DocList控件。将拦截它的菜单事件,并根据需要更改菜单。使用了一个巧妙的策略来隐藏不想要的菜单项:即拦截WM_MEASUREITEM并将它们的高度设置为零!以下是子类过程:
WNDPROC OldDLCProc = 0;
LRESULT CALLBACK NewDLCProc(HWND hs, UINT msg, WPARAM wParam, LPARAM lParam) {
if (msg == WM_INITMENUPOPUP) {
BuildProjectsList();
// just to be sure we're fresh
// We will correlate menu-item IDs with 'dlcprojects'
LRESULT ret = CallWindowProc(OldDLCProc, hs, msg, wParam, lParam);
HMENU hm = (HMENU)wParam; wstring ws;
list<TProjectInfo>::iterator it = dlcprojects.begin();
for (unsigned int pos = 0; ; pos++, it++) {
MENUITEMINFO minf; ZeroMemory(&minf, sizeof(minf));
minf.cbSize = sizeof(minf); minf.fMask = MIIM_ID;
BOOL res = GetMenuItemInfo(hm, pos, TRUE, &minf);
if (!res) break;
(*it).id = minf.wID;
}
return ret;
} else if (msg == WM_MEASUREITEM) {
MEASUREITEMSTRUCT *ms = (MEASUREITEMSTRUCT*)lParam;
bool valid = false;
for (list<TProjectInfo>::const_iterator i = dlcprojects.begin(); i != dlcprojects.end(); i++) {
if ((*i).id == (int)ms->itemID) valid = (*i).valid;
}
LONG ret = CallWindowProc(OldDLCProc, hs, msg, wParam, lParam);
if (!valid) { ms->itemWidth = 1; ms->itemHeight = 0; }
return ret;
}
return CallWindowProc(OldDLCProc, hs, msg, wParam, lParam);
}
case WM_CREATE: {
wstring initf = BuildProjectsList();
DOCLISTCREATE dlc; ZeroMemory(&dlc, sizeof(dlc));
dlc.dwStructSize = sizeof(dlc);
dlc.hwndParent = hwnd;
dlc.pszFolder = initf.c_str();
dlc.pstrFilter = L"All files\0*.*\0Text\0*.pwd;*.txt\0";
dlc.wId = 102;
hdlc = DocList_Create(&dlc);
OldDLCProc = (WNDPROC)SetWindowLong(hdlc, GWL_WNDPROC, (LONG)NewDLCProc);
RECT rc; GetClientRect(hwnd, &rc);
MoveWindow(hdlc, 0, 0, rc.right, rc.bottom, false);
DocList_Refresh(hdlc);
}