vc界面编程经典实例

所需积分/C币:1 2013-03-30 15:36:57 702KB PDF
4
收藏 收藏
举报

这篇文章包含一个 demo project,一个继承于 CListCtrl 的类和一个快速查看这个类功能的 release。我 不想让这个类十全十美,但对于我当前的项目来讲,它的功能已经足够了。当然了,它还有更进一步完善 的地方,欢迎指正! 左图是程序演示的示例图片。 类 CDragDropListCtrl 具有以下的功能和特性: 1、支持单选和复选的任意托拽 2、所选择的托拽目标跟随这鼠标突现式的移动 3、当把目标托出上下边界的时候,List Control 会自动滚动 4、以 LVS_EX_FULLROWSELECT 风格的开关方式运行、 5、保存所托拽目标的状态 6、所有的代码都封装在
在典型多文档界面程序屮,父窗口就是主框架窗口,最后一个子窗口就是覆盖在主窗口客户区,背景为黑 灰色,拥有包含文档的子框架窗口的那个窗口,这是个预定义∫窗口类的窗口,它的窗口类名是 MDIClient”'。如果用了 CSp litterWnd生成分隔条的话,最后一个子窗口就是拥有分隔条的那个窗口。其它 窗口就是工具栏窗口,状态条窗口以及可能有的别的控件窗口 这个函数和消息是:函数CWhd: Reposition bars0以及消息 WM SIZEPARENT。这个消息是mfc自定义的, 不是 windows自有的。 先简单说明一下这个函数和消息。 。函数CWnd: Reposition barso 这个函数不是虚函数,所以就无法在派生类里通过覆盖来编制白己的版本了,只能搞懂它的功能,以便能 灵活使用。 简单而言,这个函数的功能是将可用的客户区区域信息放到消息 WM SIZEPARENT的消息参数旦,然后 枚举本窗口的所有子窗口,给每个子窗口(除掉一个特定的子窗口,相当于上文提到的最后一个子窗口)都 发送这个消息,每个响应这个消息的子窗口都会把可用客户区切去一块。最后把那个特定的子窗口的尺寸 和位置调整到刚好放在最后剩下的可用区域里。 2。消息 WM SIZEPARENT 每个欲参与分配客户区的子窗口都要响应这个消息,除非这个子窗口是那个特定的子窗口。 响应这个消息的子窗口至少要做两件事:1,将可用的父窗口客户区切去自己所占据的一块。2,根据消息 参数的指示,将自己的人小和位置调整到刚好寳纳到自己所占据的区域里或不做调整 下面详细介绍一下函数 CWnd: Reposition Bars(和消息 WM SIZEPARENT Io EK1* CWnd: Rep ositionBars( void Reposition Bars( UINT nID First, UINT nIDLast, UINT nIDLeft Over, UINT nFlag=CWnd: reposDefault, LPRECT lp Rect Param= NULL, LPCRECT lp Rect Client= NULL, bool bStretch TRUE); 参数比较多,但还是比较好懂的。 (1) nIdfirst和 mIDEast 参与分配父窗口客户区的子窗口的id范围。 舒个 WM CHILD风格的窗口都有个id,这是在窗口创建过程中指定的。函数CWnd: Create0的第六个参数 就是这个id。api函数 Create window和 Create window ex里的那个 HMENU类型的参数,当窗口的风格里 有WM(IL时,它不是指的菜单句柄,而是该窗凵的id。 nIdfirst和 mIDEast参数指明了:如果一个子窗口的id值人于等于 nIDFirst并且小于等于 mIDeast,在这个 函数屮才会给这个子窗口发送 WM SIZEPARENT消息,这个子窗口才能参与父窗口客户区的分配。 (2) nIDLeftover 前面说过,有一个特定的子窗口,它不响应 WM SIZEPARENT消息。只有当共它的子窗口都分配完了, 它才来捡取父窗口客户区里剩下的那块。 nIDLeftover正是这个子窗口的id。它也必须大于等于 nId First 并且小于等于 mIDEast (3) lp RectClient 这是一个指向RECT结构数据的指针。这个RECT结构里存放的正是父窗口客户区的初始可用区域。随着 在该函数里依次给各个子窗口发送 WM SIZEPARENT消息,每个响应这个消息的子窗口都会切去自己所 占据的部分。最后剩下的部分,就是id为 nIDLeftover的子窗口将要占据的区域了。这个参数可以为NUL, 这时初始的可用区域就是整个父窗口客户区。 (4) nFlag FH lp RectParam 这两个参数放在一起讲比较好。 flag是该函数的功能标志,它可以有三个值; reposDefault, repos Query和 当 fLag等于 reposDefault时, Reposition Bars函数的功能是这样的:依次给d介于 nID First和 iDlest之 间并且不等于 lEFtover的子窗口发送 WM SIZEPARENT消息,每个响应这个消息的子窗口从 Ip rect client所指的结构里切去自己所占据的部分,并且将自己的人小和位置调整到自己所占据的区域的人 小,最后 RepositionBars函数还将id为 nIdLeftover的子窗凵的大小和位置谓整到被其他子窗凵切剩的可 用区域內,使这个子窗口正好完全覆盖最后的可用区域。这种情況下 lp Rect Param不用,可以为NULL。 当 fLag等于 reposQuery时, Repositionbars函数的功能是这样的:依次给i介于 nID First和 mIdeast之间 并且不等于 nIDLeftover的子窗口发送 WM SIZEPARENT消息,每个响应这个消息的子窗口从 lp rect Client 所指的结构里切大自己所占据的部分,但是他们并不调整自己的大小和位置,最后 Repositionbars函数并 不调整将id为 nIDLeftover的子窗口的大小和位置,而是根据 sTretch的值来做动作:如果 b Stretch为TRUE 那么 Reposition Bars函数把最后剩下的可用区域考贝到 lp Rect Param指向的RECT结构里;如果 b stretch为 AS,那么 Repositionbars函数把所有其他子窗凵用掉的可用区域的高和宽(要所有的子窗山都紧排在 一起,形成一个大的矩形,这个值才有意义)拷贝到 lp RectParan指向的RECT结构的 bottom和 right成员 里,其top和ltt成员被置零。使用这个nlg值来调用 Reposition bars的目的不是要重排子窗口,而是要 看看,假如重排」窗口的话,这些子窗口将占去多大一块,最后刺下的可用区域在什么位置等等信息。 当 fLag等于 reposExtra时,该函数的功能和nlag等于 repos Default时差不多,有点小小的区别。此时需 要用到 Ip rect Param。前面说过,当 fLag等于 reposdefault时, Reposition bars函数将在最后把id为 nIDLε fover的子窗口的大小和位置调整到被其他子窗口切剩的可用区域内,使这个子窗口正好完全覆盖最 后的可用区域。而当 fLag等丁 reposExtra时, Reposition Bars在调整d为 nIDLeftover的子窗口的大小和 位置前,还要用 lprectParam来对最后剩下的可用区域做修正。假设 lp rect撸向的是最后的可用区域,那 么这个修正是这样进行的 Rect pare lprect->left+-lpRectParam->left Ip Rect->right--lpRect Param->right: lp Rect->bottom--lp RectParam->bottom 通过这样的修正,可以使最后剩下的可用区域不被i为 nIDLeftover的子窗口占满,而是空出一些地方来 留作他用。 (5) sTretch 这个参数上面已经提到一点它的作用。它主要是提供给各个响应 WM SIZEPARENT消息的子窗口用的, 子窗口例如工具栏,状态条等在决定自己将从父窗口客户区的可用空间里划走多少时,这个参数也是个判 断的依据。详细可以参阅工具栏和状态条响应 WM SIZEPARENT的函数 OnSizeParentO。 2。消息 WM SIZEPARENT 这是个mf自定义的消息。在msd里的TN024这篇技术文章里有关于这个消息的说明。 该消息的两个参数中 wParam不用, IParam是指向一个 AFX SIZEPARENTPARAMS结构变量的指针,这 个结构变量是在 Reposition Bars函数里定义的 AFX SIZEPARENTPARAMS lay out AFX SIZEPARENTPARAMS结构定义如下 struct AFX SIZEPARENTPARAMS HDWP hDWP RECT rect SIZE SIzeTotal bool sTretch 这个结构变量的成员是在 Reposition bars函数里填写的:它的 sTretch成员就是 Reposition bars的参数 sTretch,它的 sizetotal成员的两个成员cx和cy都被设置为零,它的rect成员就是从 Rep osition Bars的参 数 lp rectclient里拷贝过来的,就是父窗口容户区的初始可用区域嘛。每个响应这个消息的子窗山都必须修 改rect成员的值,以便切去自己所占据的部分 成员hDWP是什么?这得知道三个api函数: Bebin Defer Window pos(, Defer Window pos0和 EndDefer Window pos(,这个api函数是用来成批设置窗凵的位置和尺寸的, Bebin Defer window Poso先通 知 windows分配一个将用来存贮窗口的位置和尺寸信息的结杓,它不是返回这个结构的指针,而是返回代 表这个结构的句柄,句柄的类型是HDWP。然后每个需要重新设置位置和尺寸的窗口都要调用 Defer Window pos(函数(该函数需要那个HDWP类型的句柄为参数),以便往那个结构里填写各自的窗口位 置和大小信息。最后,在某个合适的时候调用 End Defer window Poso, windows就会根据那个结杓里的信息 把有关的窗凵的位置和大小一次性设置好。比起针对每个窗凵分别用 Set window poso等函数逐个设置来说, 这种方法速度快。 好了,在 Reposition bars函数里正是调用了 Bebin defer window pos(O,获得一个HDWP类型的句柄,这个句 柄就被填写到了上面那个结构变量 lay out的成员hDWP里。然后 Reposition Bars函数给每个符合条件的子 窗口发送 WM SIZEPARENT消息。在每个响应 WM SIZEPARENT消息的子窗口旦,要调用 Defer Window Pos(来设置位置和尺寸信息。当所有的子窗口鄱响应完毕 WM SIZEPARENT消息后, Reposition bars函数再调用 ndDefer Window Pos(函数,这一来,除了那个id为 nId leftover的子窗口外, 所有的子窗口都一次性排好了位置了 至于该结构的 sizetotal成员的意义,它累计每个子窗口所占据掉的可用区域的长宽尺寸和。每个子窗口在 响应 WM SIZEPARENT消息时一殷都要把自己所占据的区域的高和宽分别累加到 sizeTotal结构的cy和cx 成员里。这冇什么意义呢?当每个子窗口所占据的区域都是挨在一起的时候,这个 sizetotal结构就有意义 了,主框架窗口可以使 flag等于 repos Query,使 sTretch等于 FALSE来调用 Reposition bars函数, Reposition Bars函数会把 sizetotal结构的两个成员值拷以到 lp RectParam参数里返回给主框架类(前面也提 到过,这样主柱架类就知道它的客户区内的子窗口占去了客户区内多大的一块空间。如果你的主框架窗口 没有利用这个信息,那么响应 WM SIZEPARENT消息的子窗口就可以不理睬 size Total成员 ID的分配 可以看到,每个子窗口都有个id,同一个父窗口的子窗口的id不能亘复。mfec的一些现成的控件子窗口都 有预定义的id: id名id值意义 AFX IDW TOOLBAR0xE800∥主窗口的工具栏的d AFX DW STATUS BAR OXI8O∥状态栏的id AFX IDW PREVIEW BAR OXE802 Print Preview Dialog Bar AFX IDW RESIZE BAR OxE803 /OLE in-place resize bar AFX IDW REBAR OxE804/CoMCTL32 rebar Bar AFX IDW DIALOGBAR OxE805 //CDialogBar 还有象单文档程序的视图窗口,多文档程序的那个 MDIClient窗口,分隔条窗口,他们的i值介于下面两 个i值之间: AFX IDW PANE FIRST OxE900 AFX IDW PANE LA ST OXE9FF 你要给你自己的子窗口分配i的话,别和上面的重复了。一般如果用IDE的菜单 view/resource symbols项 来加入自己的id的话,是不会重复的。有关id,还可以看看msdn里的TNo20文章,那是专讲id的。 实例分析 1。 FRaine Wnd类是如何调用 Reposition bars函数的 前面介绍了 Reposition bars的各个参数和意义,现在看看 FRame Wnd类是如何谓用这个函数的,从中可以 学习 Reposition Bars函数的使用方法。 CFramewnd类及其派生类生成的窗口的客户区内可以有工具栏,状态条和视图窗口等子窗口。当父窗口的 尺寸发生变化时,这些子窗口的各自的位置和大小比例关系俣持个变,这就需要父窗口一旦在它自己的尺 寸发生变化时就调用 Rep osition bars函数 CFrameWnd类是集中在函数 Recalclay out里调用 Reposition Bars 函数的。该类俣证了在窗口尺寸发生变化时函数 RecalcLay out都被调用,从而 Reposition bars函数也能祓 及时调,确保了各个子窗口都能及时调整自己的位置和大小。 Recalclay out是个虚函数。该函数的功能就是在主框架的客户区内提供一个初始的可用区域,并把这个区 域放在一个 CRect类型的变量里。该函数大致是这样的 void CFrame Wnd: Recalc Lay out(BOOL nOtify) if (m bInRecalcLay out) return;}这人概是在防止该函数重入 m bInRecalclay out= TRUE, if (Get Style(& FWS SNAPTOBARS CRect rect(0,0,、32767.32767); Reposition Bars(0, Oxffff, AFX IDW PANE FIRST, repos Query &rect, &rect, FALSE) Reposition Bars(0), Oxttff, AFX IDW PANE FIRST, repos xtra, &m rect Border, &rect, TRUE) Calc window Rect(&rect) Set Window Pos(NULL, 0, 0, rect Width(, rect Height SWP NOACTIVATESWP NOMOVESWP NOZORDER else Reposition Bars(0, Oxffff. AFX IDW PANE FIRST, repos Extra, &m rect Border) m bInReca lcl ayout=FALSE }可以看出,mf认为这个函数是不能重入的。在编制自己的 Recalc lay outo函数时也得用同样的方法来防 上重入 后面的i语句检查框架窗口是否具有风格 FWS SNAPTOBARS,这个风格用在什么时候呢?我是这样认为 的:通常都是在主框架窗口的尺寸改变 时,子窗凵在响应 WM SIZEPARENT消息时调整自己的尺寸以便跟上框架窗凵的尺寸变化。有这样的情 况:父窗口的客户区内的子窗口的数日是动态变 化的,而且这些子窗口互相不能重叠,他们的尺寸由于某种原因不好改变。那么当子窗口的数目发生增减 时,如不调整父窗口自己的尺寸,就会导 致客户区留下空白或新増加的子窗口没有多余空间安排。 FWS SNAPTOBARS风格就是用在这和情况下, 使父窗凵能调整自己的大小以便容纳子窗口 看这个分支里的语句,似乎是这样的。 般都不会有 FWS SNAPTOBARS风格的,所以一般是执行else分支。在这个分支里简单地调用 Reposition bars去重排所有的子窗口,它的参数 ψ pRect client使用默认的NUL值,意忠就是初始可用区域是父馁口的整个客户区。 可以在自己的派生类里编写自己的 RecalcLayout函数,以便用自己的方法调用 Reposition bars函数。要注 意的是在 FRame Wnd类的窗口刈被创建 时 Recalclay out函数乜被调用,此时可能某些用户自己加的子窗口还未被创建出来,所以在这个函数内如 果要引用某个用户自己加的子窗口的句柄 的话必须先用: Is Window函数判断一下该窗口句柄是否可用。否则的话就会出现非法菜作了 实成演练 由于精力有限,只提供一个实战例子:将视图,工具栏和状态栏赶到右边 我们要生成这样的界面:祝图窗口,工具栏和状态条统统在右边,左边是个自己加的窗口。 第一步:启动 App wizard生成一个单文档程序,仝韶使用默认设置。 第二步:在 MAin Frame类里增加一个成员 C Wnd m mywnd。 第三步:在 CAin frame: On)函数里增加这几行: m mywnd. CreateEx WS EX CLIENTEDGE AfxRegister WndClass CS HREDRAWCS VREDRAW LoadCursor(NULL, IDC ARROW) CreateSolidBrush(RGB(190, 190, 190)) WSⅥ SIBLE WS CHILD, CRec(00,0,0) IDC MYPAN用IDE的菜单 view/resource symbols项加入的id 第四步:片动 Class view,在 CMain frame里加上虚函数 Recall ayout(,函数体这样写 void MAin Frame: Recalc Layout(BOoL bNotify) if(m bInRecalcLay out) eturn m bInrecalcl ay out= TRUES /rctl是新加的窗口将占据的区域 ∥reu2就是提供给工具栏,状态条和视图窗口的初始可用Ⅸ域。 CRect rect 1. rect 2. Get Client rect(&rect l) rect 1. right=rect 1. right/3; Get Client Rect(&rect 2); rect2 left rect 2. right/3 i: Window( m my wnd m hWnd)∥这句是不能少的 m Iny wnd. MoveWindow (&rect 1) Reposition Bars(o, Oxffff. AFX IDW PANE FIRST, repos Extra, CRect(0,0,0, 0), &rect2) m bInrecalcLay out- FALSE, 第五步:用IDE的菜单 view/resource symbols项加入一个id: IDC MYPANE。 第六步:编译并运行程序。 好了,在主框架窗口的左边多了一个灰色的窗口,它占主窗口客户区的三分之一。工具栏,状态条和视图 都被赶到右边三分之二的地方去了。 Toolbar制作菜单条过程详解 现在许多用户界面都使用工具栏制作菜单条,小弟最近对此感兴趣,便从网上求助,可是得到的帮助大多 是 BCGControlbar的源代码或者是 Sizablcrcbar的源代码,对于只希望是自己的界面具有该功能的朋友来 说,这也许是不锗的选择,只要看一下demo,然后直接调用别人的类库就可以∫,但对丁我等对此话趑感 兴趣,希望弄憧其来龙去脉的读者来说,直接看这些没有详细解释的源代码,要从屮弄出个所以然来,实 不是件容易的是,至少对丁像我这样的菜鸟来说是这样的,本文岀丁此种原凼,希望对还在寻求此帮助的 读者能提供一些帮助 下面我们边看边侃 在接收到 toolbarbutton按下消息时,我们一般使用 TrackPopupMenuEx弹出菜单,问题的关键是,在菜单 未关闭时, TrackPop upMenuEx并不返回,并拦截鼠标和键盘消息,使用spy可以看到,此时的工具栏收不 到任何消息,当然无从改变热点,这就需要我们自己探测鼠标位置并在鼠标移动到下一个热点时关冂上一 个菜单并显示下一个菜单。这里我们使用钩子函数 Set windows hookex在调用 TrackPupupMenuEx前安装 WH MSGFILTER钩」,代码如下: m hMsgHook= Set WindowsHookEx( WH MSGFILTER, MessageProc, 0, Get CurrentThreadldO): MssageProc是钩子函数,代码如卜 LRESULT CALLBACK MessageProc(int code, WPARAM wParam, LPARAMIParam) if(code ==MSGF MENU) HookMessageProc(lParam) return CallNextHookEx(m hMsgHook, code, w Param, IParam) 函数检查消息,如果是来自菜单,则将消息传递绘函数 HookMes sageProc处理,我们所要做的就是在该函 数中检测消息 WM MOUSEMOVE,并测试鼠标位置,如果鼠标已经栘动到另一个按钮上,则关闭菜单并 显示下一个菜单,关闭菜单使用消息 WM CANCELMODE,当菜单关闭后,我们要释放钩子,在下一个菜 单弹出时重新安装钩子,弹出菜单示例代码如下 void Track Popup(hWNd hWnd ToolBar, int iButton while(iButton >-0 SendMessage(h WndToolBar, TB SETHOTITEM, iButton,O pOpup=iButton; ∥安装钩子 g hMsgHook- Set WindowsHookEx(WH MSGFILTEr, MessageProc, O, Get CurrentThreadldo): ∥弹出菜单 Track Popup MenuEx…) ∥卸毂钩子 Unhook Windows HookEx(g hMsgHook iButton= INextPop:∥下一个弹出项,若为负,则退出 Sendmessage(h WndToolBar,TB SETHOTITEM-1,0); (经验与建议:如果 button使用样式 TBSTYLE DROPDOWN请不要在消息 TBN DROPDO WN中直接调 用该凼数,应使用中间消息,然后使用 Postmessa个发送该消息,以使 TBN DROPDOWN可以直接返回, 否则消除第一个高亮热点是很麻烦的事。 pOpup为当前弹出项, INextPop为下一个弹出项,这些变量需要在函数 HookMessage proc中处理,示例代 码如卜: void HookMessageProc(MSG* pMsg) if(pMsg->message ==WM MOUSEMOVE) int iButton iCount POiNTpt-=i LOWORD(pMSg->IParam), HIWORDpMsg->IParam)); ScreenToclient(h Wnd Toolbar, &pt iButton= SendMessage(h WndToolbar, TB HITTEST, 0, &pt); iCount= SendMessage(hWndToolbar, TB BUTTONCOUNT,0, 0) If(iPopup!=iButton &c iButton ICount &k iButton >=0) INextPop=iButton: SendMessage(hWndMain, WM CANCELMODE, 0, O) (经验与建议:不要试图在此处调用 Track Popup,我曾试图取消该函数内的 while循环,直接在此调用该 函数,结果是在 TrackPopupMenulx未返回之前,该函数已被调用) tPc

...展开详情
试读 127P vc界面编程经典实例
立即下载 身份认证VIP会员低至7折
一个资源只可评论一次,评论内容不能少于5个字
您会向同学/朋友/同事推荐我们的CSDN下载吗?
谢谢参与!您的真实评价是我们改进的动力~
关注 私信
上传资源赚钱or赚积分
最新推荐
vc界面编程经典实例 1积分/C币 立即下载
1/127
vc界面编程经典实例第1页
vc界面编程经典实例第2页
vc界面编程经典实例第3页
vc界面编程经典实例第4页
vc界面编程经典实例第5页
vc界面编程经典实例第6页
vc界面编程经典实例第7页
vc界面编程经典实例第8页
vc界面编程经典实例第9页
vc界面编程经典实例第10页
vc界面编程经典实例第11页
vc界面编程经典实例第12页
vc界面编程经典实例第13页
vc界面编程经典实例第14页
vc界面编程经典实例第15页
vc界面编程经典实例第16页
vc界面编程经典实例第17页
vc界面编程经典实例第18页
vc界面编程经典实例第19页
vc界面编程经典实例第20页

试读结束, 可继续阅读

1积分/C币 立即下载