In the Win32 Standard Control Library (ComctL32.dll), most controls support the settings of the OwnerDraw feature, here to talk about the writing methods of this type of control in Delphi as an example! Since the standard buttons have a set of specified appearance criteria (drawn by code in Windows ComctL32.dll), in order to give users a chance to draw controls, most controls support setting a logo when they are created to tell the system. The control needs to draw itself, such as the button: BS_OWNERDRAW, the menu is: MF_OWNERDRAW, etc. (there are also characteristics such as: ComboBox, Listbox have this feature), set this flag in Delphi, do not need to press Win32 in the window The class is defined, as long as it is handled in the CreateParams method, although the internal implementation method is the same, but after all, we write more convenient, isn't it? When you set these flags in the style in the window class, the system will send a WM_MEASUREITEM message when the control needs to be drawn, gives a parent window for this control (note that attention here, this is due to standard Win32 development methods Decided, because most of the controls were created when receiving the WM_CREATE message of the main form, a window process was the opportunity to write the code unique, all messages sent to the main thread message loop. Therefore, the message of the control is naturally sent here! Can not be used frequently using subcatenal or hypermathens) to determine the drawing range of the control, and then send WM_DRAWITEM to the parent form of this control, and we have to do A separate component, how do you know when to draw? The code should be written in the form! Fortunately, Delphi comments in the library take into account this demand, as long as your control is used in Delphi, the TFORM form sends all received messages to the corresponding window process, TWINControl.wmdrawItem related code is as follows (after processing ): procedure TWinControl.WMDrawItem (var Message: TWMDrawItem); begin if not DoControlMsg (Message.DrawItemStruct ^ .CtlID, Message) then iherited; end; and DoControlMsg implementation is simple: function DoControlMsg (ControlHandle: HWnd; var Message): boolean; var Control: TWinControl; begin DoControlMsg: = False; Control: = FindControl (ControlHandle); if Control <> nil then with TMessage (Message) do begin Result: = Control.Perform (Msg CN_BASE, WParam, LParam); DOCONTROLMSG: = true; end; end; After finding the control, add the ID of the message to the corresponding window is (CN_DRAWITEM = CN_BASE WM_DRAWITEM), so this is the second point of attention: Cut the WM_DRAWITEM message in the component is There is no effect, in fact, there is no such message to transfer to the window process of the component, and should be CN_DRAWITEM,
WM_MEASUREITEM's message processing procedure is the same, and the CN_MeasureItem message should be intercepted in the component! Note that these we can derive a component by TButton, code is as follows: TSundyButton = class (TButton) private FCanvas: TCanvas; IsFocused: Boolean; IsDown: boolean; protected procedure WMMOUSEDOWN (var message: TWMLButtonDown); message WM_LButtonDown; procedure WMMOUSEUP (var message: TWMLButtonUp); message WM_LButtonUp; procedure CNDRAWITEM (var message: TWMDRAWITEM); message CN_DRAWITEM; procedure CNMEASUREITEM (var message: TWMMEASUREITEM); message CN_MEASUREITEM; procedure CreateParams (var Params: TCreateParams); override; procedure CMEnabledChanged (var Message: TMessage); message CM_ENABLEDCHANGED; procedure CMFontChanged (var Message: TMessage); message CM_FONTCHANGED; public constructor Create (AOwner: TComponent); override; destructor Destroy; override; procedure SetButtonStyle (ADefault: Boolean); override; procedure DrawItem (const DrawItemstruct: TDRAWITEMSTRUCT); END; Implementation
Procedure TsundyButton.cndrawItem (Var Message: TwmdrawItem); Begin DrawItem (Message.DrawItemstruct ^); END;
procedure TSundyButton.CNMEASUREITEM (var message: TWMMEASUREITEM); begin message.MeasureItemStruct ^ .itemWidth: = Width; message.MeasureItemStruct ^ .itemHeight: = Height; end;
Constructor TsundyButton.create (Aowner: Tcomponent); Begin Inherited Create (Aowner); ControlStyle: = ControlStyle [CSReflector]; fcanvas: = tcanvas.create; width: = 85; Height: = 30;
Procedure TsundyButton.createParams (Var params: TcreateParams); Begin Inherited CreateParams (Params); with params do style: = style or bs_ownerdraw;
DESTRUCTOR TSUNDYBUTTON.DESTROY; Begin FreeAndnil (FCANVAS); inherited Destroy;
/ / You can draw a variety of effects in the way you like, just simple example, no // dazzle effect ^ _ ^ procedure tsundybutton.drawItem (const drawitemstruct: tdrawitemstruct); var REC: TRECT; Begin Fcanvas.Handle: = DRAWITEMSTRUCT.HDC; REC: = ClientRect; with fcanvas do beg pen.style: = pssolid; brush: = parent.brush; rowerRect (Rec.left, rec.top, rec.right, rip .Bottom, 4, 4); brush.color: = CLBTNFACE; Pen.Color: = CLBLACK; setbkmode (fcanvas.handle, transparent); if isdown kiloc.left: = Rec.LEFT 3; Rec.top: = Rec.top 1; font.color: = CLBLACK; DRAWTEXT (Handle, Pchar (CAPTION), Length (Caption), REC, DT_CENTER OR DT_VCENTER OR DT_SINGLINE); Else if not isdown the beginning ; DrawText (Handle, PChar (Caption), Length (Caption), Rec, DT_CENTER or DT_VCENTER or DT_SINGLELINE); end; if Enabled then begin if IsFocused then DrawFocusRect (Rec); end; end; FCanvas.Handle: = 0; end Procedure TsundyButton.cmenableDChanged (VAR Message: TMESSAGE); begin inherited;
Procedure tsundybutton.cmfontchanged (var message: tMessage); begin inherited; invalidate;
// This method is to be rewritten, and INHERITED is invoked to call the upper method Procedure TsundyButton.SetButtonStyle (adefault: boolean); begin if (adefault <> isfocused)., Isfocused: = adefault; invalidate;
Procedure TsundyButton.wmmousedown (var message: twmlbuttondown); begin inherited; isdown: = true; invalidate;
Procedure Wmmouseup (var message: twmlbutton); begin inherited; isdown: = false; invalidate;