工程一开始的时候是计划尽可能的简单但是当我不断发现我不得不考虑新特色的时候,它迅速的膨胀成为一个梦魇。虽然测试并不是没有遗漏--但是我还是坚信情形不会变得太坏J。Joe很善意的允许我开放这个资源而不附加任何的语句(毕竟那是基于他的代码),但是由于工程象马拉松似的,所以我在这段代码中使用了两个非常不成熟的条件:
这段代码可以以任何方式用于已编译的形式中(包括商业用途)。只要代码不适用,即使没有作者同意,作者姓名和所有版权信息都原封不动,你可以对代码进行任何形式的使用。但是,如果没有作者的同意,这篇文章和附带的源代码都不能放在任何网站或论坛上。
你就把它当作是没有任何担保的软件,随意使用吧!
我已经尽量除去任何不良的" 特征",对由它引起的任何损害,时间的浪费或者数据丢失等,我不负任何责任。
希望不要问太多关于继续开发下去的到底有多大工作量的问题。如果你真的要用于商业场合,请给我发email让我知道。如果没有多少人使用的话,开放和维护/升级代码就没有任何意义。
</P><P>控件的特点:
●使用鼠标可以进行单元格的选择,还可以辅助ctrl和shift的组合键进行选
择。也可以取消选择。
● 行和列可以按照大小进行重排,还可以取消对行、列或两者的排序。
● 双击区分点,行或者列可以按照大小自动排序
● 可以对任何列或行固定
● 单元格可以有不同文本和背景颜色的个性化设置
● 单元格可以有字体的个性化设置
● 单元格可以标注"只读"或者其他的状态设置及检测
● OLE的拖放动作
● Ctrl-C, Ctrl-X和Ctrl-V执行拷贝、剪切、粘贴操作,Ctrl-A全选
● 当单元格成为焦点,并且在单元格的编辑区域按下字符键,就意味着在
那个单元格进行编辑了
● 支持微软的智能鼠标
● 可以在单元格中加入图片
● 对大型数据可以使用"虚拟"模式
● 充分的打印支持,支持文档/浏览环境(包括打印预览)或是基于会话的应用(不支持打印预览)
● 可选的"列表模式",包括对行的全选或单选,还有单击列标题提示进行插入的操作。
● 众多的虚函数可以很容易对控件进行功能扩充
● 支持UNICODE
● 支持WinCE
● 单元格的标题提示太小不能显示数据
● 可以隐藏行和列
● 在VC4.2、5.0、6.0和CE工具箱2.0、3.0下编译通过</P><P>示例中示范了grid控件中大部分特征</P><P>文档
如果想在你的工程中使用这个Grid控件的话,你还得在你的工程中添加一些文件:
gridctrl.cpp, gridctrl.h Grid控件资源文件和头文件
gridcellbase.cpp, gridcellbase.h 单元格的基础类
gridcell.cpp, gridcell.h 单元格的默认执行文件
CellRange.h CcellID和CcellRange类的定义
MemDC.h Keith Rule's的直接存储类
InPlaceEdit.cpp, InPlaceEdit.h 定位编辑窗口的源文件和头文件
GridDropTarget.cpp, GridDropTarget.h Grid容器的drag和drop对象 只有在gridctrl.h中没有定义 GRIDCONTROL_NO_DRAGDROP的时候才有必要使用。
Titletip.cpp, Titletip.h 从Zafir Anjum那里的到的单元格标题提示. 只有在gridctrl.h中没有定义 GRIDCONTROL_NO_TITLETIPS 的时候才有必要使用</P><P>结构
这个Grid是基于一种框架(CgridCtrl工程),这种框架组织和控制那些容纳数据、执行某些操作如画图、句柄方法如按钮的点击事件的单元格的动作。 Grid工程本身的句柄事件如点击是在单元格之前响应,如果它认为有必要的话,它还会发送某种鼠标信息。它还包含一个拖曳对象(CGridDropTarget)和一个标题提示对象(CTitleTip),前者处理拖曳操作,后者在单元格物理空间在最大限度内不足以显示其内容时可以显示出其内容。Grid单元格可以是任何类型,其长度与源自CgridBaseCell的类的长度一样。包含这个包的是一个CgridCell类,它能处理基本的数据存储和编辑操作。扩充的两个类CgridCellCombo和CGridURLCell示范了如何创建自己的单元格类。
单元格有两种主要状态即固定和非固定。固定的单元格通常在Grid的左上方,并且不会随着Grid的卷动而移动。通常这些单元格包含列和行的标题部分,并且不能进行编辑。而非固定的单元格构成了Grid的内部,你可以对它进行编辑和选择。
Grid的各种不同属性的默认值存放在CgridDefaultCell中。每个Grid中通常有四种属性--每个Grid中含有非固定、列固定、行固定以及行列同时固定的单元格的默认值。因此,为了实现设置Grid的默认属性,首先得使用CGridCtrL::GetDefaultCell来取得单元格的默认实现,然后你就可以直接设置了
单元格的属性除字体属性外都很明确。每一个单元格都有一个指向字体结构体的指针,这个指针只有当你的自行设置单元格的字体属性时才会被分配和使用。
Grid还有一种虚拟模式阻止Grid创建实际的格子,每当它需要单元格的信息时,允许你的指定一种回收函数或者消息机制来获得。这样当工作轻微减少时可以节省大量的内存费用。发送给Grid父类的消息GVN_ODCACHEHINT可以帮助你的在Grid的单元格发送信息请求时预先进行数据缓冲。
Grid的数据是以行为单位进行存储的,所以,对于大量单元格而言,所有的操作都必须以行为单位进行的。</P><P>解说
好了--那么,现在如何使用它呢?
Grid的基本类是源于CWnd的CgridCtrl。为了使用它,你可以使用微软的VC++的对话框编辑器,把一个普通的控件放在对话框上,并且输入"MFCGridCtrl"(不包括引号)作为类名。Grid的子类使用DDX机制(可以通过ClassWizard来进行默认设置),使用DDX_GridControl函数代替DDX_Control(可以通过手动设置ClassWizard的输入来实现)。这些保证你的控件作为一个注册对象而不会产生一些莫名其妙的WIN95问题。
你也可以选择使用CGridCtrl::Create
CGridCtrl grid;
grid.Create(rect, pParentWnd, nID);
其中的rect是大小,pParentWnd是父窗口,nID是标志符。</P><P>列和行的数目
int GetRowCount() const 返回行(包括固定行)的数目
int GetColumnCount() const 返回列(包括固定列)的数目
int GetFixedRowCount() const 返回固定行的数目
int GetFixedColumnCount() const 返回固定行的数目
BOOL SetRowCount(int nRows) 设置行的数目(包括固定行),如果成功,返回TRUE
BOOL SetColumnCount(int nCols) 设置列的数目(包括固定列),如果成功,返回TRUE
BOOL SetFixedRowCount(int nFixedRows = 1) 设置固定行的数目,如果成功,返回TRUE
BOOL SetFixedColumnCount(int nFixedCols = 1) 设置固定列的数目,如果成功,返回TRUE</P><P>大小和位置函数
int GetRowHeight(int nRow) const 获取由nRow指定行的高度
BOOL SetRowHeight(int row, int height) 设定由row指定行的高度为height
int GetColumnWidth(int nCol) const 获取由nCol指定列的宽度
BOOL SetColumnWidth(int col, int width) 设定由col指定列的宽度为width
int GetFixedRowHeight() const 获取固定行的高度
int GetFixedColumnWidth() const 获取固定列的高度
long GetVirtualHeight() const 获取所有行的合并高度
long GetVirtualWidth() const 获取所有列的合并宽度
BOOL GetCellOrigin(int nRow, int nCol, LPPOINT p) 取出单元格(nRow,nCol)的左上角点,成功返回TRUE(单元格必须是可见的)
BOOL GetCellOrigin(const CCellID& cell, LPPOINT p) 获取给定单元格的左上角点,成功则返回TRUE,也可以参照 CCellID.
BOOL GetCellRect(int nRow, int nCol, LPRECT pRect) 获取给定单元格的边框,成功则返回TRUE(单元格必须是可见的)
BOOL GetCellRect(const CCellID& cell, LPRECT pRect) 获取给定单元格的边框成功则返回TRUE(单元格必须是可见的) 也可以 参照CCellID.
BOOL GetTextRect(int nRow, int nCol, LPRECT pRect)t 获取给定单元格中的文本框,成功则返回TRUE(单元格必须是可见的)
BOOL GetTextRect(const CCellID& cell, LPRECT pRect) 获取给定单元格中的文本框,成功则返回TRUE(单元格必须是可见的)也可以参照 CCellID.
BOOL GetTextExtent(int nRow, int nCol, LPCTSTR str) 获取给定单元格中的指定的文本内容的边框,成功则返回TRUE
BOOL GetCellTextExtent(int nRow, int nCol) 获取给定单元格中的文本框,成功则返回TRUE</P><P>虚拟模式
虚拟模式允许Grid在不存储数据的情况下,能够显示大量数据。在虚拟模式下,不用产生单元格,也不用存储数据,列宽和行高除外。
由于Grid本身并不存储数据,所以它必须有一些方法让其它程序帮助它存储这些数据。这些是通过Grid本身的回收函数或者其父类的一个句柄GVN_GETDISPINFO的申明来实现的。
void SetVirtualMode(BOOL bVirtual) 设置Grid是否使用虚拟模式
BOOL GetVirtualMode() 当使用虚拟模式时返回TRUE
void SetCallbackFunc(GRIDCALLBACK pCallback, LPARAM lParam) 当Grid为虚拟模式时,设置回调函数
GRIDCALLBACK GetCallbackFunc() 当Grid为虚拟模式时,返回回调函数
如果没有指定回调函数,Grid就会向其父窗口发送一个GVN_GETDISPINFO信息,这个申明部分如同GV_DISPINFO结构,GV_DISPINFO机构体就象下面所示:</P><P>
typedef struct tagGV_DISPINFO {
NMHDR hdr;
GV_ITEM item;
} GV_DISPINFO;</P><P>
显而易见的是,它有一个很好的暗示就是允许Grid所需要的数据进入高速缓存。因此,在显示某一页单元格内容之前,Grid首先会发送一个GVN_ODCACHEHINT信息,结构体GV_CACHEHINT会作为消息的一部分,其结构如下:</P><P>
typedef struct tagGV_CACHEHINT {
NMHDR hdr;
CCellRange range;
} GV_CACHEHINT;</P><P>
下面有一个处理这个消息的例子:
//处理这个消息的是对话框的一个成员变量m_Gri
BOOL CGridCtrlDemoDlg::OnNotify(WPARAM wParam, LPARAM lParam,
LRESULT* pResult)
{
if (wParam == (WPARAM)m_Grid.GetDlgCtrlID())
{
*pResult = 1;
GV_DISPINFO *pDispInfo = (GV_DISPINFO*)lParam;
if (GVN_GETDISPINFO == pDispInfo->hdr.code)
{
//TRACE2("Getting Display info for cell %d,%d\n",
pDispInfo->item.row, pDispInfo->item.col);
pDispInfo->item.strText.Format(_T("Message %d,%d"),
pDispInfo->item.row, pDispInfo->item.col);
return TRUE;
}
else if (GVN_ODCACHEHINT == pDispInfo->hdr.code)
{
GV_CACHEHINT *pCacheHint = (GV_CACHEHINT*)pDispInfo;
TRACE(_T("Cache hint received for cell range %d,%d - %d,%d\n"),
pCacheHint->range.GetMinRow(),
pCacheHint->range.GetMinCol(),
pCacheHint->range.GetMaxRow(),
pCacheHint->range.GetMaxCol());
}
}</P><P>return CDialog::OnNotify(wParam, lParam, pResult);
}
也可以使用SetCallbackFunc来设置回收函数代替发送GVN_GETDISPINFO消息,而且,Grid可以直接调用这个回收函数,当然了,即使调用了回收函数,GVN_ODCACHEHINT消息还会照常发送。
回收函数应当是如下形式:
BOOL CALLBACK CallbackFunction(GV_DISPINFO * pDispInfo, LPARAM lParam);
例如:
BOOL CALLBACK CGridCtrlDemoDlg::GridCallback(GV_DISPINFO *pDispInfo,
LPARAM /*lParam*/)
{
pDispInfo->item.strText.Format(_T("Callback %d,%d"),
pDispInfo->item.row, pDispInfo->item.col);
return TRUE;
}
当调用SetCallbackFunc的时候,你可以定义一个LPARAM,这样当每次调用回收函数时可以将这个值传递给这个回收函数。注意这个回收函数必须是一个静态或全局函数。</P><P>总体的外观和特征
void SetImageList(CImageList* pList) 设置Grid的当前图形列表,它拷贝的只是列表的指针而非列表本身。
CImageList* GetImageList() 取出Grid当前图形列表
void SetGridLines(int nWhichLines = GVL_BOTH) 设置哪些(如果有的话)线条不可见,从 here 可以找到一些可能的取值。
int GetGridLines() 取出那些(如果有的话)不可见线条,从 here 可以找到一些可能的返回值。
void SetEditable(BOOL bEditable = TRUE) 设置Grid是否可以编辑。
BOOL IsEditable() 判断Grid是否可编辑。.
void SetListMode(BOOL bEnableListMode = TRUE) 将Grid设置成(或不是)排序模式,当Grid处于排序模式时,将启动所有行选,并且这时如果点击列表头时,将会对Grid按行进行排序。
BOOL GetListMode() 判断Grid是否处于排序模式。
void SetSingleRowSelection(BOOL bSing = TRUE) 将G rid设置成(或不是)单行选择模式,这种模式只有在排序模式下有效。 当处在这种模式下,每次只能选择一行,所以整个Grid表现看起来就好象是一个多列的列表框。
BOOL GetSingleRowSelection() 判断Grid是否处于单行选择模式。
void SetSingleColSelection(BOOL bSing = TRUE) 将Grid设置成(或不是)单列选择模式,在这种模式下,每次只能选择一列。
BOOL GetSingleColSelection() 判断Grid是否处于单列选择模式。
void EnableSelection(BOOL bEnable = TRUE) 设置Grid的单元格是否可选。
BOOL IsSelectable() 判断Grid的单元格是否可选。
void SetFixedRowSelection(BOOL bSelect) 设置当点击固定行时,是否选择其旁边的单元格。
BOOL GetFixedRowSelection() 判断当点击固定行时,是否选择其旁边的单元格。
void SetFixedColumnSelection(BOOL bSelect) 设置当点击固定列时,是否选择其下面的单元格
BOOL GetFixedColumnSelection() 判断当点击固定列时,是否选择其下面的单元格
void EnableDragAndDrop(BOOL bAllow = TRUE) 设置是否开启拖曳动作。
BOOL GetDragAndDrop() 判断拖曳动作是否开启。
void SetRowResize(BOOL bResize = TRUE) 设置是否可设置行的大小。
BOOL GetRowResize() 判断是否可设置行的大小。
void SetColumnResize(BOOL bResize = TRUE) 设置是否可设置列的大小。
BOOL GetColumnResize() 判断是否可设置列的大小。
void SetHandleTabKey(BOOL bHandleTab = TRUE) 设置是否启用TAB键来移动选择单元格
BOOL GetHandleTabKey() 判断是否启用TAB键来移动选择单元格。
void SetDoubleBuffering(BOOL bBuffer = TRUE) 设置画图时是否使两级缓冲(不支持闪烁)。
BOOL GetDoubleBuffering() 判断画图时是否使用了两级缓冲。
void EnableTitleTips(BOOL bEnable = TRUE) 设置是否使用标题提示
BOOL GetTitleTips() 判断是否使用标题提示
void SetTrackFocusCell(BOOL bTrack) 设置同行/列中的固定单元格作为焦点单元格时是否高亮显示并且使用凹陷边缘。
BOOL GetTrackFocusCell() 判断同行/列中的固定单元格作为焦点单元格时是否高亮显示并且使用凹陷边缘。
void SetFrameFocusCell(BOOL bFrame) 设置焦点单元格是否高亮显示并且加上外边框。
BOOL GetFrameFocusCell() 判断是否对焦点单元格高亮显示并且加上外边框。
void SetAutoSizeStyle(int nStyle = GVS_BOTH) 设置单元格如何自动调整大小GVS_BOTH = 固定和非固定单元格都可以自动调整; GVS_HEADER = 仅固定单元格可以; GVS_DATA = 仅非固定单元格可以
int GetAutoSizeStyle() 获取自动排序的执行模式。
void EnableHiddenColUnhide(BOOL bEnable = TRUE) 设置当用户调整列的宽度时隐藏列(宽度为0)是否显现出来。
BOOL GetHiddenColUnhide() 判断当用户调整列的宽度时隐藏列(宽度为0)是否显现出来。
void EnableHiddenRowUnhide(BOOL bEnable = TRUE) 设置当用户调整行的高度时隐藏行(高度为0)是否显现出来。
BOOL GetHiddenRowUnhide() 判断当用户调整行的高度时隐藏行(高度为0)是否显现出来。
void EnableColumnHide(BOOL bEnable = TRUE) 设置是否可以通过鼠标将列的宽度压缩为0。
BOOL GetColumnHide() 判断是否可以通过鼠标将列的宽度压缩为0。
void EnableRowHide(BOOL bEnable = TRUE) 设置是否可以通过鼠标将行的高度压缩为0。
BOOL GetRowHide() 判断是否可以通过鼠标将行的高度压缩为0。</P><P>颜色
void SetGridBkColor(COLORREF clr) 设置控件的背景颜色(固定和非固定单元格之外的区域)。
COLORREF GetGridBkColor() 获取控件的背景颜色。
void SetGridLineColor(COLORREF clr) 设置网格线的颜色。
COLORREF GetGridLineColor() 获取网格线的颜色。
COLORREF GetTitleTipBackClr() 获取标题提示的背景颜色。
void SetTitleTipBackClr(COLORREF clr = CLR_DEFAULT) 设置标题提示的背景颜色。
COLORREF GetTitleTipTextClr() 获取标题提示的文本颜色。
void SetTitleTipTextClr(COLORREF clr = CLR_DEFAULT) 设置标题提示的文本颜色。
不再支持如下的函数了。你应当使用GetDefaultCell来获取默认单元格实现你所感兴趣的单元格类型,然后就可以直接设置单元格的属性了。如果给定单元格是默认设置值,那么为匹配你的单元格类型,就得在默认的单元格实现中使用这些值。
void SetTextColor(COLORREF clr) 设置非固定单元格中的文本颜色。
COLORREF GetTextColor() 获取非固定单元格中的文本颜色。
void SetTextBkColor(COLORREF clr) 设置非固定单元格的背景颜色。
COLORREF GetTextBkColor() 获取非固定单元格的背景颜色。
void SetFixedTextColor(COLORREF clr) 设置固定单元格的文本颜色。
COLORREF GetFixedTextColor() 获取固定单元格的文本颜色。
void SetFixedBkColor(COLORREF clr) 设置固定单元格的背景颜色。
COLORREF GetFixedBkColor() 获取固定单元格的背景颜色。
void SetBkColor(COLORREF clr) 设置控件的背景颜色 (单元格之外的区域).
COLORREF GetBkColor() 获取控件的背景颜色。
void SetGridColor(COLORREF clr) 设置网格线的颜色。.
COLORREF GetGridColor() 获取网格线的颜色。.
也可以参照Individual Cell colour functions,它允许改变Grid中某一个单元格的颜色以不同于其它单元格。</P><P>普通的单元格信息
CGridCellBase* GetDefaultCell(BOOL bFixedRow, BOOL bFixedCol) const 为要创建的单元格类型获取一个默认的单元格实现的指针. bFixedRow 和 bFixedCol 用来申明单元格是否固定(行、列、或者两者) 或非固定. 使用它来定义Grid的默认属性. 事实上Grid中的单元格在它们创建的时候就有了自己的默认属性. 它们使用 GetDefaultCell查询Grid的默认单元格属性,并且用这些值来勾画自己。.
CGridCellBase* GetCell(int nRow, int nCol) const 根据行/列来获取相应的单元格 (或者不存在的时候返回NULL)
BOOL SetCellType(int nRow, int nCol, CRuntimeClass* pRuntimeClass); 定义响应单元格类的类型。
BOOL SetDefaultCellType(CRuntimeClass* pRuntimeClass); 为新的单元格设置默认属性。
void SetModified(BOOL bModified = TRUE, int nRow = -1, int nCol = -1) 为单元格设置一个标志符. 如果没有指明某行或某列,将对整个Grid进行设置。
BOOL GetModified(int nRow = -1, int nCol = -1) 为单元格设置一个标志符,如果没有相应的单元格,就返回整个Grid的状态。
BOOL IsCellFixed(int nRow, int nCol) 如果单元格固定就返回TRUE.
BOOL IsItemEditing(int nRow, int nCol) 如果单元格正处在编辑状态就返回TRUE.
BOOL SetItem(const GV_ITEM* pItem) 用 GV_ITEM 结构体中的值来设置单元格的内容. 注意掩码字段中的值决定到底哪些值是真正改变了(比较 CListCtrl::SetItem).
BOOL GetItem(GV_ITEM* pItem) 从指定的单元格获取其内容来填充 GV_ITEM 结构体. 注意掩码字段中的只将决定哪些值真正取回了 (比较CListCtrl::GetItem).
BOOL SetItemText(int nRow, int nCol, LPCTSTR str) 设置指定单元格的文本内容.成功则返回TRUE。
virtual CString GetItemText(int nRow, int nCol) 取出指定单元格的文本内容,为有利于扩展,这个函数设置成虚拟函数. 不要和LVN_GETDISPINFO消息或字符串共享弄混了。
BOOL SetItemData(int nRow, int nCol, LPARAM lParam) 为指定单元格设置Iparam字段(用户自定义的数据)。成功则返回TRUE,也可参考GV_ITEM.
LPARAM GetItemData(int nRow, int nCol) const 获取指定单元格的Iparam字段(用户自定义的数据)。成功则返回TRUE,也可参考GV_ITEM.
BOOL SetItemImage(int nRow, int nCol, int iImage) 设置指定单元格的图形索引,成功则返回TRUE,也可参考 GV_ITEM.
int GetItemImage(int nRow, int nCol) const 获取指定单元格的图形索引。
BOOL SetItemState(int nRow, int nCol, UINT state) 设置给定单元格的状态.成功则返回TRUE,也可参考GV_ITEM.
UINT GetItemState(int nRow, int nCol) const 获取指定单元格的状态,也可参考 GV_ITEM.
BOOL SetItemFormat(int nRow, int nCol, UINT nFormat) 设置指定单元格的格式,成功则返回TRUE。 单元格的默认实现是使用 CDC::DrawText, 所以,任何的DT_*格式都可以使用. 也可参考 GV_ITEM.
UINT GetItemFormat(int nRow, int nCol) const 获取给定单元格的格式(默认返回CDC::DrawText DT_*格式). 也可参考 GV_ITEM.
int GetSelectedCount() 获取被选单元格的数量。.
CCellID GetFocusCell() 获取焦点单元格,可参考 CCellID.
CCellID SetFocusCell(CCellID cell);CCellID SetFocusCell(int nRow, int nCol); 设置焦点单元格。
BOOL SetItemBkColour(int nRow, int nCol, COLORREF cr = CLR_DEFAULT) 设置指定单元格的背景颜色,成功则返回TRUE,可参考 GV_ITEM.
COLORREF GetItemBkColour(int nRow, int nCol) const 获取指定单元格的背景颜色,可参考 GV_ITEM.
BOOL SetItemFgColour(int nRow, int nCol, COLORREF cr = CLR_DEFAULT) 设置指定单元格的前景颜色,成功则返回TRUE,可参考 GV_ITEM.
COLORREF GetItemFgColour(int nRow, int nCol) const 获取指定单元格的前景颜色,可参考 GV_ITEM.
BOOL SetItemFont(int nRow, int nCol, LOGFONT* lf) 设置指定单元格的字体,成功则返回TRUE,可参考GV_ITEM.
LOGFONT* GetItemFont(int nRow, int nCol) const 获取指定单元格的字体,可参考 GV_ITEM.
BOOL IsItemEditing(int nRow, int nCol) 如果单元格处于编辑状态,则返回TRUE.
void EnsureVisible(CCellID &cell) 确保指定单元格可见。.
BOOL IsCellVisible(CCellID &cell) constBOOL IsCellVisible(CCellID cell) const 如果单元格可见则返回TRUE.
BOOL IsCellEditable(CCellID &cell) constBOOL IsCellEditable(CCellID cell) const 如果单元格可编辑则返回TRUE.
BOOL IsCellSelected(CCellID &cell) constBOOL IsCellSelected(CCellID cell) const 如果单元格已选,则返回TRUE。
void EnsureVisible(int nRow, int nCol) 确保指定单元格可见.
BOOL IsCellFixed(int nRow, int nCol) 如果单元格是固定的,则返回TRUE。
int GetDefCellHeight() const 返回单元格的默认高度 (对于新创建的单元格)
void SetDefCellHeight(int nHeight) 设置默认单元格高度(对于新创建的单元格). 如果调用了SetFont就将被忽略
int GetDefCellWidth() const 返回单元格的默认宽度 (对于新创建的单元格)
void SetDefCellWidth(int nWidth) 设置默认单元格宽度(对于新创建的单元格). 如果调用了SetFont就将被忽略
int GetDefCellMargin() const Returns the default cell internal margin
void SetDefCellMargin(int nMargin) Sets the default cell internal margin.</P><P>操作
int InsertColumn(LPCTSTR strHeading, UINT nFormat, int nColumn = -1) 在nCol指定的地方插入一列,如果 nCol<0则在末尾插入一列. StrHeading就是列标题头nFormat 是列的格式.返回插入列的位置.
int InsertRow(LPCTSTR strHeading, int nRow = -1) 在nRow处插入一行,如果nRow<0则在末尾插入一行. strHeading 是行标题头. 此行的单元格的格式与其同列的第一行单元格格式相同. 返回插入行的位置.
BOOL DeleteColumn(int nColumn) 删除"nColumn"指定的列,成功则返回TRUE.
BOOL DeleteRow(int nRow) 删除"nRow"指定的行, 成功则返回TRUE.
BOOL DeleteAllItems() 删除Grid中的所有行和内容.
BOOL DeleteNonFixedRows() 删除所有非固定行。
BOOL AutoSizeRow(int nRow, BOOL bResetScroll=TRUE) 自动调整行的大小与最大行一样. 如果bResetScroll是 TRUE那么滚动条也会被重置。
BOOL AutoSizeColumn(int nCol, UINT nAutoSizeStyle = GVS_DEFAULT, BOOL bResetScroll = TRUE) 自动调整列的大小与最大列一样. NAutoSizeStyle设置了调整的方式(参考 AutoSizing options). 如果bResetScroll是TRUE那么滚动条将会被重置.
void AutoSizeRows() 自动调整所有行的大小。
void AutoSizeColumns(UINT nAutoSizeStyle=GVS_DEFAULT) 自动调整所有列的大小. nAutoSizeStyle 设置了调整的方式(参考AutoSizing options)
void AutoSize(UINT nAutoSizeStyle = GVS_DEFAULT) 自动调整所有行和列的大小. nAutoSizeStyle设置了调整的方式(参考 AutoSizing options)
void ExpandColumnsToFit(BOOL bExpandFixed=TRUE) 为添满Grid区域,扩大列宽.如果 bExpandFixed是TRUE固定列也会调整, 否则不受影响.
void ExpandLastColumn() 调整最后一列的宽度以添满grid区域.
void ExpandRowsToFit(BOOL bExpandFixed=TRUE) 为添满Grid区域,扩大行高. 如果 bExpandFixed是TRUE固定行也会调整, 否则不受影响.
void ExpandToFit(BOOL bExpandFixed = TRUE) 为添满Grid区域,扩大行高和列宽. 如果 bExpandFixed是TRUE固定单元格也会调整, 否则不受影响.<
CSize GetTextExtent(int nRow, int nCol, LPCTSTR str) 获取指定单元格中由str指定的文本大小。
CSize GetCellTextExtent(int nRow, int nCol) 获取指定单元格的文本大小。
void SetRedraw(BOOL bAllowDraw, BOOL bResetScrollBars = FALSE) 在行或列数量改变或者自动调整大小的时候关闭/启动重画功能,但对用户的动作如重新调整大小不起作用。
BOOL RedrawCell(int nRow, int nCol, CDC* pDC = NULL) 重画指定单元格。如果提供了pDC则通过它来描画。
BOOL RedrawCell(const CCellID& cell, CDC* pDC = NULL) 重画指定单元格。如果提供了pDC则通过它来描画。
BOOL RedrawRow(int row) 重画指定行.
BOOL RedrawColumn(int col) 重画指定列
BOOL Refresh() 重画整个Grid.
CCellRange GetCellRange() 获取整个Grid中的单元格的范围. 可参考 CCellRange.
void SetSelectedRange(const CCellRange& Range, BOOL bForceRepaint = FALSE); 设置选定单元格的范围. 可参考 CCellRange.
void SetSelectedRange(int nMinRow, int nMinCol, int nMaxRow, int nMaxCol, BOOL bForceRepaint = FALSE); 设置选定单元格的范围.
BOOL IsValid(int nRow, int nCol) 如果指定行和列有效,则返回TRUE.
BOOL IsValid(const CCellID& cell) 如果指定单元格有效,则返回TRUE.
BOOL IsValid(const CCellRange& range) 如果指定单元格范围有效,则返回TRUE.
CCellID GetNextItem(CCellID& cell, int nFlags) const 查找具有特定属性并且和指定内容有特定关系的单元格. (也可参考 CListCtrl::GetNextItem 和Cell Searching options)</P><P>排序操作
void SetHeaderSort(BOOL bSortOnClick = TRUE) 设置在ListMode下,点击列标题头时,是否对行进行排序.
BOOL GetHeaderSort() 判断在ListMode下,点击列标题头时,是否对行进行排序.
SetSortColumn(int nCol 设置当前已排序列的索引。
int GetSortColumn() 获取当前已排序列的索引。
void SetSortAscending(BOOL bAscending) 设置当前排序列是否按升序排序。
BOOL GetSortAscending() 判断当前排序列是否按升序排序。
BOOL SortTextItems(int nCol, BOOL bAscending, LPARAM data = 0) 根据单元格文本内容对指定列排序. 成功则返回TRUE.
BOOL SortItems(int nCol, BOOL bAscending, LPARAM data = 0) 使用比较函数在指定列进行排序.如果没有指定函数,则对行按文本进行排序.成功则返回TRUE.可参考SetCompareFunction()
void SetCompareFunction(PFNLVCOMPARE pfnCompare) 设置对进行排序的回调函数. 可从下面的到更多的细节。
BOOL SortItems(PFNLVCOMPARE pfnCompare, int nCol, BOOL bAscending, LPARAM data = 0) 使用比较函数pfnCompare在指定的列进行排序.从函数CListCtrl::SortItems中可以寻求一些关于这个函数的形式的信息成功则返回TRUE.
排序是通过使用变量SortItems, SortTextItems中的一个或者在listmode下点击列标题头来实现。
注意:在虚拟模式下不能进行排序。这是因为在虚拟模式下,Grid不能存储单元格中的任何信息,因此,也就不能存储任何对单元格进行排序的信息。
处理排序的最简单方法是先设置单元格的比较函数(调用SetCompareFunction),接着调用SortItems(int nCol, BOOL bAscending, LPARAM data = 0)。nCol是要排序的列,bAscending设置是按升序还是按降序排序,data是应用中将要传递给比较函数的的一个特殊数据。
比较函数必须是一个全局或静态函数,其格式如下:
int CALLBACK pfnCellCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
lParam1和 lParam2是CGridCellBase的指针,而lParamSort就是应用中作为参数变量传递给函数SortItems的特殊数据。如果第一个单元格的值小于第二个单元格的值,函数返回-1,相等返回0,否则返回1。
下面就是一个排序比较函数的例子:
int CALLBACK MyClass::pfnCellNumericCompare(LPARAM lParam1,
LPARAM lParam2,
LPARAM lParamSort)
{
CGridCellBase* pCell1 = (CGridCellBase*) lParam1;
CGridCellBase* pCell2 = (CGridCellBase*) lParam2;
if (!pCell1 || !pCell2) return 0;</P><P>int nValue1 = _ttol(pCell1->GetText());
int nValue2 = _ttol(pCell2->GetText());</P><P>if (nValue1 < nValue2)
return -1;
else if (nValue1 == nValue2)
return 0;
else
return 1;
}
还提供了两个有用的函数:
int CALLBACK CGridCtrl::pfnCellTextCompare(LPARAM lParam1,
LPARAM lParam2,
LPARAM lParamSort)
int CALLBACK CGridCtrl::pfnCellNumericCompare(LPARAM lParam1,
LPARAM lParam2,
LPARAM lParamSort)
这两个函数是通过文本内容和数值进行排序的(分别使用itoa)。为Grid设置比较函数,仅仅需要调用:
m_Grid.SetCompareFunction(CGridCtrl::pfnCellNumericCompare);
如果这个比较函数设置为空,那么将默认调用函数CGridCtrl::pfnCellTextCompare</P><P>打印
void EnableWysiwygPrinting(BOOL bEnable = TRUE) 设置WYSIWYG 打印
BOOL GetWysiwygPrinting() 如果设置了WYSIWYG打印则返回TRUE。
void Print() 在用户选择的设备上打印G rid (控件在对话框中有用)
virtual void OnBeginPrinting(CDC *pDC, CPrintInfo *pInfo) 用于文档/浏览环境下,在 CView dervied class' OnBeginPrinting中调用
virtual void OnPrint(CDC *pDC, CPrintInfo *pInfo) 用于文档/浏览环境下. 在CView dervied class' OnPrint中调用
virtual void OnEndPrinting(CDC *pDC, CPrintInfo *pInfo) 用于文档/浏览环境下. 在CView dervied class' OnEndPrinting中调用。
void SetShadedPrintOut(BOOL bEnable = TRUE) 如果为TRUE,则近似打印彩色单元格。如果为FALSE ,则所有文本按照白纸黑字的形式打印出来。.
BOOL GetShadedPrintOut() 判断单元格是否阴影或近似打印。.
void SetPrintMarginInfo(int nHeaderHeight, int nFooterHeight, int nLeftMargin, int nRightMargin, int nTopMargin, int nBottomMargin, int nGap) 设置打印边缘信息。.
void GetPrintMarginInfo(int &nHeaderHeight, int &nFooterHeight, int &nLeftMargin, int &nRightMargin, int &nTopMargin, int &nBottomMargin, int &nGap) 获取打印边缘信息.</P><P>结构、定义和消息
CGridCellBase类
这个类是所有Grid单元格类的基类,包含有每个单元格的信息。同时,它还定义了大量的方法供Grid调用,例如画图和打印。几乎所有的方法和函数都是虚拟模式,类本身不能直接使用--只能被引用。作为Grid控件的默认单元格类CGridCell就是基于CGridCellBase的。
属性
virtual void SetText(LPCTSTR szText);
virtual void SetImage(int nImage);
virtual void SetData(LPARAM lParam);
virtual void SetState(DWORD nState);
virtual void SetFormat(DWORD nFormat);
virtual void SetTextClr(COLORREF clr);
virtual void SetBackClr(COLORREF clr);
virtual void SetFont(const LOGFONT* plf);
virtual void SetGrid(CGridCtrl* pGrid);
virtual void SetCoords(int nRow, int nColumn);
virtual void SetMargin(UINT nMargin);</P><P>virtual LPCTSTR GetText() const // 返回单元格的文本内容
virtual LPCTSTR GetTipText() const // 根据意愿返回标题提示文本
virtual int GetImage() const // 返回单元格的图形索引
virtual LPARAM GetData() const // 返回单元格的关联数据
virtual DWORD GetState() const // 返回单元格的状态
virtual DWORD GetFormat() const // 返回单元格的格式
virtual COLORREF GetTextClr() const // 返回单元格文本颜色
virtual COLORREF GetBackClr() const // 返回单元格背景颜色
virtual LOGFONT* GetFont() const // 以LOGFONT的形式返回单元格的字体
virtual CFont* GetFontObject() const // 将单元格的字体返回成Cfont对象
virtual UINT GetMargin() const // returns internal margin for cell
virtual CGridCtrl* GetGrid() const // 返回与单元格关联的Grid
virtual CWnd* GetEditWnd() const // 返回NULL或单元格的编辑窗口</P><P>virtual BOOL IsEditing() const
virtual BOOL IsFocused() const
virtual BOOL IsFixed() const
virtual BOOL IsFixedCol() const
virtual BOOL IsFixedRow() const
virtual BOOL IsSelected() const
virtual BOOL IsReadOnly() const
virtual BOOL IsModified() const
virtual BOOL IsDropHighlighted() const
virtual BOOL IsDefaultFont() const // 如果单元格使用的是默认字体,返回TRUE</P><P>virtual CGridCellBase* GetDefaultCell() const;
操作符
virtual void operator=(CGridCellBase& cell);</P><P>操作
virtual void Reset();</P><P>virtual BOOL Draw(CDC* pDC, int nRow, int nCol, CRect rect,
BOOL bEraseBkgnd = TRUE);
virtual BOOL GetTextRect( LPRECT pRect) - 单元格内部文本框
virtual BOOL GetTipTextRect( LPRECT pRect) - 工具提示的边框
virtual CSize GetTextExtent(LPCTSTR str) -文本大小
virtual CSize GetCellExtent(CDC* pDC) - 单元格大小</P><P>// 开启和终止对单元格的编辑
virtual BOOL Edit(int nRow, int nCol, CRect rect,
CPoint point, UINT nID, UINT nChar)
virtual void EndEdit()</P><P>// 确认编辑的结果.如果"str"不是一个有效的值,那么返回FALSE并且编辑无效
virtual BOOL ValidateEdit(LPCTSTR str);</P><P>virtual BOOL PrintCell(CDC* pDC, int nRow, int nCol, CRect rect);</P><P>//仅仅只能被基类CgridCellBase调用而不是CgridCellBase本身.
LRESULT SendMessageToParent(int nRow, int nCol, int nMessage);</P><P>重载
virtual void OnEndEdit();
virtual void OnMouseEnter();
virtual void OnMouseOver();
virtual void OnMouseLeave();
virtual void OnClick( CPoint PointCellRelative);
virtual void OnClickDown( CPoint PointCellRelative);
virtual void OnRClick( CPoint PointCellRelative);
virtual void OnDblClick( CPoint PointCellRelative);
virtual BOOL OnSetCursor();</P><P>重载使得制作普通单元格变的极为简单。为创建自己的单元格类,既可以重载CGridCtrl::CreateCell也可以创建自己的源于CGridCellBase的单元格,或者使用CGridCtrl::SetCellType 和 CGridCtrl::SetDeafaultCellType自动产生。
仅仅需要创建一个源于CGridCellBase 或CGridCell (如CMyGridCell)的新单元格类,然后在Grid中调用如下函数代替原来的单元格就可以了。
MyGrid.SetCellType(row, column, RUNTIME_CLASS(CMyGridCell));
现在的单元格(行、列)就是CMyGridCell类型的</P><P>CGridCell类
这个类起源于CGridCellBase并且提供一个使用CGridCtrl的默认实现。</P><P>CcellID类
这是一个用来参考个别单元格的便利的辅助类。所有的成员都是公共的。这个类是根据Joe Willcoxsons的最初实现改写的。
class CCellID
{
public:
int row, col; // 基于0的单元格的行和列.</P><P>CCellID(int nRow = -1, int nCol = -1)</P><P>int IsValid();
int operator==(const CCellID& rhs);
int operator!=(const CCellID& rhs);
}</P><P>CCellRange类
这是一个用来参考单元格范围的便利的辅助类。这是一个从Joe Willcoxsons最初实现改写的类。
class CCellRange
{
public:
CCellRange(int nMinRow = -1, int nMinCol = -1,
int nMaxRow = -1, int nMaxCol = -1);
void Set(int nMinRow = -1, int nMinCol = -1,
int nMaxRow = -1, int nMaxCol = -1);</P><P>int IsValid() const;
int InRange(int row, int col) const; // 判断行/列是否在范围之内
int InRange(const CCellID& cellID) const; // 判断单元格是否在范围之内</P><P>CCellID GetTopLeft() const; //取出左上角单元格
CCellRange Intersect(const CCellRange& rhs) const;
//返回两个区域的交集</P><P>int GetMinRow() const; // 下面的容易理解
void SetMinRow(int minRow);
int GetMinCol() const;
void SetMinCol(int minCol);
int GetMaxRow() const;
void SetMaxRow(int maxRow);
int GetMaxCol() const;
void SetMaxCol(int maxCol);</P><P>int GetRowSpan() const; // 行的跨度
int GetColSpan() const; // 列的跨度</P><P>void operator=(const CCellRange& rhs);
int operator==(const CCellRange& rhs);
int operator!=(const CCellRange& rhs);
}</P><P>结构体GV_ITEM,它在调用Get/SetItem时使用]
typedef struct _GV_ITEM {
int row,col; //对象的行和列
UINT mask; // 用于设置/获取单元格数据的一种掩码
UINT state; //单元格状态 (如焦点/突出显示)
UINT nFormat; //单元格格式.默认使用CDC::DrawText格式
CString szText; // 单元格的文本
int iImage; // 系列可视对象图标的索引
COLORREF crBkClr; // 背景颜色 (或者 CLR_DEFAULT)
COLORREF crFgClr; // 前景颜色(或者 CLR_DEFAULT)
LPARAM lParam; // 与对象有联系的32位值
LOGFONT lfFont; // 单元格的字体
} GV_ITEM;</P><P>网格线或者滚动条的选择
GVL_NONE // 无网格线
GVL_HORZ // 仅仅有水平网格线
GVL_VERT //仅仅有垂直网格线
GVL_BOTH // 水平和垂直网格线都有</P><P>
自动调整大小选项
GVS_DEFAULT //默认
GVS_HEADER //仅用于列的固定单元格数据
GVS_DATA //仅用于列的非固定单元格数据
GVS_BOTH // 固定列和非固定列都适用</P><P>单元格数掩码
GVIF_TEXT // 存取单元格文本
GVIF_IMAGE // 存取单元格图片数量
GVIF_PARAM // 存取单元格用户数据(lParam)
GVIF_STATE // 存取单元格状态
GVIF_BKCLR //存取单元格背景颜色
GVIF_FGCLR // 存取单元格前景颜色
GVIF_FORMAT // 存取单元格格式
GVIF_FONT // 存取单元格逻辑字体
GVIF_MARGIN // 存取单元格边缘信息
GVIF_ALL // 存取所有信息 </P><P>
单元格状态
GVIS_FOCUSED // 单元格成为焦点
GVIS_SELECTED // 选择单元格
GVIS_DROPHILITED // Cell is drop highlighted
GVIS_READONLY // 设置只读,不能编辑
GVIS_FIXED // 单元格锁定
GVIS_FIXEDROW // 单元格是锁定行的一部分
GVIS_FIXEDCOL //单元格是锁定列的一部分
GVIS_MODIFIED // 单元格被修改过</P><P>
单元格查找选项
GVNI_FOCUSED //查找焦点单元格
GVNI_SELECTED // 查找已选单元格
GVNI_DROPHILITED // Search for drop highlighted cells
GVNI_READONLY // 查找只读单元格
GVNI_FIXED // 查找锁定单元格
GVNI_MODIFIED // 查找修改过的单元格</P><P>GVNI_ABOVE // 在初始单元格上方查找
GVNI_BELOW //在初始单元格下方查找
GVNI_TOLEFT // 向左查找初始单元格
GVNI_TORIGHT //向右查找初始单元格
GVNI_ALL // 从指定单元格开始查找全部单元格
GVNI_AREA // 从指定单元格右下方查找单元格</P><P>
通知消息
GVN_BEGINDRAG // 拖曳发生时发送
GVN_BEGINLABELEDIT // 定位编辑开始时发送
GVN_ENDLABELEDIT //定位编辑停止时发送
GVN_SELCHANGING // 单元格选择改变之前发送
GVN_SELCHANGED // 单元格选择改变之后发送
GVN_GETDISPINFO //当Grid处在虚拟模式下的一个单元格信息请求
GVN_ODCACHEHINT // 虚拟模式下的隐藏提示
当不使用NM_GRIDVIEW机构体时,这些消息就很象它们的LVN_...副本
typedef struct tagNM_GRIDVIEW {
NMHDR hdr;
int iRow;
int iColumn;
} NM_GRIDVIEW;</P><P>Protect类型的重载函数
这些函数作成虚拟的以便于扩展。
Printing - 在OnPrint事件中调用.
virtual void PrintColumnHeadings(CDC *pDC, CPrintInfo *pInfo);
virtual void PrintHeader(CDC *pDC, CPrintInfo *pInfo);
virtual void PrintFooter(CDC *pDC, CPrintInfo *pInfo);
virtual void PrintRowButtons(CDC *pDC, CPrintInfo* pInfo);
Drag n' drop
//虽然没什么必要,但是我觉得这些代码很酷,所以还是保留了。:).
virtual CImageList* CreateDragImage(CPoint *pHotSpot)
Mouse Clicks
virtual void OnFixedColumnClick(CCellID& cell);
virtual void OnFixedRowClick(CCellID& cell);
Editing
//开始编辑
virtual void OnEditCell(int nRow, int nCol, CPoint point,
UINT nChar)
// 结束编辑
virtual void OnEndEditCell(int nRow, int nCol, CString str)
//创建定位编辑控件
virtual void CreateInPlaceEditControl(CRect& rect, DWORD dwStyle,
int nRow, int nCol,
LPCTSTR szText, int nChar)
Drawing
virtual void OnDraw(CDC& origDC); //画任何东西
Construction and Cleanup
//创建一个新的单元格并初始化.
virtual CGridCellBase* CreateCell(int nRow, int nCol)</P><P>// 删除一个单元格并作必要的清除工作
virtual void DestroyCell(int nRow, int nCol)</P><P>剪贴板
其中还还包含了很多剪贴板函数
virtual void OnEditCut() 将所选单元格内容拷贝到剪贴板,并删除单元格中的响应内容. (Ctrl-X)
virtual void OnEditCopy() 将所选单元格内容拷贝到剪贴板 (Ctrl-C)
virtual void OnEditPaste() 将剪贴板中的内容粘贴到Grid中. (Ctrl-V)
virtual void OnEditSelectAll() 虽然不是一个真正的剪贴板函数,但会经常用到. 这个程序全选Grid中的单元格 (Ctrl-A)</P><P>单元格的编辑和确认
控制单元格是否可以编辑,或者是否保存或放弃修改的方法有好几种。
最简单的方法就是使用SetEditable(BOOL)。这个函数决定了Grid中的单元格是否可以编辑。可以通过在特定单元格中使用GVIS_READONLY来实现更好的控制。
int row = 1;
int col = 10;
m_Grid.SetItemState(row,col, m_Grid.GetItemState(row,col) | GVIS_READONLY);
深层的控制可以通过处理GVN_BEGINLABELEDIT消息来实现。如果消息的返回值小于0,那么试图对指定单元格所做的修改将被取消同时,这个单元格将被看作是只读属性的。当每次试图编辑某一个单元格时都会发送一个这种消息。
为处理这个消息,需要在主窗口中增加一个句柄:
BEGIN_MESSAGE_MAP(CGridCtrlDemoDlg, CDialog)
...
// 增加句柄
ON_NOTIFY(GVN_ENDLABELEDIT, IDC_GRID, OnGridEndEdit)
END_MESSAGE_MAP()</P><P>...</P><P>// GVN_ENDLABELEDIT
void CGridCtrlDemoDlg::OnGridStartEdit(NMHDR *pNotifyStruct,
LRESULT* pResult)
{
NM_GRIDVIEW* pItem = (NM_GRIDVIEW*) pNotifyStruct;</P><P>// 如果你允许对单元格进行编辑,那么虚构函数AllowCellToBeEdited将返回TRUE
BOOL bAllowEdit = AllowCellToBeEdited(pItem->iRow, pItem->iColumn);</P><P>*pResult = (bAllowEdit)? 0 : -1;
}
可以通过相同的方法处理GVN_ENDLABELEDIT消息来实现是保存还是放弃对单元格的编辑行为。
// GVN_ENDLABELEDIT
void CGridCtrlDemoDlg::OnGridEndEdit(NMHDR *pNotifyStruct,
LRESULT* pResult)
{
NM_GRIDVIEW* pItem = (NM_GRIDVIEW*) pNotifyStruct;</P><P>// 如果你想保存对单元格的修改,那么虚构函数AcceptChange将返回TRUE
BOOL bAcceptChange = AcceptChange(pItem->iRow, pItem->iColumn);</P><P>*pResult = (bAcceptChange)? 0 : -1;
}
你所能用的最终的确认方法是源于CGridCellBase类,并重载ValidateEdit方法的。如果它返回的是TRUE,那么表示保存修改,否则表示放弃修改。</P><P>声名
如果没有下面这些作者的免费代码,这些工作将不会成功。
Joe Willcoxson:我的工作依赖于 Joe的原始代码,它提供了这个控件的基本结构。
Keith Rule:Keith有一个很灵巧的类CMemDC,它能很容易的处理闪烁显示工作。他还提供了copy/paste/drag/drop对象源代码。
Ravi Reddy:我使用了Ravi的一个列表浏览打印代码。
Zafir Anjum:他为我提供了CInPlaceEdit的切入点,并且提供了排序程序和标题提示的源代码。
Eric Woodruff, Brian V. Shifrin, Scot Reed, Aqiruse, Ken Bertelson, Martin Daly帮助我把控件升级到2.X版本--还有很多在以前版本中提供帮助的人。
转自:http://www.cppblog.com/totti1006/archive/2009/11/18/96192.html