65.9K
CodeProject 正在变化。 阅读更多。
Home

自定义 DocList

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (8投票s)

2003年6月8日

公共领域

3分钟阅读

viewsIcon

60104

downloadIcon

215

在 PocketPC 2002 应用程序中操作 "Folders" 下拉列表。

Sample Image - ce_doclist.gif

引言

当您启动 Pocket Word 或 Pocket Excel 或其他标准的 PocketPC 应用程序时,您看到的就是 DocList。它显示文件列表,用户可以通过左上角的下拉列表选择要查看的文件夹。 它是 PocketPC 对 FileOpen 的回应。

本文介绍了如何自定义文件夹列表。在这里,我心血来潮,让它只显示名称中带有 "L" 的文件夹,并隐藏 "所有文件夹" 和 "添加/删除文件夹" 选项。 更严重的是,对于一个总是将文档保存在 SD 卡上的应用程序,您可以将其限制为只显示 SD 卡文件夹。或者从列表中删除愚蠢的“模板”和“注释”文件夹。

背景

DocList 控件存在一些错误。首先,尽管它显示了存在于每个存储卡以及 My Documents 下的文件夹,但它不允许用户从第一个存储卡之外的任何地方 选择 文件夹。这在诸如 Mitac 的系统上是很有问题的,他们认为其内置的备份闪存磁盘是主要的,而 SD 卡是次要的。 哎呀! 为了对用户友好,您可能需要隐藏所有位于次要存储卡上的文件夹。

其次,如果多个位置存在同名文件夹(例如 \My Documents\Lu 以及 Storage Card\Lu),那么下拉文件夹列表将把这两个文件夹合并在一起。但是,如果其中一个文件夹的命名方式不同,则它们会为了选择目的而合并,但为了显示目的而保持未合并状态。

准备工作

此代码使用标准模板库 (STL)。 每个人都应该使用 STL。 下载 STL,由 Giuseppe Govi 移植到 eVC++。 将其解压缩到您项目的名为 stl_eVC 的子目录中。 接下来,在 Project > Settings > Compiler > Preprocessor 下,将目录 stl_eVC 添加到搜索路径列表中。

以下显示了要包含的库。 我在 STL 中禁用了警告 4018,因为它是不真实的。 另外,在 Project > Settings > Linker > Input 下,链接 doclist.libnote_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;

HINSTANCE hInstance;
HWND hdlc=0;

全局变量 hdlcDocList 窗口。

构建一个假文件夹列表

第一个问题是,虽然我们可以操作 DocList 的下拉文件夹列表,但我们无法检索项目的名称。 我们的解决方案是构建我们自己的“假”列表,列出我们认为 DocList 最终将包含的内容,并按相同的顺序。 这样我们就不必盲目地操作 DocList 了。

// BuildProjectsList -- the goal here is to to build exactly
// the same list as the DocList will build. (We also
// annotate it to say whether each entry is valid, according
// to our criteria).

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 Doc-
  // uments and also in a flash card, we only show it once.
  // This is in accordance with the behaviour of DocList.
  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

接下来,我们将对 DocList 控件进行子类化。 我们将拦截它的 Menu 事件,并根据需要更改菜单。 我们使用一个巧妙的策略来隐藏我们不想要的:也就是说,我们拦截 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);
}

以下是如何在我们的主窗口的 WM_CREATE 响应中安装子类,(即创建 DocList 的正常位置)。 观察我们对初始文件夹的选择。 这是因为,在这个示例应用程序中,我禁止了“所有文件夹”选项 - 如果用户以后无法选择它,那么 DocList 从默认的 所有文件夹 开始是不礼貌的。

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);
© . All rights reserved.