??xml version="1.0" encoding="utf-8" standalone="yes"?> 包含d新项到树(wi)形视控g所使用的信息。这个结构被TVM_INSERTITEM消息使用。这个结构与TV_INSERTSTRUCTl构是一L(fng)Q但它已l按当前的命名习(fn)惯重命名了?/p>
Windows NT/2000Q需要Windows NT 3.51或更高版本? 指定或接收树(wi)形视的属性。这个结构与TV_ITEMl构一P但它已经被当前命名协议重新命名了。新的应用程序应该用这个结构?/p>
q个成员??位包含了的状态标记。关于可能的状态标讎ͼ参见Tree View Control Item States. 覆盖囑փ覆盖在项的图标图像之上。这个成员的8?1位指定了?为基准的覆盖囑փ索引。如果这些位?Q这个项没有覆盖囑փ。要隔离q些位,使用TVIS_OVERLAYMASK掩码。要在这个成员中讄覆盖囑փ索引Q用INDEXTOOVERLAYMASK宏。图像列表的覆盖囑փ是被ImageList_SetOverlayImage函数讄的?/p>
一个状态图像是仅次于指出应用程序定义的状态的的图标昄的。通过发送TVM_SETIMAGELIST消息来指定一个状态图像列表。要讄一个项的状态图像,在TVITEMl构的stateMask成员中包含TVIS_STATEIMAGEMASK倹{结构的state成员?2?5位指定状态图像列表中被绘制图像的索引?/p>
要设|状态图像烦引,使用INDEXTOSTATEIMAGEMASK。这个宏把一个烦引适当的设|到12?5位上。要指出Ҏ(gu)有状态图像,讄索引?。这意味着在状态图像列表中的图?不能被作Z个状态图像用。要隔离state成员的位12?5Q用TVIS_STATEIMAGEMASK掩码?/p>
如果l构是取回项的属性,q个成员是取回项文本~冲的地址?/p>
如果q个成员是值I_IMAGECALLBACKQ父H口Z存烦引负责。既然这P当树(wi)形视控g需要显C个图像时Q向父窗口发送TVN_GETDISPINFO通知消息来获得烦引?/p>
如果q个成员是值I_IMAGECALLBACKQ父H口Z存烦引负责。既然这P当树(wi)形视控g需要显C个图像时Q向父窗口发送TVN_GETDISPINFO通知消息来获得烦引?/p>
If the tree view control has the TVS_HASBUTTONS style, it uses this member to determine whether to display the button indicating the presence of child items. You can use this member to force the control to display the button even though the item does not have any child items inserted. This allows you to display the button while minimizing the control's memory usage by inserting child items only when the item is visible or expanded. Windows NT/2000Q需要Windows NT 3.51或更高版本? 包含关于?wi)Ş视通知消息的信息。这个结构与NM_TREEVIEWl构一P但它已经用当前的命名规则q行了重命名?/p>
WM_NOTIFY
函数原型QBOOL SetWindowPosQHWN hWndQHWND hWndlnsertAfter,int XQint Y,int cxQint cy,UNITQFlagsQ;
参数Q?br>
hWnd:H口句柄?br>
hWndlnsertAfter:在z序中的位于被|位的窗口前的窗口句柄。该参数必须Z个窗口句柄,或下列g一Q?br>
HWND_BOTTOMQ将H口|于Z序的底部。如果参数hWnd标识了一个顶层窗口,则窗口失去顶U位|,q且被置在其他窗口的底部?br>
HWND_DOTTOPMOSTQ将H口|于所有非层H口之上Q即在所有顶层窗口之后)。如果窗口已l是非顶层窗口则该标志不起作用?br>
HWND_TOP:窗口置于Z序的剙?br>
HWND_TOPMOST:窗口置于所有非层H口之上。即使窗口未被激zȝ口也保持顶U位|?br>
查g看该参数的用方法,L(fng)说明部分?br>
xQ以客户坐标指定H口C|的左边界?br>
YQ以客户坐标指定H口C|的边界?br>
cx:以像素指定窗口的新的宽度?br>
cyQ以像素指定H口的新的高度?br>
uFlags:H口寸和定位的标志。该参数可以是下列值的l合Q?br>
SWP_ASNCWINDOWPOSQ如果调用进E不拥有H口Q系l会向拥有窗口的U程发出需求。这防止调用线E在其他U程处理需求的时候发生死锁?br>
SWP_DEFERERASEQ防止生WM_SYNCPAINT消息?br>
SWP_DRAWFRAMEQ在H口周围M个边框(定义在窗口类描述中)?br>
SWP_FRAMECHANGEDQ给H口发送WM_NCCALCSIZE消息Q即使窗口尺寸没有改变也会发送该消息。如果未指定q个标志Q只有在改变了窗口尺寸时才发送WM_NCCALCSIZE?br>
SWP_HIDEWINDOW;隐藏H口?br>
SWP_NOACTIVATEQ不Ȁzȝ口。如果未讄标志Q则H口被激z,q被讄到其他最高H口或非最高l的剙Q根据参数hWndlnsertAfter讄Q?br>
SWP_NOCOPYBITSQ清除客户区的所有内宏V如果未讄该标志,客户区的有效内容被保存ƈ且在H口寸更新和重定位后拷贝回客户区?br>
SWP_NOMOVEQ维持当前位|(忽略X和Y参数Q?br>
SWP_NOOWNERZORDERQ不改变z序中的所有者窗口的位置?br>
SWP_NOREDRAW:不重L变的内容。如果设|了q个标志Q则不发生Q何重d作。适用于客户区和非客户区(包括标题栏和滚动条)和Q何由于窗回移动而露出的父窗口的所有部分。如果设|了q个标志Q应用程序必L地使窗口无效ƈ区重ȝ口的M部分和父H口需要重ȝ部分?br>
SWP_NOREPOSITIONQ与SWP_NOOWNERZORDER标志相同?br>
SWP_NOSENDCHANGINGQ防止窗口接收WM_WINDOWPOSCHANGING消息?br>
SWP_NOSIZEQ维持当前尺寸(忽略cx和Cy参数Q?br>
SWP_NOZORDERQ维持当前Z序(忽略hWndlnsertAfter参数Q?br>
SWP_SHOWWINDOWQ显C窗口?br>
q回|如果函数成功Q返回gؓ非零Q如果函数失败,q回gؓ零。若惌得更多错误消息,误用GetLastError函数?br>
备注Q如果设|了SWP_SHOWWINDOW和SWP_HIDEWINDOW标志Q则H口不能被移动和改变大小。如果用SetWindowLoog改变了窗口的某些数据Q则必须调用函数SetWindowPos来作真正的改变。用下列的l合标志QSWP_NOMOVEISWP_NOSIZEISWP_FRAMECHANGED?br>
有两U方法将H口设ؓ最层H口Q一U是参数hWndlnsertAfter讄为HWND_TOPMOSTq确保没有设|SWP_NOZORDER标志Q另一U是讄H口在Z序中的位|以使其在其他存在的H口之上。当一个窗口被|ؓ最层H口Ӟ属于它的所有窗口均为最层H口Q而它的所有者的z序ƈ不改变?br>
如果HWND_TOPMOST和HWND_NOTOPMOST标志均未指定Q即应用E序要求H口在激zȝ同时改变其在Z序中的位|时Q在参数hWndinsertAfter中指定的值只有在下列条g中才使用Q?br>
在hWndlnsertAfter参数中没有设定HWND_NOTOPMOST和HWND_TOPMOST标志?br>
由hWnd参数标识的窗口不是激zȝ口?br>
如果未将一个非Ȁzȝ口设定到z序的端Q应用程序不能激z该H口。应用程序可以无M限制地改变被Ȁzȝ口在Z序中的位|,或激zM个窗口ƈ其Ud最高H口的顶部或非最高H口的顶部?br>
如果一个顶层窗口被重定位到z序的底部QHWND_BOTTOMQ或在Q何非最高序的窗口之后,该窗口就不再是最层H口。当一个最层H口被置为非最Q则它的所有者窗口和所属者窗口均为非最层H口?br>
一个非最端H口可以拥有一个最端H口Q但反之则不可以。Q何属于顶层窗口的H口Q例如一个对话框Q本w就被置为顶层窗口,以确保所有被属窗口都在它们的所有者之上?br>
如果应用E序不在前台Q但应该位于前台Q就应调用SetForegroundWindow函数来设|?br>
Windows CEQ如果这是一个可见的层H口Qƈ且未指定SWP_NOACTIVATE标志Q则q个函数激zȝ口、如果这是当前的Ȁzȝ口,q且指定了SWP_NOACTIVATE或SWP_HIDEWINDOW标志Q则Ȁzd外一个可见的层H口?br>
当在q个函数中的nFlags参数里指定了SWP_FRAMECHANGED标志ӞW(xu)indowsCE重画H口的整个非客户区,q可能会改变客户区的大小。这也是重新计算客户区的唯一途径Q也是通过调用SetwindowLong函数改变H口风格后通常使用的方法?br>
SetWindowPosWM_WINDOWPOSCHANGED消息向窗口发送,在这个消息中传递的标志与传递给函数的相同。这个函C传递其他消息?/div>
]]>TVINSERTSTRUCT
typedef struct tagTVINSERTSTRUCT {
HTREEITEM hParent;
HTREEITEM hInsertAfter;
#if (_WIN32_IE >= 0x0400)
union
{
TVITEMEX itemex;
TVITEM item;
} DUMMYUNIONNAME;
#else
TVITEM item;
#endif
} TVINSERTSTRUCT, FAR *LPTVINSERTSTRUCT;
成员
?/th>
意味
TVI_FIRST
在列表的开始插入项
TVI_LAST
在列表的最后插入项
TVI_ROOT
作ؓ一个根Ҏ(gu)?/td>
TVI_SORT
以字母顺序插入项
需?/h4>
Windows 95/98Q需要Windows 95或更高版本?
HeaderQ定义在commctrl.h?/p>
TVITEM
typedef struct tagTVITEM{
UINT mask;
HTREEITEM hItem;
UINT state;
UINT stateMask;
LPTSTR pszText;
int cchTextMax;
int iImage;
int iSelectedImage;
int cChildren;
LPARAM lParam;
} TVITEM, FAR *LPTVITEM;
成员
TVIF_CHILDREN
cChildren成员是有效的?/td>
TVIF_DI_SETITEM
?wi)Ş视控件将保留支持信息q且不重新请求它。当处理TVN_GETDISPINF通知Ӟq个标记是有效的?/td>
TVIF_HANDLE
hItem成员有效?/td>
TVIF_IMAGE
iImage成员有效?/td>
TVIF_PARAM
lParam成员有效?/td>
TVIF_SELECTEDIMAGE
iSelectedImage成员有效?/td>
TVIF_STATE
state和stateMask成员有效?/td>
TVIF_TEXT
pszText和cchTextMax成员有效?/td>
zero
q个Ҏ(gu)有子V?/td>
one
q个Ҏ(gu)一个或更多的子V?/td>
I_CHILDRENCALLBACK
The parent window keeps track of whether the item has child items. In this case, when the tree view control needs to display the item, the control sends the parent a TVN_GETDISPINFO notification message to determine whether the item has child items.
需?/h4>
Windows 95/98Q需要Windows 95或更高版本?
HeaderQ定义在commctrl.h?/p>
NMTREEVIEW
typedef struct tagNMTREEVIEW {
NMHDR hdr;
UINT action;
TVITEM itemOld;
TVITEM itemNew;
POINT ptDrag;
} NMTREEVIEW, FAR *LPNMTREEVIEW;
成员
参见
]]>
Invalidate()
UpdateWindow()
当需要更新或者重l窗口时Q一般系l会发出两个消息WM_PAINT(通知客户区有变化)和W(xu)M_NCPAINT(通知非客户区有变?WM_NVPAINTpȝ会自己搞定WM_PAINT消息对应的函数是OnPaint(),它是pȝ默认的接受WM_PAINT消息的函敎ͼ但我们一般在E序中做重绘旉在OnDraw函数中进行的Q因为在视图cONPAINT函数中调用了ONDRAW函数?br> CView默认的标准的重画函数
void CView::OnPaint()
{
CPaintDC dc(this);
OnPreparDC(&dc)Q?nbsp;
OnDraw(&dc); //调用了OnDraw}
上面讲到InvalidateRect(&Rect)Invalidate()两个函数形式和功能差不多但Invalidate是得整个窗口无效,形成无效矩ŞQ而InvalidateRect(&Rect)是得指定的区域无效 Invalidate()x无效Q等待WM_PAINT消息以便重绘Q队列中无其他消息时pȝ会自动发送UpdateWindow()会立卛_送WM_PAINT,不过在它发送前Q先调用GetUpdateRect(hWnd,NULL,TRUE)看有无可l制区域Q如果没有则不发送消?RedrawWindow()RedrawWindow()则是hInvalidate()和UpdateWindow()的双Ҏ(gu)。声明窗口的状态ؓ无效Qƈ立即更新H口Q立卌用WM_PAINT消息处理?nbsp;
pȝZ么不在调用Invalidate时发送WM_PAINT消息呢?又ؓ什么非要等应用消息队列为空时才发送WM_PAINT消息呢?q是因ؓpȝ把在H口中的l制操作当作一U低优先U的操作Q于是尽 可能地推后做。不q这样也有利于提高绘制的效率Q两个WM_PAINT消息之间通过InvalidateRect和InvaliateRgn使之失效的区域就会被累加hQ然后在一个WM_PAINT消息中一ơ得?更新Q不仅能避免多次重复地更新同一区域Q也优化了应用的更新操作。像q种通过InvalidateRect和InvalidateRgn来ɽH口区域无效Q依赖于pȝ在合适的时机发送WM_PAINT消息的机 制实际上是一U异步工作方式,也就是说Q在无效化窗口区域和发送WM_PAINT消息之间是有延迟的;有时候这Ugqƈ不是我们希望的,q时我们当然可以在无效化H口区域后利用SendMessage 发送一条WM_PAINT消息来强制立即重画,但不如用Windows GDI为我们提供的更方便和强大的函敎ͼ
UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的 Update RegionQ当其不为空时才发送WM_PAINT消息QRedrawWindow则给我们更多的控Ӟ是否重画非客户区和背景,是否L发?WM_PAINT消息而不Update Region是否为空{?BeginPaint和W(xu)M_PAINT消息紧密相关。试一试在WM_PAINT处理函数中不写BeginPaint会怎样Q程序会像进入了一个死循环一栯到惊人的CPU占用率,你会发现E序d处理一个接 一个的WM_PAINT消息。这是因为在通常情况下,当应用收到WM_PAINT消息ӞH口的Update Region都是非空的(如果为空׃需要发送WM_PAINT消息了)QBeginPaint的一个作用就是把该Update Region|ؓI,q样如果不调用BeginPaintQ窗口的Update Region׃直不为空Q如前所qͼpȝ׃一直发送WM_PAINT消息?BeginPaint和W(xu)M_ERASEBKGND消息也有关系。当H口的Update Region被标志ؓ需要擦除背景时QBeginPaint会发送WM_ERASEBKGND消息来重画背景,同时在其q回信息里有一个标志表明窗口背景是否被重画q。当我们用InvalidateRect和InvalidateRgn来把指定区域加到Update Region中时Q可以设|该区域是否需要被擦除背景Q这样下一个BeginPaintq道是否需要发送WM_ERASEBKGND消息了。另外要注意的一Ҏ(gu)QBeginPaint只能在WM_PAINT处理函数中用?
]]>
当你使用了ClassWizard建立了控件和变量之间的联pdQ当你修改了变量的|而希望对话框控g更新昄Q就应该在修改变量后调用UpdateData(FALSE)Q如果你希望知道用户在对话框中到底输入了什么,应该在讉K变量前调用UpdateData(TRUE),控件的输入映射到变量中?/p>
Invalidate():
该函数的作用是整个H口客户区无效。窗口的客户区无效意味着需要重l,例如Q如果一个被其它H口遮住的窗口变成了前台H口Q那么原来被遮住的部分就是无效的Q需要重l。这时Windows会在应用E序的消息队列中攄WM_PAINT消息。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaintQOnPaint负责重绘H口。视囄有一些例外,在视囄的OnPaint函数中调用了OnDraw函数Q实际的重绘工作由O(jin)nDraw来完成。参数bErase为TRUEӞ重绘区域内的背景 被擦除Q否则,背景保持不变?/p>
InvalidateRect():
该函数的功能与Invalidate基本一P不同的是Q它是指定的某个区域无效,需要输入一个区域?/p>
UpdateWindow():
UpdateWindow( )的作用是使窗口立即重l。调用Invalidate{函数后H口不会立即重绘Q这是由于WM_PAINT消息的优先很低Q它需要等消息队列中的其它消息发送完后才能被处理。调用UpdateWindow函数可WM_PAINT被直接发送到目标H口Q从而导致窗口立即重l?br>
UpdateWindowQ?strong>如果有无效区Q则马上sending a WM_PAINT message到窗口处理过E,不进消息队列q行排队{待Q立卛_新窗口,否则Q什么都不做?/strong>
InvalidateRectQ设|无效区Q如果ؓNULL参数Q则讄整个H口为无效区。当应用E序的那个窗口的消息队列为空Ӟ则sending a WM_PAINT message(即更新区域为空).在sending a WM_PAINT message的所有InvalidateRect的更新区域会累加?
1:讄无效?
InvalidateRect
2Q立卛_?
UpdateWindow();
如果不调?nbsp;InvalidateRectp?nbsp;UpdateWindowQ那么UpdateWindow什么都不做?/strong> ??????
如果调用 InvalidateRect 后不调用UpdateWindowQ则pȝ会自动在H口消息队列为空的时候,pȝ自动发送一WM_PAINT消息?
调用UpdateWindow()时将会发送一个WM_PAINT消息Q而应用程序在接收到WM_PAINT消息后,自动地调用Invalidate(),所以,在程序代码中Q不一定要出现Invalidate()!
UpdateWindow()是立即发送WM_PAINT消息,只对声明无效的区域v作用Q?/strong> Invalidate()表示客户区域无效Q在下次WM_PAINT发生旉l?strong>而W(xu)M_PAINT是由pȝq行l护的,每当CWnd的更新区域不为空Qƈ且在应用E序的窗口消息队列中没有其它消息ӞW(xu)indows发送一条WM_PAINT消息
Invalidate()则是声明无效的方式之一?/p>
Invalidate里面有个bool型的参数Q用来标识重l的时候是否用背景色填充。是不是用SetBkcolor函数Q下ȝl研I?/p>
updateWindow则是要求pȝ对区域进行立即重l?/p>
看到有h在网上提出问题,他在Invalidate后面又写了绘囄函数但是没有执行Q因为invalidate执行q以后{到PAINT命o了。所以后面的都没有显C?/p>
也终于想通我l的图一直在闪啊闪,因ؓ我在PAINT里面用到Invalidate()函数Q所以他不停的自嵌套Q倒是l的图不停的闪?/p>
Invalidate让客户区处于可以重画的状态,而UpdateWindow开始重画,但是它先判断客户区是否ؓI,不空UpdateWindow不执行,为空才执行重甅R?/p>
Invalidat最后也是调用InvalidatRect,在windows API里只有InvalidatRect?/p>
作ؓ一个程序员有许多普通的windows控g可以用在应用E序的外观上。许多的控g的从列表到按钮再到进E条都是可以现成的用。尽如此,在如此多的控件中我们q是会碰到那些标准的控g不够用的时候。欢q进入子分类控g的艺术?/span>
子分cM?span>windows控g不像子分cM?span>C++cR子分类一个控件意呌你用你自q消息处理函数取代了改控g的一些或者所有的消息处理函数。你可以有效的截h控g的消息ƈ使它按照你的意愿行事Q而非windows的默认方式。这可以让改控g实现大多数而非全部的你惛_到的行ؓQƈ且它表现得很完。有两种cd的子分类Q局部子分类和全局子分cR局部子分类是子分cM个实体,全局子分cd是将一个特定类型的控g全部子分成你的类型?/span>
C一个从CWndcL生的cd象和一个与它相联的H口Q?span>hwndQ的区别是很重要的?span>CWnd的派生类对象包含一个成员变量指?span>hwndQ而且包含那些通过hwnd作ؓ参数的处理消息的函数(比如Q?span>WM_PAINT, WM_MOUSEMOVE)。当你子分类一个控仉过你的C++对象Ӟ你就是将相应?span>hwndq接C?span>C++对象上ƈ把改控g激发的消息回调函数Ҏ(gu)你的?/span>
子分cL很容易的。首先,你创Z个处理了你感兴趣的所以消息的c,然后该cL子分一个已l存在的控g使它按照你的新类的行事。某中方面上Ҏ(gu)件已l变成了你所拥有的了。在q个例子中我们将子分一个按钮控件ƈ且它做一些它从来都没能够做的事?/span>
一个新c?/span>
子分一个控件我们需要创Z个新cLcd该处理了所有我们感兴趣的消息。由于我们很懒,最好是使我们处理的消息最,而且最好的方式是从你将要子分的控gz你的新类Q我们这里选择的是CButton?/span>
我们设想的是使按钮在鼠标每次l过时显C出高亮的黄艌Ӏ奇怪的事已l生了。首先我们通过向导创徏一个从CButtonz的新c?span>CMyButton?/span>
通过MFC框架zCButtoncL许多优点Q最大的是我们不需要实际的为我们的cL加一行控件的代码。假如我们愿意我们可以通过我们的新cd下一步子分一个按钮控Ӟ管有些烦琐。这是因?span>MFC实现了所有缺省的消息处理函数Q所以我们可以简单的选择一个我们感兴趣的消息忽略其他的?/span>
However for this example we have loftier plans for our control - making it bright yellow.
无论怎样在这个例子中我们我们的控件表面高亮黄色显C?/span>
Z查鼠标是否是l过了控件我们将讄一个布?yu)(dng)变?span>m_bOverControl?span>TRUE当鼠标进入控件边界时Qƈ且通过定时器实时的查跟t鼠标什么时候离开了控件。不q的是我们没有^台提供的OnMouseEnter ?span> OnMouseLeave函数可用Q我们将通过OnMouseMove。加入我么在某个时候发现鼠标不再在控g上,我们关闭定时器q画该控g?/span>
通过cd导添?span>WM_MOUSEMOVE?span>WM_TIMER消息处理函数OnMouseMove?span>OnTimer ?/span>
cd导将会添加如下代码到你的?span>buttonc:
BEGIN_MESSAGE_MAP(CMyButton, CButton)
//{{AFX_MSG_MAP(CMyButton)
ON_WM_MOUSEMOVE()
ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMyButton message handlers
void CMyButton::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CButton::OnMouseMove(nFlags, point);
}
void CMyButton::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
CButton::OnTimer(nIDEvent);
}
消息映像的入口将消息映射成函数?span>ON_WM_MOUSEMOVE映射成函?span>OnMouseMoveQ?span>ON_WM_TIMER映射?span>OnTimer。这些宏?span>MFC源代码中定义Q但是它们不需要阅诅R这个练?fn)仅仅知道它们这样处理就可以了?/span>
我们定义了两个布?yu)(dng)变?span>m_bOverControl?span>m_nTimerQ一?span>UNIT变量Q在构造函数里面初始化它们Q我们的消息处理函数如下Q?/span>
IDvoid CMyButton::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_bOverControl) // Cursor has just moved over control
{
TRACE0("Entering control\n");
m_bOverControl = TRUE; // Set flag telling us the mouse is in
Invalidate(); // Force a redraw
SetTimer(m_nTimerID, 100, NULL); // Keep checking back every 1/10 sec
}
CButton::OnMouseMove(nFlags, point); // drop through to default handler
}
void CMyButton::OnTimer(UINT nIDEvent)
{
// Where is the mouse?
CPoint p(GetMessagePos());
ScreenToClient(&p);
// Get the bounds of the control (just the client area)
CRect rect;
GetClientRect(rect);
// Check the mouse is inside the control
if (!rect.PtInRect(p))
{
TRACE0("Leaving control\n");
// if not then stop looking...
m_bOverControl = FALSE;
KillTimer(m_nTimerID);
// ...and redraw the control
Invalidate();
}
// drop through to default handler
CButton::OnTimer(nIDEvent);
}
我们的新cL后将要做的就是绘Ӟq里我们不是要处理一个消息而是重蝲一CWnd的虚Ҏ(gu)DrawItem。这个方法仅仅是在自l控件时调用Q而且没有一个缺省的实现可以让你调用。(可以通过ASSERT'试试Q这个方法被设计成仅被重载以及被zcM用?/span>
通过向导d一?/span>DrawItem
Ҏ(gu)q且d如下代码Q?/span>
Collapse
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rect = lpDrawItemStruct->rcItem;
UINT state = lpDrawItemStruct->itemState;
CString strText;
GetWindowText(strText);
// draw the control edges (DrawFrameControl is handy!)
if (state & ODS_SELECTED)
pDC->DrawFrameControl(rect, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED);
else
pDC->DrawFrameControl(rect, DFC_BUTTON, DFCS_BUTTONPUSH);
// Deflate the drawing rect by the size of the button's edges
rect.DeflateRect( CSize(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)));
// Fill the interior color if necessary
if (m_bOverControl)
pDC->FillSolidRect(rect, RGB(255, 255, 0)); // yellow
// Draw the text
if (!strText.IsEmpty())
{
CSize Extent = pDC->GetTextExtent(strText);
CPoint pt( rect.CenterPoint().x - Extent.cx/2,
rect.CenterPoint().y - Extent.cy/2 );
if (state & ODS_SELECTED)
pt.Offset(1,1);
int nMode = pDC->SetBkMode(TRANSPARENT);
if (state & ODS_DISABLED)
pDC->DrawState(pt, Extent, strText, DSS_DISABLED, TRUE, 0, (HBRUSH)NULL);
else
pDC->TextOut(pt.x, pt.y, strText);
pDC->SetBkMode(nMode);
}
}
所有的都已l做好了-q缺最后一步?span>DrawItemҎ(gu)需要被l制控g可以自绘。这可以通过在对话框~辑器里选中相应的选项实现-但是更好的方式是通过c自p|样式得该cL为真正替?span>CButton?#8220;drop-in”。ؓ了实现这Ҏ(gu)们需要重写最后一个方法:PreSubclassWindow
.
q个Ҏ(gu)?/span>SubclassWindow
调用Qƈ依次?span>CWnd::Create
或?/span>DDX_Control
调用Q这意味着加入你动态或者通过对话框模板创Z个新cȝ对象Q?span>PreSubclassWindow仍然会被调用?span>PreSubclassWindow
在你子分的控g已经产生但是qؓ昄之前调用。换句话说这是控g的一个完的初始化时刅R?/span>
需要重Ҏ(gu)意的一Ҏ(gu)Q假如你的控件是通过对话框编辑器创徏Q那么你子分的控件将不会?span>WM_CREATE消息Q因此我们不能通过使用OnCreate
来初始化Q因为它Ҏ(gu)不会被调用?/span>
通过向导重蝲PreSubclassWindowq添加如下代码:
void CMyButton::PreSubclassWindow()
{
CButton::PreSubclassWindow();
ModifyStyle(0, BS_OWNERDRAW); // make the button owner drawn
}
贺?span>-你现在已l有一?span>CButton的派生类了!
通过DDX在创建的时候子分一个窗?/span>
在这个例子中我们要子分的控g是在对话框上攄的:
我们让正常的对话框创建流E创Z个带有控件的对话框,然后通过DDX和我们的新类子分Ҏ(gu)件。ؓ了做到这点,我们仅仅需要通过向导d一个控件变量其成为对话框cȝ成员Q在q里它的ID?/span>IDC_BUTTON1
Q,cd?span>CMyButton?/span>
向导在你的对话框成员Ҏ(gu)DoDataExchange中生了一?span>DDX_Control调用?span>DDX_Control会调?span>SubclassWindow使得该按钮?span>CMyButton代替常规?span>CButton的处理方法。此按钮已经被劫持而且会按照你的设惛_Ş式了?/span>
子分cM个窗口但是不被向D?/span>
加入你添加一个窗口类C的工Eƈ且希望通过q个cȝ一个对象子分一个窗口,但是向导q不允许你的新类作ؓ一个类型选项Q这时你也许需要重新构建类向导文g了?/span>
先备份工E中?span>.clw文gQ然后删除之Q接着?span>Visual Studio上按CTRL+W。你会看到一个提CZ哪些文g被包含到类扫描q程中。确信你的新cL件在其中?/span>
现在你的新类可以作Z个类选项了,如果不是Q你仍然可以通过cdg生一个控Ӟ比如?span>CButtonQ,然后在头文g中手动修改其cdQ比?span>CMyButtonQ?/span>
子分一个存在的H口
使用DDX很简单,但是它不能帮助我们子分一个已l存在的控g。比如说Q你惛_分一个组合框中的~辑框控件。你需要在你子分编辑框控g之前已经创徏了组合框Q因此它的子~辑框窗口也创ZQ?/span>
在这U情况下你可以用非常好用的SubclassDlgItem 或?span> SubclassWindowҎ(gu)。这两个Ҏ(gu)允许你动态的子分一个窗?span>-换句话说Q可以连接一个你的新H口对象C个已l存在的H口?/span>
例如,假设我们包含ID?/span>IDC_BUTTON1
的按钮的对话框。那个按钮已l被创徏了,我们希望兌一个类型ؓCMyButton的对象到那个按钮上从而该按钮按照我们希望的方式响应?/span>
Z做这些我们需要有一个已l存在的新类对象Q一个对话框或者视囄成员变量是最好的?/span>
CMyButton m_btnMyButton;
接着在对话框中调?/span>OnInitDialog
Q或者Q何恰当的地方Q:
m_btnMyButton.SubclassDlgItem(IDC_BUTTON1, this);
另一斚wQ假设你已经有一个希望子分类的指向窗口的指针或者一个从CView
或其?/span>CWnd
z的类中动态创建的控gQ而你不希望?span>SubclassDlgItemQ那么可以简单的调用Q?/span>
CWnd* pWnd = GetDlgItem(IDC_BUTTON1); // or use some other method to get
// a pointer to the window you wish
// to subclass
ASSERT( pWnd && pWnd->GetSafeHwnd() );
m_btnMyButton.SubclassWindow(pWnd->GetSafeHwnd());
l制按钮非常?span>,除了不能按钮的样式设ؓflat或者两端对齐的文本外其他Q何你惌的样式都可以。当你编译运行改E序时你会看见一个简单的按钮当你的鼠标经q它时会呈现Z黄色?/span>
注意到我们仅仅真正的重蝲了自l的Ҏ(gu)Q以及鼠标移动消息的处理函数Q这意味q该控g仍然是下按式的按钮。给对话框类d一个单ȝ处理函数你会发现它仍然可以被调用?/span>
l尾
子分cdƈ不难-你仅仅需要仔l选择你需要子分的c,而且要意识到你需要处理的消息函数。仔l研I你需要子分的控g?/span>学习(fn)相关消息的处理函数和该类实现的虚成员Ҏ(gu)。一旦你深入一个控件ƈ且掌握了它的内部工作机制Q你会发C切都是那么容易!
1?API函数CreateFile可打开和创建文件、管道、邮槽、通信服务、设备以及控制台Q但是在此时只是介绍用这个函数怎么实现创徏和打开一个文件?br>HANDLE CreateFile(
LPCTSTR lpFileName, // 要打开的文件名
DWORD dwDesiredAccess, // 文g的操作属?br>DWORD dwShareMode, // 文g׃n属?br>LPSECURITY_ATTRIBUTES lpSecurityAttributes,// 文g安全Ҏ(gu)?br>DWORD dwCreationDisposition, //文g操作
DWORD dwFlagsAndAttributes, // 文g属?br>HANDLE hTemplateFile // 如果不ؓӞ则指定一个文件句柄。新文g从q个文g中复制扩展属?
);
文g的操作属性:
如果为零Q表C只允许获取与一个设备有关的信息;
GENERIC_READ 表示允许对设备进行读讉K;
如果?GENERIC_WRITE 表示允许对设备进行写讉KQ可l合使用Q?
文g的共享属性:
零表CZ׃n;
FILE_SHARE_READ ?FILE_SHARE_WRITE 表示允许Ҏ(gu)件进行读/写共享访?/p>
文g的操作有Q?br>CREATE_NEWQ创建文?如文件存在则会出?br>CREATE_ALWAYSQ创建文Ӟ会改写前一个文?br>OPEN_EXISTINGQ文件必dl存在。由讑֤提出要求
OPEN_ALWAYSQ如文g不存在则创徏?br>TRUNCATE_EXISTINGQ将现有文g~短为零长度
文g属性有Q?br>FILE_ATTRIBUTE_ARCHIVEQ标记归档属?br>FILE_ATTRIBUTE_COMPRESSEDQ将文g标记为已压羃Q或者标Cؓ文g在目录中的默认压~方?br>FILE_ATTRIBUTE_NORMALQ默认属?br>FILE_ATTRIBUTE_HIDDENQ隐藏文件或目录
FILE_ATTRIBUTE_READONLYQ文件ؓ只读
FILE_ATTRIBUTE_SYSTEMQ文件ؓpȝ文g
FILE_FLAG_WRITE_THROUGHQ操作系l不得推q对文g的写操作
FILE_FLAG_OVERLAPPEDQ允许对文gq行重叠操作
FILE_FLAG_NO_BUFFERINGQ禁止对文gq行~冲处理。文件只能写入磁盘卷的扇区块
FILE_FLAG_RANDOM_ACCESSQ针寚w问对文g~冲q行优化
FILE_FLAG_SEQUENTIAL_SCANQ针对连l访问对文g~冲q行优化
FILE_FLAG_DELETE_ON_CLOSEQ关闭了上一ơ打开的句柄后Q将文g删除。特别适合临时文g
可以l合的属性有QFILE_FLAG_WRITE_THROUGHQFILE_FLAG_OVERLAPPEDQFILE_FLAG_NO_BUFFERINGQFILE_FLAG_RANDOM_ACCESSQFILE_FLAG_SEQUENTIAL_SCANQFILE_FLAG_DELETE_ON_CLOSEQFILE_FLAG_BACKUP_SEMANTICSQFILE_FLAG_POSIX_SEMANTICSQFILE_FLAG_OPEN_REPARSE_POINTQFILE_FLAG_OPEN_NO_RECALL
如果成功q回一个打开文g得句柄,
如果调用函数之前文g存在Q文件操作属性ؓQCREATE_ALWAYS ?OPEN_ALWAYSQ用GetLastError函数q回的是ERROR_ALREADY_EXISTSQ包括函数操作成功)Q?br>如果之前函数不存在,则返??br>使用p|q回INVALID_HANDLE_VALUEQ?br>要取得更多的信息Q用GetLastError函数?/p>
文g的关闭用Q?br>BOOL CloseHandle(
HANDLE hObject // handle to object to close
);
例子1
在当前目录下面创Z个文Ӟ
HANDLE handle;
DWORD Num;
handle= ::CreateFile("new.tmp",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_FLAG_DELETE_ON_CLOSE,NULL);
if(INVALID_HANDLE_VALUE!= handle )
{
::SetFilePointer(handle,0,0,FILE_BEGIN);
char Buffer[] = "q是个刚创徏的文?;
::WriteFile(handle,Buffer,sizeof(Buffer),&Num,NULL);
ZeroMemory(Buffer,sizeof(Buffer));
::SetFilePointer(handle,0,0,FILE_BEGIN);
::ReadFile(handle,Buffer,sizeof(Buffer),&Num,NULL);
MessageBox(Buffer);
::CloseHandle(handle);
}
可以改变上面的创建文件的属性和操作看下不同效果?/p>
CFile创徏和打开一个文Ӟ
创徏文g和打开文g的方法有很多U,下面单介l下几个构造函敎ͼ
CFile( LPCTSTR lpszFileName, UINT nOpenFlags );throw( CFileException );
CFile( );
BOOL Open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL );
lpszFileName:文g名称Q可以是相对路径Q绝对\径或|络路径
nOpenFlagsQ打开方式有:
CFile::modeCreate 调用构造函数构造一个新文gQ如果文件已存在Q则长度变成0?br>CFile::modeNoTruncate 此gmodeCreatel合使用。如果所创徏的文件已存在则其长度不变?。因而此文g被打开Q或者作Z个新文g或者作Z个已存在的文件。这是很有用的Q例如当打开一个可能存在也可能不存在的文g时?br>CFile::modeRead 打开文g仅供诅R?br>CFile::modeReadWrite 打开文g供读写?br>CFile::modeWrite 打开文g仅供写?br>CFile::modeNoInherit L文g被子q程l承?br>CFile::ShareDenyNone 不禁止其它进E读或写讉KQ打开文g。如果文件已被其它进E以兼容模式打开Q则Createp|?br>CFile::ShareDenyRead 打开文gQ禁止其它进E读此文件。如果文件已被其它进E以兼容模式打开Q或被其它进E读Q则Createp|
CFile::ShareDenyWrite 打开文gQ禁止其它进E写此文件。如果文件已被其它进E以兼容模式打开Q或被其它进E写Q则Createp|?br>CFile::ShareExclusive 以独占模式打开文gQ禁止其它进E对文g的读写。如果文件已l以其它模式打开dQ即使被当前q程Q,则构造失败?br>CFile::ShareCompat 此标志在32位MFC中无效。此标志在用CFile:: Open时映ؓCFile::ShareExclusive?br>CFile::typeText 对回车换行设|特D进E(仅用于派生类Q?br>CFile::typeBinary 讄二进制模式(仅用于派生类Q?/p>
下面l出MSDN中的一个例子:
char* pFileName = "test.dat";
TRY
{
CFile f( pFileName, CFile::modeCreate | CFile::modeWrite );
}
CATCH( CFileException, e )
{
#ifdef _DEBUG
afxDump << "File could not be opened " << e->m_cause << "\n";
#endif
}
END_CATCH
CFile fileTest;
char* pFileName = "test.dat";
TRY
{
fileTest.Open(pFileName, CFile::modeCreate |CFile::modeWrite);
}
CATCH_ALL(e)
{
fileTest.Abort( );
THROW_LAST ( );
}
END_CATCH_ALL
二.文g的读写定位:
Q一Q定位文件中的数据是很重要的Q这军_了写入的数据在文件中的位|?/p>
1、API函数
DWORD SetFilePointer(
HANDLE hFile, //文g的句?br>LONG lDistanceToMove, //字节偏移量r
PLONG lpDistanceToMoveHigh, //指定一个长整数变量Q其中包含了要用的一个高双字偏移Q一般用来操作大型文Ӟ。可设ؓӞ表示只用lDistanceToMove
DWORD dwMoveMethod //文g定位
);
dwMoveMethod文g定位的方式有三种Q?br>FILE_BEGINQ从文g开始处?br>FILE_CURRENTQ从当前位置?br>FILE_ENDQ从文g的末?br>此函数可以用来定位大型文ӞlpDistanceToMoveHigh是高32位,lDistanceToMove是低32位。如果lpDistanceToMoveHigh为NULLӞ函数操作成功Q返回的是当前文件数据的偏移量,如果lpDistanceToMoveHigh不NULLQ则q回数据的偏U量?2位放在lpDistanceToMoveHigh中,函数调用p|q回的是0xffffffff.
BOOL SetEndOfFile(
HANDLE hFile //文g的句?br>);
2、CFilecȝ文g数据定位函数有:
LONG Seek(LONG lOff,UINT nFrom);throw(CFileException);
如果要求的位|合法,则Seekq回从文件开始v的新字节偏移?br>lOffQ指针移动的字节数?br>nFromQ指针移动的模式。可以是CFile::beginQCFile::currentQCFile::end
void SeekToBegin( );
DWORD SeekToEnd( );//q回文g长度Q字节数Q?/p>
下面是一个读取位图文件的信息的例子:
CFile file;
BITMAPINFOHEADER bmpinfo;
try
{
file.Open("D:\\ToolBar.bmp",CFile::modeRead);
file.Seek(sizeof(BITMAPFILEHEADER),CFile::begin);
file.Read(&bmpinfo,sizeof(BITMAPINFOHEADER ));
CString str;
str.Format("位图文g的长?d,?d",bmpinfo.biWidth,bmpinfo.biHeight);
MessageBox(str);
file.Close();
}
catch(CFileException *e)
{
CString str;
str.Format("d数据p|的原因是:%d",e->m_cause);
MessageBox("str");
file.Abort();
e->Delete();
}
Q二Q读取数据:
1、BOOL ReadFile(
HANDLE hFile, //文g的句?br>LPVOID lpBuffer, //用于保存d数据的一个缓冲区
DWORD nNumberOfBytesToRead, //要读入的字符?br>LPDWORD lpNumberOfBytesRead, //从文件中实际d的字W数
LPOVERLAPPED lpOverlapped //如文件打开时指定了FILE_FLAG_OVERLAPPEDQ那么必,用这个参数引用一个特D的l构。该l构定义了一ơ异步读取操作。否则,应将q个参数设ؓNULL
);
2、CFile的成员函数有Q?br>UINT Read (void* lpBuf,UINT nCount); throw(CFileException);// q回值是传输到缓冲区的字节数?/p>
Q三Q写入数据:
1、BOOL WriteFile(
HANDLE hFile, //文g的句?br>LPCVOID lpBuffer, //要写入的一个数据缓冲区
DWORD nNumberOfBytesToWrite, //要写入数据的字节数量。如写入零字节,表示什么都不写入,但会更新文g?#8220;上一ơ修Ҏ(gu)?#8221;?br>LPDWORD lpNumberOfBytesWritten, //实际写入文g的字节数?br>LPOVERLAPPED lpOverlapped // OVERLAPPEDQ倘若在指FILE_FLAG_OVERLAPPED的前提下打开文gQ这个参数就必须引用一个特D的l构。该l构定义了一ơ异步写操作。否则,该参数应|ؓNULL
);
2、void Write(const void* lpBuf,UINT nCount);throw (CFileException);
lpBufQ指向用h供的~冲区,包含写入文件中的数?br>nCountQ从~冲区内传输的字节数。对文本模式的文Ӟ回R换行作ؓ一个字W?br>下面是象一个文件中写入数据的例子:
CFile file;
try
{
file.Open("d:/my.dat",CFile::modeCreate|CFile::modeWrite);
file.SeekToBegin();
char Data[] = "111111111\n1111111111";
file.Write(Data,sizeof(Data));
file.Flush();
file.Close();
}
catch(CFileException *e)
{
CString str;
str.Format("d数据p|的原因是:%d",e->m_cause);
MessageBox("str");
file.Abort();
e->Delete();
}
三.取得和设|文件的创徏旉、最后访问时间、最后写旉
BOOL GetFileTime(
HANDLE hFile, // 文g句柄
LPFILETIME lpCreationTime, // 创徏旉
LPFILETIME lpLastAccessTime, // 最后访问时?br>LPFILETIME lpLastWriteTime // 最后写旉
);
BOOL SetFileTime(
HANDLE hFile,
CONST FILETIME *lpCreationTime,
CONST FILETIME *lpLastAccessTime,
CONST FILETIME *lpLastWriteTime
);
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME;
取得三个参数都是FILETIMEl构Q得到的都是UTC旉Q可以通过API函数FileTimeToLocalFileTimeQ)和FileTimeToSystemTime()他们{换ؓ本地旉和系l时间格式,也可以通过LocalFileTimeToFileTime和SystemTimeToFileTime()转换回来Q通过SetFileTime讄文g的创建时间、最后访问时间、最后写旉。由于用的时候要先打开文gQ而且取得的最后访问时间就是当前时_没有多大意义Q且比较ȝQ下面介lCFilecM的静态方法?br>static BOOL PASCAL GetStatus( LPCTSTR lpszFileName, CFileStatus& rStatus );
static void SetStatus( LPCTSTR lpszFileName, const CFileStatus& status );
throw( CFileException );
q回的是一个CfileStatus对象Q这个结构的具体的成员变量包括:
struct CFileStatus
{
CTime m_ctime; // 文g创徏旉
CTime m_mtime; // 文g最q一ơ修Ҏ(gu)?br>CTime m_atime; // 文g最q一ơ访问时?br>LONG m_size; // 文g大小
BYTE m_attribute; // 文g属?br>BYTE _m_padding; // 没有实际含义Q用来增加一个字?br>TCHAR m_szFullName[_MAX_PATH]; //l对路径
#ifdef _DEBUG
//实现Dump虚拟函数Q输出文件属?br>void Dump(CDumpContext& dc) const;
#endif
};
下面׃D一个例子来实现Q?br>CFileStatus status;
char *path = "D:\\VSS";
if(CFile::GetStatus( path, status ))
{
CString cTime,mTime,aTime;
cTime = status.m_ctime.Format("文g建立旉Q?Yq?m?d?%H?M?SU?);
mTime = status.m_mtime.Format("文g最q修Ҏ(gu)_%Yq?m?d?%H?M?SU?);
aTime = status.m_atime.Format("文g最q访问时_%Yq?m?d?%H?M?SU?);
CString str;
str = cTime + "\n" + mTime +"\n" + aTime ;
MessageBox(str);
}
四.取得和设|文件的属?/p>
DWORD GetFileAttributes(
LPCTSTR lpFileName //文g或文件夹路经
);
BOOL SetFileAttributes(
LPCTSTR lpFileName, // 文g?br>DWORD dwFileAttributes // 要设|的属?br>);
取得的文件属性包括:FILE_ATTRIBUTE_ARCHIVEQFILE_ATTRIBUTE_HIDDENQFILE_ATTRIBUTE_NORMALQFILE_ATTRIBUTE_OFFLINEQFILE_ATTRIBUTE_READONLYQFILE_ATTRIBUTE_SYSTEMQFILE_ATTRIBUTE_TEMPORARY
不能讄的文件属性包括有QFILE_ATTRIBUTE_COMPRESSEDQFILE_ATTRIBUTE_DIRECTORYQFILE_ATTRIBUTE_ENCRYPTEDQFILE_ATTRIBUTE_REPARSE_POINTQFILE_ATTRIBUTE_SPARSE_FILEQFILE_ATTRIBUTE_SYSTEM?/p>
CFileStatus中也定义了一l属性:
enum Attribute {
normal,
readOnly,
hidden,
system,
volume,
directory,
archive
};
可以通过if((status. m_attribute& readOnly) = =FILE_ATTRIBUTE_READONLY)来判断,q里利用另外的API来实现获得文件的详细信息Q?/p>
HANDLE FindFirstFile(
LPCTSTR lpFileName, //文g或文件夹路经r
LPWIN32_FIND_DATA lpFindFileData
);
BOOL FindNextFile(
HANDLE hFindFile,
LPWIN32_FIND_DATA lpFindFileData
);
BOOL FindClose(
HANDLE hFindFile );
取得的是一个WIN32_FIND_DATAl构;
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //文g属?br>FILETIME ftCreationTime; // 文g创徏旉
FILETIME ftLastAccessTime; // 文g最后一ơ访问时?br>FILETIME ftLastWriteTime; // 文g最后一ơ修Ҏ(gu)?br>DWORD nFileSizeHigh; // 文g长度?2?br>DWORD nFileSizeLow; // 文g长度?2?br>DWORD dwReserved0; // pȝ保留
DWORD dwReserved1; // pȝ保留
TCHAR cFileName[ MAX_PATH ]; // 长文件名
TCHAR cAlternateFileName[ 14 ]; // 8.3格式文g?br>} WIN32_FIND_DATA, *PWIN32_FIND_DATA;
也可以利用另外一个函数来取得文g的信?
BOOL GetFileInformationByHandle(
HANDLE hFile, // 文g的句?
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
);
函数填充的是BY_HANDLE_FILE_INFORMATIONl构?
typedef struct _BY_HANDLE_FILE_INFORMATION {
DWORD dwFileAttributes; //文g属?br>FILETIME ftCreationTime; // 文g创徏旉
FILETIME ftLastAccessTime; // 文g最后一ơ访问时?br>FILETIME ftLastWriteTime; // 文g最后一ơ修Ҏ(gu)?br>DWORD dwVolumeSerialNumber; // 文g所在的盘的序列号
DWORD nFileSizeHigh; // 文g长度?2?br>DWORD nFileSizeLow; // 文g长度?2?br>DWORD nNumberOfLinks; //链接的数?br>DWORD nFileIndexHigh;
DWORD nFileIndexLow;
} BY_HANDLE_FILE_INFORMATION;
下面׃D一个例子来实现Q?br>HANDLE handle;
WIN32_FIND_DATA find_data;
handle = :: FindFirstFile("D:\\VSS",&find_data);
FindClose(handle);
find_data.dwFileAttributes = find_data.dwFileAttributes|FILE_ATTRIBUTE_READONLY;
::SetFileAttributes("D:\\VSS",find_data.dwFileAttributes);
在上面的介绍?除了可以讄文g的属性之外,在操作的q程当中也可以取得文件的其他一些信息,可以Ҏ(gu)具体的需要来实现?/p>
五.获取文g?文gcd,文g长度,文g路径
用利用CFile打开一个文件时,可以在利用成员函?br>virtual CString GetFileName( ) const,
virtual CString GetFileTitle( ) const,
virtual CString GetFilePath( ) const,
virtual DWORD GetLength( ) const;throw( CFileException );
来取得相关信?如果一个文件的全\l是: c:\windows\write\myfile.wri,则每个函数取得的? myfile.wri, myfile, c:\windows\write\myfile.wri. GetLength取得文g大小是按字节为单位的.
也可以利用:
virtual void SetLength( DWORD dwNewLen );throw( CFileException );
virtual void SetFilePath( LPCTSTR lpszNewName );
来设|文件的长度和\径?br>在当前的文g下面新徏一个Text.txt文gQ在里面写点东西Q然后运行下面程序:
CFile file("Text.txt",CFile::modeReadWrite);
ULONGLONG length;
CString strFilePath;
length = file.GetLength();
length = length + 1024*10;
file.SetLength(length);
file.SetFilePath("D:\\Text.txt");
strFilePath = file.GetFilePath();
MessageBox(strFilePath);
file.Close();
最后发现文件的路径变了Q但是在D盘下面ƈ没有扑ֈText.txtQ原因是SetFilePath只能指定一个\径给文gQSetFilePathq不能做为移动文件来使用?br>CFileq没有给出取得文件类型的函数Q有了上面基Q这个很Ҏ(gu)实现?br>API函数中也有获得文件\径的操作Q这里只是做单介l,可以参照MSDNN的说明:
GetFileSize可以获得文g的大,
GetFullPathName 函数获取文g的完整\径名Q只有当该文件在当前目录下,l果才正?br>GetModuleFileName函数获取文g的完整\径名Q这些函数有些用到文件句柄的?br>用CFileDialog打开的文?可以使用它的成员变量m_ofn,或者成员函数GetFileName, GetFileTitle, GetFilePath,GetFileExt来取得相关信?
CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL );
各个参数如下Q?br>bOpenFileDialog 为TRUE为打开对话框,为FALSEZ存对话文件对话框
lpszDefExt 指定默认的文件扩展名?
lpszFileName 指定默认的文件名?
dwFlags 指明一些特定风根{?
lpszFilter它指明可供选择的文件类型和相应的扩展名。参数格式如Q?
"Chart Files (*.xlc)|*.xlc|Worksheet Files (*.xls)|*.xls|Data Files (*.xlc;*.xls)|*.xlc; *.xls|All Files (*.*)|*.*||";文gcd说明和扩展名间用 | 分隔Q同U类型文件的扩展名间可以?; 分割Q每U文件类型间?| 分隔Q末 || 指明?
pParentWnd 为父H口指针
CString FileFilter = "所有文?*.*)|*.*||";
CFileDialog FileDialog(true,NULL,NULL,OFN_HIDEREADONLY,FileFilter,NULL);
FileDialog.DoModal();
MessageBox(FileDialog.GetFileName());
6Q小l:
在实际中q有很多其他操作文g的方法,上面介绍的只是简单的几种Q希望通过上面的简单介l,在加上具体实践,能够扑ֈ解决问题的最好办法!
以下是程序结束的q程Q?br>1、用者按[File/Close],pȝ发出WM_CLOSE消息
2、Frame把这条消息直接发l预处理E序
3、预处理E序发出WM_DESTROY消息
4、预处理E序收到WM_DESTROY后执行PostQuitMessageQ发出WM_QUIT.
5、GetMessage收到q条消息后就推出消息循环Q程序结束?/p>
InvalidateRect函数中的参数TRUE表示pȝ会在你画之前用背景色所选区域覆盖一ơ,默认背景色ؓ白色Q可以通过讄BRUSH来改变背景色?/p>
Invalidate()之后Q?br>...OnPaint()->OnPrepareDC()->OnDraw()
所以只是刷新在OnPaint()和OnDraw()函数中的l图语句。其它地Ҏ(gu)有媄响?/p>
Invalidate标记一个需要重l的无效区域Qƈ不意味着调用该函数后q刻进行重l。类gPostMessage(WM_PAINT)Q需要处理到WM_PAINT消息时才真正重绘。以为?zhn)Invalidate之后q有其他的语句正在执行,E序没有Zd理WM_PAINT消息Q但当函数执行完毕后Q消息处理才得以q行?/p>
Invalidate只是放一个WM_PAINT消息在队列里Q不做别的,所以只有当当前函数q回后,q入消息循环Q取出WM_PAINTQ才执行PAINTQ所以不Invalidate攑֓里,都是最后的?/p>
InvalidateRect(hWnd,&rect,TRUE);向hWndH体发出WM_PAINT的消息,强制客户区域重绘Ӟ
rect是你指定要刷新的区域Q此区域外的客户区域不被重绘Q这样防止客户区域的一个局部的改动Q而导致整个客户区域重l而导致闪烁,如果最后的参数为TRUEQ则q向H体发送WM_ERASEBKGND消息Q背景重绘Q当然在客户区域重绘之前?br>UpdateWindow只向H体发送WM_PAINT消息Q在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可l制的客户区域,如果没有Q则不发送WM_PAINT
如果希望立即h无效区域Q可以在调用InvalidateRect之后调用UpdateWindowQ如果客户区的Q一部分无效Q则UpdateWindow导致Windows用WM_PAINT消息调用H口q程Q如果整个客户区有效Q则不调用窗口过E)?font color=#ff0000>q一WM_PAINT消息不进入消息队列,直接由WINDOWS调用H口q程。窗口过E完成刷C后立刻退出,W(xu)INDOWS控制返回给E序中UpdateWindow调用之后的语句?/font>(windowsE序设计W??P98)
UpdateData()Z说下Q这个函C是刷新界面用的?br>UpdateData();参数为FALSEӞ界面上控gl定的变量的数据导到控g内,参数为TRUEӞ导入方向则相?/p>
pȝ会在多个不同的时机发送WM_PAINT消息Q当W一ơ创Z个窗口时Q当改变H口的大时Q当把窗口从另一个窗口背后移出时Q当最大化或最化H口Ӟ{等Q这些动作都是由pȝ理的,应用只是被动地接收该消息Q在消息处理函数中进行绘制操作;大多数的时候应用也需要能够主动引发窗口中的绘制操作,比如当窗口显C的数据改变的时候,q一般是通过InvalidateRect?InvalidateRgn函数来完成的。InvalidateRect和InvalidateRgn把指定的区域加到H口的Update Region中,当应用的消息队列没有其他消息Ӟ如果H口的Update Region不ؓI时Q系l就会自动生WM_PAINT消息? pȝZ么不在调用Invalidate时发送WM_PAINT消息呢?又ؓ什么非要等应用消息队列为空时才发送WM_PAINT消息呢?q是因ؓpȝ把在H口中的l制操作当作一U低优先U的操作Q于是尽可能地推后做Q这h利于提高l制的效率:在两个WM_PAINT消息之间多个Invalidate调用使之失效的区域就会被累加hQ然后在一个WM_PAINT消息中一ơ得到更斎ͼ不仅能避免多ơ重复地更新同一区域Q也优化了应用的更新操作。像q种通过InvalidateRect和InvalidateRgn来ɽH口区域无效Q依赖于pȝ在合适的时机发送WM_PAINT消息的机 制实际上是一U异步工作方式,也就是说Q在无效化窗口区域和发送WM_PAINT消息之间是有延迟的;有时候这Ugqƈ不是我们希望的,q时我们当然可以在无效化H口区域后利用SendMessage 发送一条WM_PAINT消息来强制立即重画,但不如用Windows GDI为我们提供的更方便和强大的函敎ͼUpdateWindow和RedrawWindow。UpdateWindow会检查窗口的Update RegionQ当其不为空时才发送WM_PAINT消息QRedrawWindow则给我们更多的控Ӟ是否重画非客户区和背景,是否L发送WM_PAINT消息而不Update Region是否为空{?/td> |
一Q消息映机?/p>
1Q消息响应函敎ͼQ例Q在CDrawViewcd应鼠标左键按下消息)
1Q在头文?DrawView.h)中声明消息响应函数原型?br>//{{AFX_MSG(CDrawView) //注释?br>afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG //注释?br>说明Q?br>在注释宏之间的声明在VC中灰色显C。afx_msg宏表C声明的是一个消息响应函数?br> 2Q在源文ӞDrawView.cpp)中进行消息映?br>BEGIN_MESSAGE_MAP(CDrawView, CView)
//{{AFX_MSG_MAP(CDrawView)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
说明Q?br>在宏BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间q行消息映射?br>宏ON_WM_LBUTTONDOWN()把消息WM_LBUTTONDOWN与它的响应函数OnLButtonDownQ)相关联。这样一旦有消息的生,׃自动调用相关联的消息响应函数d理?br>宏ON_WM_LBUTTONDOWN()定义如下Q?br>#define ON_WM_LBUTTONDOWN() \
{ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \
(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown },
3Q源文g中进行消息响应函数处理。(DrawView.cpp中自动生成OnLButtonDown函数轮廓Q如下)
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CView::OnLButtonDown(nFlags, point);
}
说明Q?br>可见当增加一个消息响应处理,在以上三处进行了修改。可在消息响应函数里d消息处理代码完成Ҏ(gu)息的响应、处理?/p>
2Q消息响应的方式Q?br>1Q在基类中针Ҏ(gu)U消息做一个虚函数Q当子类Ҏ(gu)息响应时候,只要在子cM重写q个虚函数即可。缺点:MFCcL生层ơ很多,如果在基cd每个消息q行虚函数处理,那么从基cL生的每个子类都将背负一个庞大的虚表Q这h费内存,故MFC没有采取q中方式而采取消息映方式?br>2Q消息映方式:MFC在后台维护了一个句柄和C++对象指针对照表,当收C个消息后Q通过消息l构里资源句柄(查对照表Q就可找C它对应的一个C++对象指针Q然后把q个指针传给基类Q基cd用这个指针调用WindowProc()函数Ҏ(gu)息进行处理,W(xu)indowProc()函数中调用OnWndMsg()函数Q真正的消息路由及处理是由O(jin)nWndMsg()函数完成的。由于WindowProc()和OnWndMsg()都是虚函敎ͼ而且是用zcd象指针调用的Q由多态性知最ȝ调用子类的。在OnWndMsg()函数处理的时候,Ҏ(gu)消息U类L找消息映,判断所发的消息有没有响应函敎ͼ具体方式是到相关的头文g和源文g中寻找消息响应函数声明(从注释宏//{{AFX_MSG(CDrawView)...//}}AFX_MSG之间LQ,消息映射Q从宏BEGIN_MESSAGE_MAP(...)....END_MESSAGE_MAP()之间LQ,最l找到对应的消息处理函数。当Ӟ如果子类中没有对消息q行处理Q则消息交由基类处理?br>说明Q?br>virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);
二,有关l图
1Q用SDK获取DC句柄Q?br>HDC hdc;
hdc=::GetDc(m_hWnd);//获取DC句柄
MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);
LineTo(hdc,point.x,point.y);
::ReleaseDC(m_hWnd,hdc);//释放DC
2Q利用CDCcL针和CWincL员函数获取DC?br>CDC *pDC=GetDC();
pDC->MoveTo(m_ptOrigin);
pDC->LineTo(point);
ReleaseDC(pDC);
3Q利用CClientDC对象。(CClientDCcMCDCcL生来的)
CClientDC dc(this);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
说明Q?br>The CClientDC class is derived from CDC and takes care of calling the Windows functions GetDC at construction time and ReleaseDC at destruction time. This means that the device context associated with a CClientDC object is the client area of a window.
4Q利用CWindowDC对象。(CWindowDCcMCDCcL生来的)
CWindowDC dc(this);//
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
说明Q?br>The CWindowDC class is derived from CDC. It calls the Windows functionsGetWindowDC at construction time andReleaseDC at destruction time. This means that a CWindowDC object accesses the entire screen area of a CWnd (both client and nonclient areas).
5QGetParent()得到父窗口指针;GetDesktopWindow()得到屏幕H口指针?/p>
6Q利用画W改变线条颜色和cdQ?br>CPen pen(PS_DOT,1,RGB(0,255,0));//构造画W对?br>CClientDC dc(this);CPen *pOldPen=dc.SelectObject(&pen);//画W选入DC
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
dc.SelectObject(pOldPen);//恢复先前的画W?/p>
7Q用画P通常利用dd充矩形区域)Q?br>使用单色d
CBrush brush(RGB(255,0,0));//构造画刷对?br>CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的dd充矩形区?/p>
使用位图d
CBitmap bitmap;//构造位囑֯象(使用前需要初试化Q?br>bitmap.LoadBitmap(IDB_BITMAP1);//初试化位囑֯?br>CBrush brush(&bitmap);//构造位囄?br>CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的位图dd充矩形区?/p>
使用透明d
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//获取透明d对象指针
CClientDC dc(this);
CBrush *pOldBrush=dc.SelectObject(pBrush);//透明d选入DC
dc.Rectangle(CRect(m_ptOrigin,point));
dc.SelectObject(pOldBrush);//释放透明d
说明Q?br>The GetStockObject function retrieves a handle to one of the predefined stock pens, brushes, fonts, or palettes.
HGDIOBJ GetStockObject(
int fnObject // type of stock object
);
Returns a pointer to a CBrush object when given a handle to a Windows HBRUSH object.
static CBrush* PASCAL FromHandle( HBRUSH hBrush );//FromHandle是一个静态方法,故可用CBrush::FromHandle()形式调用?br>注意点:
1Q静态方法不属于某一个具体对象,而属于类本nQ在cd载的时候就已经为类静态方法分配了代码去,故可用CBrush::FromHandle()形式调用?br>2Q静态方法中Q不能引用非静态的数据成员和方法?br>3Q静态数据成员需要在cd单独做初始化QŞ式如Q?变量cd cd::变量?初始?
8QCDC::SetROP2Ҏ(gu)Q?br>int SetROP2( int nDrawMode );
Sets the current drawing mode.
2Q对于全局对象或全局变量来说Q在E序q行即WINMAIN函数加蝲的时候,已经为全局对象或全局变量分配了内存和赋初倹{?br>所以:CTEApp theApp;->CTEApp ::CTEApp(){}->_tWinMain(){}
说明Q每一个MFCE序Q有且只有一个从WinAppcL生的c(应用E序c)Q也只有一个从应用E序cL事例化的对象Q表C应用程序本w。在WIN32E序当中Q表C应用程序是通过WINMAIN入口函数来表C的Q通过一个应用程序的一个事例号q一个标识来表示的)。在ZMFC应用E序中,是通过产生一个应用程序对象,用它来唯一的表CZ应用E序?/p>
3Q通过构造应用程序对象过E中调用基类CWinApp的构造函敎ͼ在CWinApp的构造函C对程序包括运行时一些初始化工作完成了?br>CWinApp构造函敎ͼMFC|SRC|APPCORE.CPP
CWinApp::CWinApp(LPCTSTR lpszAppName){...}//带参敎ͼ而CTEApp构造函数没有显式向父类传参Q难道CWinApp()有默认参敎ͼ见下Q?br>Q在CWinAppcd义中Q?nbsp;CWinApp(LPCTSTR lpszAppName = NULL); Q?br>注意QCWinApp()函数中:
pThreadState->m_pCurrentWinThread = this;
pModuleState->m_pCurrentWinApp = this
Qthis指向的是zcCTEApp对象Q即theAppQ?br>调试QCWinApp::CWinApp();->CTEApp theApp;Q?>CTEApp ::CTEApp()Q?>CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}
4,_tWinMain函数中通过调用AfxWinMain()函数来完成它要完成的功能。(Afx*前缀代表q是应用E序框架函数Q是一些全局函数Q应用程序框架是一套辅助生成应用程序的框架模型Q把一些类做一些有机的集成Q我们可Ҏ(gu)q些cd数来设计自己的应用程序)?br>AfxWinMain()函数路径QMFC|SRC|WINMAIN.CPPQ?br>在AfxWinMain()函数中:
CWinApp* pApp = AfxGetApp();
说明QpApp存储的是指向WinAppzcd象(theAppQ的指针?br>//_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
// { return afxCurrentWinApp; }
调用pThread->InitInstance()
说明QpThread也指向theApp,׃基类中virtual BOOL InitApplication()定义函数Q所以调用pThread->InitInstance()时候,调用的是zcCTEApp的InitInstance()函数?/p>
nReturnCode = pThread->Run();
说明QpThread->Run()完成了消息@环?/p>
5,注册H口c:AfxEndDeferRegisterClass();
AfxEndDeferRegisterClass()函数所在文ӞMFC|SRC|APPCORE.CPP
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...}
说明Q设计窗口类Q在MFC中事先设计好了几U缺省的H口c,Ҏ(gu)不同的应用程序的选择Q调用AfxEndDeferRegisterClass()函数注册所选择的窗口类?br>调试QCWinApp::CWinApp();->CTEApp theApp;Q?>CTEApp ::CTEApp()Q?>CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}//q入E序
->AfxWinMain();->pApp->InitApplication()Q?>pThread->InitInstance()//父类InitInstance虚函?->CTEApp::InitInstance()//子类实现函数;->AfxEndDeferRegisterClass(LONG fToRegister)//注册所选择的窗口类Q出于文档管理,注册提前Q正常的应在PreCreateWindow中进行注册)//之后q入创徏H口阶段Q以下再不做调试Q?/p>
6QPreCreateWindow()Q?/主要是注册窗口类
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
return TRUE;
}
说明Q?br>CFrameWnd::PreCreateWindow()函数所在文ӞMFC|SRC|WINFRM.CPP
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
//判断AFX_WNDFRAMEORVIEW_REG型号H口cL否注册,如果没有注册则注?br> cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
//把注册后的窗口类名赋lcs.lpszClass
}
if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4)
cs.style |= FWS_PREFIXTITLE;
if (afxData.bWin4)
cs.dwExStyle |= WS_EX_CLIENTEDGE;
return TRUE;
}
其中Q?br>virtual BOOL PreCreateWindow(CREATESTRUCT& cs);//PreCreateWindow()是个虚函?如果子类有则调用子类的?br>#define VERIFY(f) ASSERT(f)
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
define AFX_WNDFRAMEORVIEW_REG 0x00008
const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//WINCORE.CPP文g中,定义为全局数组?br>//#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView")
7Q创建窗口:
Create()函数路径QMFC|SRC|WINFRM.CPP:
CFrameWnd::Create(...){
...
CreateEx(...);//从父cȝ承来的,调用CWnd::CreateEx().
...
}
CWnd::CreateEx()函数路径QMFC|SRC|WINCORE.CPP
BOOL CWnd::CreateEx(...){
...
if (!PreCreateWindow(cs))//虚函敎ͼ如果子类有调用子cȝ?br> {
PostNcDestroy();
return FALSE;
}
...
HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
...
}
说明QCreateWindowEx()函数与CREATESTRUCTl构体参数的对应关系Q我们在创建窗口之前通过可PreCreateWindow(cs)修改csl构体成员来修改所要的H口外观。PreCreateWindow(cs))//是虚函数Q如果子cL调用子类的?br>HWND CreateWindowEx(
DWORD dwExStyle,
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
typedef struct tagCREATESTRUCT { // cs
LPVOID lpCreateParams;
HINSTANCE hInstance;
HMENU hMenu;
HWND hwndParent;
int cy;
int cx;
int y;
int x;
LONG style;
LPCTSTR lpszName;
LPCTSTR lpszClass;
DWORD dwExStyle;
} CREATESTRUCT;
8Q显C和更新H口Q?br>CTEAppc,TEApp.cpp?br>m_pMainWnd->ShowWindow(SW_SHOW);//昄H口Qm_pMainWnd指向框架H口
m_pMainWnd->UpdateWindow();//更新H口
说明Q?br>class CTEApp : public CWinApp{...}
class CWinApp : public CWinThread{...}
class CWinThread : public CCmdTarget
{
...
public:
CWnd* m_pMainWnd;
...
...
}
9Q消息@环:
int AFXAPI AfxWinMain()
{ ...
// Perform specific initializations
if (!pThread->InitInstance()){...}
//完成H口初始化工作,完成H口的注册,完成H口的创建,昄和更新?br> nReturnCode = pThread->Run();
//l承基类Run()Ҏ(gu)Q调用CWinThread::Run()来完成消息@?br> ...
}
////////////////////////////////////////////////////////////////
CWinThread::Run()Ҏ(gu)路径QMFC|SRC|THRDCORE.CPP
int CWinThread::Run()
{ ...
// phase2: pump messages while available
do//消息循环
{
// pump message, but quit on WM_QUIT
if (!PumpMessage())//取消息ƈ处理
return ExitInstance();
...
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
...
}
说明Q?br>BOOL PeekMessage(,,,,)函数说明
The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure.
If a message is available, the return value is nonzero.
If no messages are available, the return value is zero.
/////////////////////////////////////////////////////////////
BOOL CWinThread::PumpMessage()
{
...
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))//取消?br> {...}
...
// process this message
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur);//q行消息Q如键盘消息Q{?br> ::DispatchMessage(&m_msgCur);//分派消息到窗口的回调函数处理Q实际上分派的消息经q消息映,交由消息响应函数q行处理。)
}
return TRUE;
}
9Q文档与视结构:
可以认ؓViewcȝ口是CMainFramcȝ口的子窗口?br>DOCumentcL文档cR?br>DOC-VIEWl构数据本w与它的昄分离开?br>文档c:数据的存储,加蝲
视类Q数据的昄Q修?/p>
10Q文档类Q视c,框架cȝ有机l合Q?br>在CTEAppcCTEApp::InitInstance()函数中通过文档模板文档类Q视c,框架cȝ有机l织一赗?br>...
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTEDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CTEView));
AddDocTemplate(pDocTemplate);//增加到模?/p>
3Q消息队列:
每个应用E序OS都ؓ它徏立一个消息队列,消息队列是个先进先出的缓冲区Q其中每个元素都是一个消息,OS生成的每个消息按先后顺序放q消息队列中Q应用程序L取走当前消息队列中的W一条消息,应用E序取走消息后便知道用户的操作和E序的状态,然后对其处理x息响应,消息响应通过~码实现?/p>
4Q用VC~程除了良好的C基础外还需要掌握两斚wQ?br>一Q消息本w。不同消息所代表的用h作和应用E序的状态?br>二,对于某个特定的消息来_要让OS执行某个特定的功能去响应消息?/p>
5QW(xu)indowE序入口Q?br>int WINAPI WinMain(
HINSTANCE hInstance, // 当前事例句柄?br> HINSTANCE hPrevInstance, // 先前事例句柄?br> LPSTR lpCmdLine, // 命o行指?br> int nCmdShow // Q窗口)昄的状?br>);
说明QWinMain函数是WindowsE序入口点函敎ͼ由O(jin)S调用Q当OS启动应用E序的时候,winmain函数的参数由O(jin)S传递的?/p>
6Q创Z个完整的H口需要经q下面四个操作步骤:
一Q设计一个窗口类Q如QWNDCLASS wndcls;
二,注册H口c; 如:RegisterClass(&wndcls);
三,创徏H口Q?nbsp; 如:CreateWindow(),CreateWindowEX();
四,昄及更新窗口。如QShowWindow(),UpdateWindow();
说明Q创建窗口的时候一定要Z已经注册的窗口类.
7QW(xu)indows提供的窗口类Q?br>typedef struct _WNDCLASS {
UINT style; //H口的类?br> WNDPROC lpfnWndProc; //H口q程函数指针Q回调函敎ͼ
int cbClsExtra; //H口c附加字节,cȝ口所׃n。通常0?br> int cbWndExtra; //H口附加字节。通常设ؓ0?br> HANDLE hInstance; //当前应用E序事例句柄?br> HICON hIcon; //图标句柄 LoadIcon();
HCURSOR hCursor; //光标句柄 LoadCursor();
HBRUSH hbrBackground; //d句柄 (HBRUSH)GetStockObject();
LPCTSTR lpszMenuName; //菜单名字
LPCTSTR lpszClassName; //cȝ名字
} WNDCLASS;
8,H口cL册:
ATOM RegisterClass(
CONST WNDCLASS *lpWndClass // address of structure with class
// data
);
9Q创建窗口:
HWND CreateWindow(
LPCTSTR lpClassName, // pointer to registered class name
LPCTSTR lpWindowName, // pointer to window name
DWORD dwStyle, // window style
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // handle to menu or child-window identifier
HANDLE hInstance, // handle to application instance
LPVOID lpParam // pointer to window-creation data
);
10,昄和更新窗口窗口:
BOOL ShowWindow(
HWND hWnd, // handle to window
int nCmdShow // show state of window
);
BOOL UpdateWindow(
HWND hWnd // handle of window
);
11,消息循环Q?br>MSG msg;
while(GetMessage(&msg,...)) //从消息队列中取出一条消?br>{
TranslateMessage(&msg); //q行消息Q如键盘消息Q{?br> DispatchMessage(&msg); //分派消息到窗口的回调函数处理,QOS调用H口回调函数q行处理Q?br>}
其中Q?br>//**The GetMessage function retrieves a message from the calling thread's message queue and places it in the specified structure.
//**If the function retrieves a message other than WM_QUIT, the return value is nonzero.If the function retrieves the WM_QUIT message, the return value is zero. If there is an error, the return value is -1.
BOOL GetMessage(
LPMSG lpMsg, // address of structure with message
HWND hWnd, // handle of window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax // last message
);
//The TranslateMessage function translates virtual-key messages into character messages. The character messages are posted to the calling thread's message queue, to be read the next time the thread calls the GetMessage or PeekMessage function.
BOOL TranslateMessage(
CONST MSG *lpMsg // address of structure with message
);
//The DispatchMessage function dispatches a message to a window procedure.
LONG DispatchMessage(
CONST MSG *lpmsg // pointer to structure with message
);
12Q窗口过E函敎ͼ回调函数Q原型:
The WindowProc function is an application-defined function that processes messages sent to a window. The WNDPROC type defines a pointer to this callback function. WindowProc is a placeholderQ占位符Q?for the application-defined function name.
LRESULT CALLBACK WindowProc( //q里W(xu)indowProc是个代号名字?br> HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
说明Q两U函数调用约定(__stdcall ?__cdeclQ:
#define CALLBACK __stdcall
//__stdcall 标准调用预定Q是PASCAL 调用U定Q象DELPHI使用的就是标准调用约?br>#define WINAPIV __cdecl
// __cdecl 是C 语言形式的调用约定?/p>
主要区别Q函数参C递顺??对堆栈的清除上?br>问题Q除了那些可变参数的函数调用外,其余的一般都是__stdcallU定。但 C/C++~译默然的是__cdeclU定。所以如果在VC{环境中调用__stdcallU定的函敎ͼ必须要在函数声明的时加上 __stdcall 修饰W,以便对这个函数的调用是用__stdcallU定Q如使用DELPHI~写的DLL时候)?br>QVC中可通过q途径修改Qproject|settings..|c/c++|...)
在窗口过E函C通过一lswitch语句来对消息q行处理Q?br>如:
LRESULT CALLBACK WindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch(uMsg)
{
case WM_PAINT:
...
break;
case ...
break;
case WM_CLOSE:
//DestroyWindow(hwnd);
//销毁窗口,q发送WM_DESTROY消息?br> break;
case WM_DESTROY:
//PostQuitMessage(0);
//发送WM_QUIT消息到消息队列中Q请求终止?br> //GetMessage()取到WM_QUIT消息后,q回0Q退出消息@ // 环,从而终止应用程序?br> break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
//用缺省的H口q程处理我们不感兴趣的消息(其它消息Q?br> //q是必须的?br> }//switch
return 0;
}//WindowProc
13QDestroyWindow()函数和PostQuitMessage()函数原型Q?br>//**The DestroyWindow function destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages?/p>
BOOL DestroyWindow(
HWND hWnd // handle to window to destroy
);
//**The PostQuitMessage function indicates to the system that a thread has made a request to terminate (quit). It is typically used in response to a WM_DESTROY message.
//**The PostQuitMessage function posts a WM_QUIT message to the thread's message queue and returns immediately; the function simply indicatesQ预C,通知Q?to the system that the thread is requesting to quit at some time in the future.
When the thread retrieves the WM_QUIT message from its message queue, it should exit its message loop and return control to the system.
VOID PostQuitMessage(
int nExitCode // exit code
);
14Q关于DC句柄获取Q?br>a)使用BeginPaint(),EndPaint()寏V注意只能在响应WM_PAINT消息时用?br>b)使用GetDc(),ReleaseDC()寏V注意他们不能在响应WM_PAINT中用?/p>