根据Windows操作系统的规定,操作系统发送的消息只能由程序的窗口接收。但是在MFC体系特别是基于窗口/视图结构体系的应用程序中程序的每个模块 包括应用程序类、框架窗口类、视图类、文档类都能够接受由操作系统发送而来的消息并进行处理。这并不是MFC体系获得了操作系统的例外而是MFC体系底层 通过一系列的代码实现了一种所谓的消息传递机制。
所谓消息传递机制指的就是当属于应用程序的消息由操作系统传递而来的时候,活动视图是操作系统消息传递机制所传递的窗口类。视图类首先查找其自身对于该消 息有没有对应的消息处理程序,如果没有则传递给文档类,文档类如果不能处理该消息则传递给文档模板,按照上面所描述的过程,消息将按照活动视图-> 文档->文档模板->框架窗口->应用程序对象->::DefWindowProc的流程进行传递。当消息在这个流程的某个节点 上得到处理那么消息传递的过程将被终止。
需要注意的是消息传递机制传递的是命令消息,也就是说仅有COMMAND消息和UPDATE_COMMAND_UI消息将通过这个机制被传递;而其他的消息不会被传递,处理其他消息遵循消息在哪里发生就在哪里处理的规则。
GetTreeCtrl ().SetImageList (&m_ilDrives, TVSIL_NORMAL);
{
ITEMINFO *pItem = new ITEMINFO;
if (pItem)
{
pItem->FileName = fd.cFileName;
pItem->size = fd.nFileSizeLow;
LV_ITEM lvi;
lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
lvi.iImage = 0;
lvi.iSubItem = 0;
lvi.iItem = nIndex;
lvi.pszText = LPSTR_TEXTCALLBACK;
lvi.lParam = (LPARAM) pItem;
GetListCtrl().InsertItem(&lvi);
return true;
}
return false;
}
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
if (pDispInfo->item.mask & LVIF_TEXT)
{
ITEMINFO *pItem = (ITEMINFO *)(pDispInfo->item.lParam);
CString string;
switch (pDispInfo->item.iSubItem)
{
case 0:
lstrcpy(pDispInfo->item.pszText,pItem->FileName);
break;
case 1:
string.Format(_T("%d"),pItem->size);
lstrcpy(pDispInfo->item.pszText,string);
break;
}
}
*pResult = 0;
}
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
GetListCtrl().SortItems(CompareFunc,pNMLV->iSubItem);
*pResult = 0;
}
{
ITEMINFO *pItem1 = (ITEMINFO *)lParam1;
ITEMINFO *pItem2 = (ITEMINFO *)lParam2;
switch (SortCoumn)
{
case 0:
return pItem1->FileName.CompareNoCase(pItem2->FileName);
case 1:
return (pItem1->size) - (pItem2->size);
}
}
m_ilLarge.Create(IDB_LARGE,32,1,RGB(255,0,255));
GetListCtrl().SetImageList(&m_ilSmall,LVSIL_SMALL);
GetListCtrl().SetImageList(&m_ilLarge,LVSIL_NORMAL);
MDI和拆分窗口
一、MDI应用程序
MDI应用程序提供了一个多文档应用程序的解决方案。创建MDI应用程序只需要在程序向导当中做相应的设置即可,程序的实现与SDI应用程序完全相同。 MDI应用程序需要注意的仅仅是当某个文档当中的数据被改变后需要将与文档关联的所有视图当中的显示都进行更新,这个操作与SDI应用程序相同调用 UpdateAllViews函数,这个函数的第一个参数是一个指向视图的指针,这个指针指向的视图将不会被更新,所以如果要更新所有的关联视图则可以将 这个指针设置为NULL。另外这个函数的默认过程是调用OnUpdate函数并有这个函数使整个视图失效并调用OnDraw函数重绘整个视图。如果要提高 绘制效率也就是说只将视图的被修改部分重绘,那么我们需要用到UpdateAllViews的后两个参数,一个LPARAM类型的pHint参数和一个 CObject *的pHint参数,前一个参数可以设置为一个标志值,后一个参数可以用来传递一个CObject对象比如CRect告诉程序需要重绘的部分,然后在 OnUpdate函数当中根据两个相同的函数完成部分重绘过程。
二、窗口拆分
除了使用MDI体系结构来实现一个文档多个视图的程序外,我们也可以使用SDI结构来实现。使用SDI来实现一个文档多个视图的方法称为窗口拆分。窗口拆 分可以分为动态拆分和静态拆分两种;动态拆分是指窗口在行方向上和列方向上能构被拆分为几个子窗口由用户在程序运行时决定,静态拆分指程序在行方向祸者列 方向上能够拆分为几个窗口由程序员在程序编码阶段确定,用户不能更改拆分方式。无论是静态拆分还是动态拆分都只能在行方向或者列方向的其中一个方向上对窗 口进行拆分。动态拆分限制在行或列方向上只能拆分2个子窗口,而静态拆分可以拆分为16个子窗口。
1.动态拆分
动态拆分窗口的方法很简单,按照标准的SDI程序创建程序以后在MainFrame类当中添加一个CSplitterWnd类数据对象,并且在 MainFrame类的OnCreateClient成员函数当中加入对CSplitterWnd对象的Create过程调用,在这个成员函数当中第一个 参数表示了其父窗口对象的指针,第二第三个参数分别表示行、列方向上的拆分数,第四个参数表示拆分窗口的最小宽与高当拆分窗口小于这个数值时拆分窗口将会 被移除,第五个参数是一个指向主结构的CCreateContext指针.
2.静态拆分
静态拆分的窗口样式在程序编码时就被确定不允许用户修改,但是静态拆分的窗口在每个子窗口当中允许使用不同的视图类型。静态拆分窗口的方法可以用下面的3步描述:
1.在MainFrame类当中声名一个CSpilitterWnd对象;
2.在MainFrame类的OnCreateClient成员函数当中调用CSplitterWnd对象的CreateStatic函数确定拆分样式,其原型为:
virtual BOOL CreateStatic(
CWnd* pParentWnd,
int nRows,
int nCols,
DWORD dwStyle = WS_CHILD | WS_VISIBLE,
UINT nID = AFX_IDW_PANE_FIRST
);
3.接着对每个子窗口调用CSplitterWnd的CreateView函数用来创建视图,该函数的原型为:
virtual BOOL CreateView(
int row,
int col, //行列位置
CRuntimeClass* pViewClass, //运行时视图类指针,需要使用RUNTIME_CLASS宏
SIZE sizeInit, //视图大小,对于可以由系统确定的值,即使给定值也将被忽略
CCreateContext* pContext
);
下面是一个MainFrame::OnCreateClient函数的例子:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
if (!m_SplitterWnd.CreateStatic(this,1,2) ||
!m_SplitterWnd.CreateView(0,0,RUNTIME_CLASS(CExplorerView),CSize(192,0),pContext) ||
!m_SplitterWnd.CreateView(0,1,RUNTIME_CLASS(CFileView),CSize(0,0),pContext))
return FALSE;
return TRUE;
}
在按照上面的步骤定义完一个拆分窗口以后,需要解决的问题还有不同视图类之间的数据通信问题。在MFC的文档/视图体系中数据是被存放在文档中的,但是某 些时候两个视图之间需要直接进行数据交换。实现这个功能的方法是在视图当中调用GetDocument获取文档类,并调用UpdateAllViews函 数,通过其中的lhInt和phInt参数来传递数据,同时视图类通过OnUpdate成员函数来接受数据。
由于静态拆分窗口以后窗口中可能包括不止一个视图类,而且MFC文档/视图体系结构的消息传递机制是活动视图->文档->文档模板-> 框架窗口->应用程序对象->DefWndProc,这样就导致了某些需要传递给某个非活动视图的消息不能被传递到达,因此我们需要修改默认 的消息传递机制。首先我们知道文档对象可以枚举所有的视图对象,因此我们可以利用这个机制来传递消息。具体过程是文档对象枚举除要求枚举的视图对象外的所 有视图对象。而某个可能的活动视图要求文档枚举所有视图对象并传递默认消息处理机制所不能处理的消息。
BOOL CExplorerDoc::RouteToAllViews(CView *pView,UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo) //文档对象枚举视图
{
POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
CView *pNextView = GetNextView(pos);
if (pNextView != pView)
if (pNextView->OnCmdMsg(nID,nCode,pExtra,pHandlerInfo))
return TRUE;
}
return FALSE;
}
BOOL CExplorerView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
if (CTreeView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) //使用默认消息传递机制,成功(返回TRUE)就结束,否则开始枚举
return TRUE;
else
{
CExplorerDoc *pDoc = GetDocument();
if (pDoc)
return pDoc->RouteToAllViews(this,nID,nCode,pExtra,pHandlerInfo);
return FALSE;
}
}