关于窗体的子类化(100分)

  • 主题发起人 主题发起人 游戏规则
  • 开始时间 开始时间

游戏规则

Unregistered / Unconfirmed
GUEST, unregistred user!
要在MDI程序的可户窗口画一幅图,必须使用所谓的子类化技术。 为什么呢? 子类化到底<br>是做什么用的?我总是不大明白,那位大虾能给解释一下?
 
使用API函数自己画,这方面的例子很多啊!
 
子類化也是我一直困惑的問題<br>做出來了但是不知為什么要這樣做<br>幫你提一下
 
楼上的说的对,要是做的话我也可以做的出。但是对这个概念感觉不是很清楚,请高手来<br>解释一下吧。我把程序贴上来<br>unit MainFrm;<br><br>interface<br><br>uses Windows, SysUtils, Classes, Graphics, Forms, Controls, Menus,<br>&nbsp; StdCtrls, Dialogs, Buttons, Messages, ExtCtrls, JPeg;<br><br>type<br>&nbsp; TMainForm = class(TForm)<br>&nbsp; &nbsp; mmMain: TMainMenu;<br>&nbsp; &nbsp; mmiFile: TMenuItem;<br>&nbsp; &nbsp; mmiNew: TMenuItem;<br>&nbsp; &nbsp; mmiClose: TMenuItem;<br>&nbsp; &nbsp; N1: TMenuItem;<br>&nbsp; &nbsp; mmiExit: TMenuItem;<br>&nbsp; &nbsp; mmiImage: TMenuItem;<br>&nbsp; &nbsp; mmiTile: TMenuItem;<br>&nbsp; &nbsp; mmiCenter: TMenuItem;<br>&nbsp; &nbsp; mmiStretch: TMenuItem;<br>&nbsp; &nbsp; imgMain: TImage;<br>&nbsp; &nbsp; procedure mmiNewClick(Sender: TObject);<br>&nbsp; &nbsp; procedure mmiCloseClick(Sender: TObject);<br>&nbsp; &nbsp; procedure mmiExitClick(Sender: TObject);<br>&nbsp; &nbsp; procedure mmiTileClick(Sender: TObject);<br>&nbsp; private<br>&nbsp; &nbsp; FOldClientProc,<br>&nbsp; &nbsp; FNewClientProc: TFarProc;<br>&nbsp; &nbsp; FDrawDC: hDC;<br>&nbsp; &nbsp; procedure CreateMDIChild(const Name: string);<br>&nbsp; &nbsp; procedure ClientWndProc(var Message: TMessage);<br>&nbsp; &nbsp; procedure DrawStretched;<br>&nbsp; &nbsp; procedure DrawCentered;<br>&nbsp; &nbsp; procedure DrawTiled;<br>&nbsp; protected<br>&nbsp; &nbsp; procedure CreateWnd; override;<br>&nbsp; end;<br><br>var<br>&nbsp; MainForm: TMainForm;<br><br>implementation<br><br>uses MdiChildFrm;<br><br>{$R *.DFM}<br><br>procedure TMainForm.CreateWnd;<br>begin<br>&nbsp; inherited CreateWnd;<br>&nbsp; // Turn the ClientWndProc method into a valid window procedure<br>&nbsp; FNewClientProc := MakeObjectInstance(ClientWndProc);<br>&nbsp; // Get a pointer to the original window procedure<br>&nbsp; FOldClientProc := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));<br>&nbsp; // Set ClientWndProc as the new window procedure<br>&nbsp; SetWindowLong(ClientHandle, GWL_WNDPROC, LongInt(FNewClientProc));<br>end;<br><br>procedure TMainForm.DrawCentered;<br>{ This procedure centers the image on the form's client area }<br>var<br>&nbsp; CR: TRect;<br>begin<br>&nbsp; GetWindowRect(ClientHandle, CR);<br>&nbsp; &nbsp;with imgMain do<br>&nbsp; &nbsp; &nbsp;BitBlt(FDrawDC, ((CR.Right - CR.Left) - Picture.Width) div 2,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ((CR.Bottom - CR.Top) - Picture.Height) div 2,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Picture.Graphic.Width, Picture.Graphic.Height,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Picture.Bitmap.Canvas.Handle, 0, 0, SRCCOPY);<br>end;<br><br>procedure TMainForm.DrawStretched;<br>{ This procedure stretches the image on the form's client area }<br>var<br>&nbsp; CR: TRect;<br>begin<br>&nbsp; GetWindowRect(ClientHandle, CR);<br>&nbsp; StretchBlt(FDrawDC, 0, 0, CR.Right, CR.Bottom,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;imgMain.Picture.Bitmap.Canvas.Handle, 0, 0,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;imgMain.Picture.Width, imgMain.Picture.Height, SRCCOPY);<br>end;<br><br>procedure TMainForm.DrawTiled;<br>{ This procedure tiles the image on the form's client area }<br>var<br>&nbsp; Row, Col: Integer;<br>&nbsp; CR, IR: TRect;<br>&nbsp; NumRows, NumCols: Integer;<br>begin<br>&nbsp; GetWindowRect(ClientHandle, CR);<br>&nbsp; IR := imgMain.ClientRect;<br>&nbsp; NumRows := CR.Bottom div IR.Bottom;<br>&nbsp; NumCols := CR.Right div IR.Right;<br>&nbsp; with imgMain do<br>&nbsp; &nbsp; for Row := 0 to NumRows+1 do<br>&nbsp; &nbsp; &nbsp; for Col := 0 to NumCols+1 &nbsp;do<br>&nbsp; &nbsp; &nbsp; &nbsp; BitBlt(FDrawDC, Col * Picture.Width, Row * Picture.Height,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Picture.Width, Picture.Height, Picture.Bitmap.Canvas.Handle,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;0, 0, SRCCOPY);<br>end;<br><br>procedure TMainForm.ClientWndProc(var Message: TMessage);<br>begin<br>&nbsp; case Message.Msg of<br>&nbsp; &nbsp; // Capture the WM_ERASEBKGND messages and perform the client area drawing<br>&nbsp; &nbsp; WM_ERASEBKGND:<br>&nbsp; &nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; &nbsp; CallWindowProc(FOldClientProc, ClientHandle, Message.Msg, Message.wParam,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Message.lParam);<br>&nbsp; &nbsp; &nbsp; &nbsp; FDrawDC := TWMEraseBkGnd(Message).DC;<br>&nbsp; &nbsp; &nbsp; &nbsp; if mmiStretch.Checked then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DrawStretched<br>&nbsp; &nbsp; &nbsp; &nbsp; else if mmiCenter.Checked then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DrawCentered<br>&nbsp; &nbsp; &nbsp; &nbsp; else DrawTiled;<br>&nbsp; &nbsp; &nbsp; &nbsp; Message.Result := 1;<br>&nbsp; &nbsp; &nbsp; end;<br>&nbsp; &nbsp; { Capture the scrolling messages and ensure the client area<br>&nbsp; &nbsp; &nbsp; is redrawn by calling InvalidateRect }<br>&nbsp; &nbsp; WM_VSCROLL, WM_HSCROLL:<br>&nbsp; &nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; &nbsp; Message.Result := CallWindowProc(FOldClientProc, ClientHandle, Message.Msg,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Message.wParam, Message.lParam);<br>&nbsp; &nbsp; &nbsp; &nbsp; InvalidateRect(ClientHandle, nil, True);<br>&nbsp; &nbsp; &nbsp; end;<br>&nbsp; &nbsp; else<br>&nbsp; &nbsp; // By Default, call the original window procedure<br>&nbsp; &nbsp; &nbsp; Message.Result := CallWindowProc(FOldClientProc, ClientHandle, Message.Msg,<br>&nbsp; &nbsp; &nbsp; &nbsp; Message.wParam, Message.lParam);<br>&nbsp; end; { case }<br>end;<br><br>procedure TMainForm.CreateMDIChild(const Name: string);<br>var<br>&nbsp; MdiChild: TMDIChildForm;<br>begin<br>&nbsp; MdiChild := TMDIChildForm.Create(Application);<br>&nbsp; MdiChild.Caption := Name;<br>end;<br><br>procedure TMainForm.mmiNewClick(Sender: TObject);<br>begin<br>&nbsp; CreateMDIChild('NONAME' + IntToStr(MDIChildCount + 1));<br>end;<br><br>procedure TMainForm.mmiCloseClick(Sender: TObject);<br>begin<br>&nbsp; if ActiveMDIChild &lt;&gt; nil then<br>&nbsp; &nbsp; ActiveMDIChild.Close;<br>end;<br><br>procedure TMainForm.mmiExitClick(Sender: TObject);<br>begin<br>&nbsp; Close; <br>end;<br><br>procedure TMainForm.mmiTileClick(Sender: TObject);<br>begin<br>&nbsp; mmiTile.Checked := false;<br>&nbsp; mmiCenter.Checked := False;<br>&nbsp; mmiStretch.Checked := False;<br>&nbsp; { Set the Checked property for the menu item which invoked }<br>&nbsp; { this event handler to Checked &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br>&nbsp; if Sender is TMenuItem then<br>&nbsp; &nbsp; TMenuItem(Sender).Checked := not TMenuItem(Sender).Checked;<br>&nbsp; { Redraw the client area of the form }<br>&nbsp; InvalidateRect(ClientHandle, nil, True);<br>end;<br><br>end.
 
重点在这里:<br>procedure TMainForm.CreateWnd;<br>begin<br>&nbsp; inherited CreateWnd;<br>&nbsp; // Turn the ClientWndProc method into a valid window procedure<br>&nbsp; FNewClientProc := MakeObjectInstance(ClientWndProc);<br>&nbsp; // Get a pointer to the original window procedure<br>&nbsp; FOldClientProc := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));<br>&nbsp; // Set ClientWndProc as the new window procedure<br>&nbsp; SetWindowLong(ClientHandle, GWL_WNDPROC, LongInt(FNewClientProc));<br>end;<br><br>我猜这程序并不是你亲手做得吧(冒犯了:) ,否则也不会来问这个问题了;<br>虽然只是简单的几行代码,但不是拍脑袋就能写出来的。<br><br>大概解释一下,为什么叫做子类化呢,如果你了解Windows的消息机制,应该知道所有<br>的window 都必须向系统注册(在CreatWinow时)自身的窗口函数,运行期间消息可被<br>指派至特定窗口控件的窗口函数处理。<br><br>现在的问题是,客户区窗口(对应于句柄ClientHandle)事先已经有了自己的窗口<br>消息处理函数,所以需要向系统重新注册一个新的窗口函数以代替旧的,这一过程就是<br>子类化。子类化动作由<br>SetWindowLong(ClientHandle, GWL_WNDPROC, LongInt(FNewClientProc));<br>来完成,<br><br>前几行代码当然必不可少<br>1. &nbsp;inherited CreateWnd; 调用缺省的MainForm窗口创建过程<br>2.至于<br>&nbsp; FNewClientProc := MakeObjectInstance(ClientWndProc);<br>这行代码大有文章,因为窗口函数是需要被系统callback的,所以必须是全局函数<br>(否则会有隐含的self参数) 或是静态函数(Delphi里没有),MakeObjectInstance便<br>是将一个对象方法转化为一个有效的窗口函数指针的,其过程相当复杂,我写过一篇文章专<br>门讲述它,感兴趣可参考<br>http://www.delphibbs.com/delphibbs/dispq.asp?lid=584889<br><br>3. &nbsp;FOldClientProc := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));<br>就简单了,只是把就的窗口函数指针保留下来,虽然大多数情况也没什么用,留着就留着吧,<br>也占不了多少地方。
 
呵呵,当然不是我写的了:) 很有帮助,感觉比以前清楚些了。多谢,多谢~~~
 
多人接受答案了。
 

Similar threads

后退
顶部