This will be the last component, the target is determined, in fact, non-visual components are difficult to do more than the visual components, because it is not inherited from Tcomponent, there is no many attributes and events. And these must be done from the beginning.
This non-visual component, I decided to use the tray component, where there is more technologies, I am not as column, and then I will talk. In addition, there may be more items, please see patient.
Used technology:
1 As a core function, of course is the application of the tray.
2 How do 2 tray components affect the minimization of the main window to hide
3 how to deal with the message
4 components editor usage
Each technology above is very interesting, let us look at it one by one:
A tray is a feature of the system shell programming. I believe that we have seen a lot, probably knowing what it looks like.
So how are it implemented?
Windows defines such a structure to store the tray information:
Typedef struct _notifyicondata {// NID
DWORD CBSIZE;
Hwnd hwnd;
UINT UID;
UINT UFLAGS;
Uint ucallbackmessage;
Hicon Hicon;
Char sztip [64];
Notifyicondata, * PNOTIFYICIONDATA;
CBSIZE is the size of the Notifyicondata structure, we generally use sizeof.
HWND a window handle for retrieving tray messages. However, our non-visual components do not have a window, this is the technical list, third, here, here is omitted
UID only identifies the tray icon, we can specify a number, but if there is a different icon at the same time, the number should be different.
UFLAGS is one or more of Nif_icon, Nif_Message, Nif_tip, we can use it.
UcallbackMessage; Tray message is our custom message, here we are defined as:
Const
WM_TRAYMSG = WM_USER 10;
Hicon tray icon handle
Sztip This is a tray prompt. When the tray appears, the mouse is moved to, there will be this prompt.
Delphi will define this structure as TNotifyicondata, and we will use this.
We apply pallets to use the API function shell_notifyicon, two of which are parameters, the first one
One of NIM_ADD, NIM_DELETE, NIM_MODIFY, indicates the addition of the tray (icon appears)
Modify the tray (such as icon, prompt), delete (icon disappearance) The second parameter is the pointer of Notifyicondata
Well, the tray should be almost.
Second, if the component can determine whether the main form is minimized, it is normal to minimize and there is no pallet icon. Still minimizing the screen, we can't see it, and the icon appears in the tray area. Here is a member to decide for FACTIVE.
So how do we affect the main form, that is, how to cut the form of the form of the form.
Global Variable Application has a method for procedure hookmainwindow (hook: twindowhook;
As the name suggests, hook all messages in the main window. The parameters inside are TwindowHook types, it is a method pointer, defined as follows:
Type TwindowHook = Function: Boolean of Object;
We have to define the process, then pass it to HookmainWindow: Function AppmsGHOOK (VAR Msg: TMESSAGE): Boolean;
Application.hookmainWindow (Appmsghook);
After doing so, all the news of the main window will pass the AppMsGHOOK method, minimize the message is no exception, then we can cut this message in it, and do some operations:
What to do, first determine if the component is designed, if not, do not operate, if not the next step
IF not (csdesigningin companystate) THEN
Such intentions are obvious because the main window when the design is actually Delphi IDE, if he handles the message, is actually the minimization message for processing IDE, then if you minimize IDE, there will be a tray. . So can't.
The next step is to intercept the minimization message, and whether Factive is true:
IF (msg.msg = wm_syscommand) and (factive) THEN
Established, execute the code inside, explain in the code, here only say two:
SetWindowlong (Application.handle, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
After setting this property, the window minimizes does not stop in the task bar, but stops on a position of the screen, where is this location, by
Placement.flags: = WPF_SETMINPSITION;
Placement.ptMinPosition.x: = 1050;
Placement.ptMinPosition.y: = 800;
SetWindowPlacement (Application.handle, @ planement);
Decided, specifically see the code, check it yourself, not much here
After setting the setting setwindowlong, the problem is coming, the window minimized the style changed, when you set FACTIVE to false, minimize the window, there is no pallet icon, but the window is also minimized to the screen The location is going, we can't see it, and you can't restore it (no tray). How to do it,
It turns out that a getWindowlong function returns the current style, we can call this in the constructor's constructor
OldStyleEx: = getWindowlong (Application.Handle, GWL_EXSTYLE);
At this time, OldStyleex: The window is saved, the window is minimized, the window minimizes, calls setWindowlong, set the new smallest style. And when we trigger the tray event, let the form recover than the size, we call in the process function
SetWindowlong (Application.handle, GWL_EXSTYLE, OLDSTYLEEX);
In this way, the window returns to the original style. At this time, we set up FACTIVE for false, and the window can be minimized.
When the control is released, we must call Application.unhookMainWindow (AppmsGHOOK); to release the hook
In fact, there is also an imperfect place. You should set a member variable. When you set up a tray, the window is normal and minimized, or it is minimized. And I didn't do this, directly if Factive is True, minimize the pallet icon, and the window minimizes can't see it. However, it is impossible. If you are interested, you can help me perfect, and be used as your own practice. If the three tray is processed, it is said to set the pallet structure, you must have a window handle to retrieve the tray message, then what this handle is, non-visual components do not have a window handle.
If you have the source code of TTIMER, you must know this code:
FwindowHandle: = AllocatehWnd (WndProc);
It creates a visible window, returns his handle, and specifies the message processing process for WndProc as the window.
Why don't we follow it?
So it is also defined a member handle:
Fhandle: hwnd;
Assign the handle to the hwnd field of Notifyicondata
Refine a message processing process:
Procedure WndProc (Var Msg: TMESSAGE);
In the component constructor:
FHANDE: = AllocatehWnd (WndProc);
Thus, the component can intercept the message of the tray and processed accordingly during the WNDPROC. It is necessary to introduce a custom message of the tray here:
We customize this message WM_TRAYMSG, which is the same as the UID of the tray, WPARAM is an event message that occurs on the icon, such as click, double click.
We must translate these messages into events, supply users, define several event scheduling functions:
// The following is the scheduling function of the event
Dynamic DBLCLICK; DYNAMIC
Dynamic;
Procedure MouseDown (Button: TMouseButton; Shift: TshiftState; x, y: integer; Dynamic;
PROCEDURE MOUSEUP (Button: TMousebutton; Shift: TshiftState; x, y: integer; Dynamic;
PROCEDURE MOUSEMOVE (Shift: TshiftState; x, y: integer); Dynamic;
It means very obvious, not to say,
Of course there are several event methods pointers:
FoniconClick: tnotifyevent;
Fonicondblclick: tnotifyevent;
FoniconMouseMove: TMOUSEMOVEEVENT;
FoniconMousedown: tmouseevent;
FoniconMouseUp: tmouseevent;
Then determine the message in WndProc and call the corresponding event scheduling function. Look at the code, there is interpretation.
Ok, three technologies solved, fourth, or wait for the code to add the component editor. The following is the source code:
Unit mytray;
Interface
Uses
Windows, Messages, Sysutils, Classes, Graphics, Controls,
Forms, Dialogs, Shellapi, ExtCtrls, stdctrls;
Const
// Custom tray message
WM_TRAYMSG = WM_USER 10;
Type
/ / Restore the window, left double click, right double click, left click, right double click TRMODE = (LDBClick, RDBClick, LClick, Rclick);
TMYTRAY = Class (Tcomponent)
Private
// Private member
Ficon: ticon; // icon
FDFICON: THANDLE; / / Application Default icon
FSETDFICON: BOOLEAN; / / Whether to use the app's icon, if true, FICON is NIL
FICONDATA: TNOTIFYICONDATA; / / Tray Data Structure
Ismin: boolean; // Identify if the window minimizes
FHANDLE: HWND; / / Unopened form handle for processing tray events
Factive: boolean; // Whether to enable the tray
FHINT: STRING; // Tray prompt string
FRMODE: TRMODE; / / Restore Window
Isclickin: boolean; // Identifies whether the mouse is point on the icon
OldStyleEx: longint; // Save old window style
// Event member
FoniconClick: tnotifyevent;
Fonicondblclick: tnotifyevent;
FoniconMouseMove: TMOUSEMOVEEVENT;
FoniconMousedown: tmouseevent;
FoniconMouseUp: tmouseevent;
// Setting method
Procedure seticon (Value: ticon);
Procedure setdficon (Value: boolean);
Procedure setActive (Value: Boolean);
Procedure Sthint (Value: string);
Procedure StrMode (Value: TRMODE);
// Private method
Procedure settray (WAY: DWORD); // Set tray style, modify, delete, increase
Function getActiveicon: thandle; // get a useful icon handle
protected
// Application message hook, get minimization of the main window
Function AppmsGHOOK (VAR Msg: TMESSAGE): Boolean;
Procedure WndProc (Var Msg: TMessage); // Unconado window process
// The following is the scheduling function of the event
Dynamic DBLCLICK; DYNAMIC
Dynamic;
Procedure MouseDown (Button: TMouseButton; Shift: TshiftState; x, y: integer; Dynamic;
PROCEDURE MOUSEUP (Button: TMousebutton; Shift: TshiftState; x, y: integer; Dynamic;
PROCEDURE MOUSEMOVE (Shift: TshiftState; x, y: integer); Dynamic;
public
Constructor Create (Aowner: Tcomponent); OVERRIDE;
DESTRUCTOR DESTROY; OVERRIDE;
Published
Property Active: Boolean Read Factive Write SetActive Default False;
Property Icon: Ticon Read Ficon Write Seticon;
Property Setdficoned: Boolean Read Fsetdficon Write Setdficon Default TRUE
Property Hint: String Read Fhint Write Sthint; Property Rmode: TRMode Read FRMODE WRITE SETRMODE DEFAULT LDBCLICK
// Event method pointer
Property OnConclick: TNotifyEvent Read FoniconClick Write FoniconClick;
Property ONICONDBLCLICK: TNOTIFYEVENT READ FONICONDBLCLICK WRITE FONICONDBLCLICK
Property Onicymousemove: TMOUSEMOVEEVENT READ FONICOMOVE WRITE FONICOMOVE
Property OnicyMousedown: TMouseEvent Read FoniconMousedown Write FoniconMousedown
Property Onicymouseup: TMouseEvent Read FoniconMouseup Write FoniconMouseup;
END;
PROCEDURE register;
IMPLEMentation
PROCEDURE register;
Begin
Registercomponents ('Wind', [TMYTRAY]);
END;
/// Tmytray
Constructor TMYTRAY.CREATE (AOWNER: TComponent);
Begin
Inherited Create (Aowner);
// Set the program hook, specify AppMsGHOOK to process functions,
//, any message from the application will pass this function
Application.hookmainWindow (Appmsghook);
Ficon: = ticon.create;
/ / Get the handle of the default icon, the icon is the icon for the application
FDFICON: = Application.icon.handle;
FSetdficon: = true;
FACTIVE: = FALSE;
FRMODE: = ldbclick;
ISMIN: = false;
// Create a unconventional window and specify a window process to process tray events
FHANDE: = AllocatehWnd (WndProc);
/ / Save the old style of the form, and restore the original window while recovering the window.
OldStyleEx: = getWindowlong (Application.Handle, GWL_EXSTYLE);
END;
DEStructor TMYTRAY.DESTROY;
Begin
Application.unhookMainWindow (AppMsghook);
// eliminate the tray before the object is released
Settray (nim_delete);
/ / Release the handle of impossible windows
Deallocatehwnd (fhaldle);
Ficon.free;
Inherited destroy;
END;
// Application hook, can intercept all messages of the application
Function TMYTRAY.Appmsghook (var Msg: TMESSAGE): Boolean
VAR Placement: Windowplacement;
Begin
Result: = FALSE;
/ / Ensure that the program does not process the minimization message during design
IF not (csdesigningin companystate) THEN
IF (msg.msg = wm_syscommand) and (factive) THEN
Begin
IF msg.wparam = sc_minimize kil
Begin
// After setting this property, the window minimizes does not stop in the task bar, but stops on the screen.
// The location is determined by setwindowplacement to determine showWindow (Application.handle, Sw_hide);
SetWindowlong (Application.handle, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
GetWindowPlacement (Application.handle, @ planement);
Placement.flags: = WPF_SETMINPSITION;
Placement.ptMinPosition.x: = 1050;
Placement.ptMinPosition.y: = 800;
SetWindowPlacement (Application.handle, @ planement);
Settray (NIM_ADD);
END;
END;
END;
Procedure Tmytray.seticon (Value: ticon);
Begin
Ficon.Assign (value);
Fsetdficon: = false; // With custom icons, the default icon is set to false
IF ficon.empty life
FSetdficon: = true;
IF (ismin) and (factive) THEN
Settray (NIM_MODIFY);
END;
/ / Set whether or not the default icon, with ficon is the mutual variable, only one of them
Procedure Tmytray.Setdficon (Value: Boolean);
Begin
IF FSETDFICI <> Value Then
Begin
FSETDFICON: = Value;
IF not fsetdficon then
Begin
IF ficon.empty life
FSetdficon: = true;
EXIT;
END;
end
Else Begin
IF (ismin) and (factive) THEN
Settray (NIM_MODIFY);
END;
END;
END;
Procedure Tmytray.setActive (Value: Boolean);
Begin
IF factive <> value kil
Begin
Factive: = Value;
END;
END;
Procedure Tmytray.Sethint (Value: String);
Begin
IF fhint <> Value Then
Begin
FHINT: = VALUE;
IF (ismin) and (factive) THEN
Settray (NIM_MODIFY);
END;
END;
Procedure Tmytray.setrMode (Value: TRMODE);
Begin
IF FRMODE <> Value Then
FRMODE: = Value;
END;
// Set the tray method, display, modify, delete, important ways
Procedure Tmytray.settray (Way: DWORD);
Begin
FICONDATA.CBSIZE: = Sizeof (Ficondata);
FICONDATA.WND: = fhandle;
FICONDATA.UID: = 0;
Ficondata.uflags: = nif_icon or nif_message or nif_tip;
FICONDATA.UCALLBACKMESSAGE: = WM_TRAYMSG;
Ficondata.hicon: = getActiveICON;
Strlcopy (FICONDATA.SZTIP, PCHAR (FHINT), 63);
Shell_Notifyicon (Way, @ ficondata);
END;
// Get the available icon Function Tmytray.getActiveIveicon: thandle;
Begin
IF not fsetdficon then
Result: = ficon.handle
Else
RESULT: = fdficon;
END;
// Tray message interception to call the corresponding event scheduling method
Procedure Tmytray.WndProc (var Msg: tMessage);
VAR P: TPOINT;
Begin
IF (msg.msg = wm_traymsg) and (factive) THEN
Begin
Case msg.lparam of
WM_LBUTTONDBLCLK: // Double click on
Begin
GetCursorpos (P);
DBLClick;
MouseDown (mbleft, keystoshiftstate (twmmouse (msg) .keys) [SSDOUBLE], P.X, P.Y);
IF frmode = ldbclick then
Begin
ShowWindow (Application.Handle, SW_SHOW);
// This is a very important one is to restore the window style, otherwise set the Active to True next time.
// After the minimization, the window will still fly in the lower left corner, and the pallet icon has seen it.
SetWindowlong (Application.handle, GWL_EXSTYLE, OLDSTYLEEX);
SendMessage (Application.handle, WM_SYSCOMMAND, SC_RESTORE, 0);
Settray (nim_delete);
END;
END;
WM_RBUTTONDBLCLK: / / Right Double click
Begin
GetCursorpos (P);
DBLClick;
MouseDown (Mbright, KeystoshiftState (TwMMouse (MSG) .keys) [SSDOUBLE], P.X, P.Y);
IF frmode = rdbclick then
Begin
ShowWindow (Application.Handle, SW_SHOW);
SetWindowlong (Application.handle, GWL_EXSTYLE, OLDSTYLEEX);
SendMessage (Application.handle, WM_SYSCOMMAND, SC_RESTORE, 0);
Settray (nim_delete);
END;
END;
WM_MOUSEMOVE: / / mouse movement
Begin
GetCursorpos (P);
MouseMove (KeystoshiftState (TWMMOUSE (MSG) .keys), P.X, P.Y);
END;
WM_LBUTTONDOWN: / / left left
Begin
GetCursorpos (P);
Isclickin: = true;
MouseDown (mbleft, keystoshiftstate (twmmouse (msg) .keys) [SSLEFT], P.X, P.Y);
END;
WM_LBUTTONUP: // Left click
Begin
GetCursorpos (P);
IF isclickin
Begin
ISClickin: = false;
Click;
IF frmode = lclick kilick
Begin
ShowWindow (Application.Handle, SW_SHOW);
SetWindowlong (Application.handle, GWL_EXSTYLE, OLDSTYLEEX);
SendMessage (Application.handle, WM_SYSCOMMAND, SC_RESTORE, 0);
Settray (nim_delete);
END;
END;
Mouseup (mbleft, keystoshiftstate (twmmouse (msg) .keys) [SSLEFT], P.X, P.Y); END;
WM_RBUTTONDOWN: / / Right click
Begin
GetCursorpos (P);
Isclickin: = true;
MouseDown (Mmbright, KeystoshiftState (Twmmouse (MSG) .keys) [SSRight], P.X, P.Y);
END;
WM_RBUTTONUP: / / Right click bounce
Begin
GetCursorpos (P);
IF isclickin
Begin
ISClickin: = false;
Click;
IF frmode = rclick kilick
Begin
ShowWindow (Application.Handle, SW_SHOW);
SetWindowlong (Application.handle, GWL_EXSTYLE, OLDSTYLEEX);
SendMessage (Application.handle, WM_SYSCOMMAND, SC_RESTORE, 0);
Settray (nim_delete);
END;
END;
Mouseup (Mbright, KeystoshiftState (TWMMOUSE (MSG) .keys) [SSRight], P.X, P.Y);
END;
END;
end
Else
Msg.Result: = DefWindowProc (Fhaandle, Msg.msg, Msg.wParam, Msg.lparam);
END;
/ / The following is a scheduling function of several events, relatively simple.
Procedure tmytray.dblclick;
Begin
IF assigned (fonicondblclick)
Fonicondblclick (Self);
END;
Procedure TMYTRAY.CLICK;
Begin
IF assigned (foniconclick) THEN
FoniconClick (Self);
END;
Procedure Tmytray.Mousedown (Button: TMouseButton; Shift: TshiftState; x, y: integer);
Begin
IF assigned (foniconmousedown) THEN
FoniconMouseDown (Self, Button, Shift, X, Y);
END;
Procedure TMYTRAY.MOUSEUP (Button: TMouseButton; Shift: TshiftState; x, y: integer);
Begin
IF assigned (foniconmouseup) THEN
FoniconMouseUp (Self, Button, Shift, X, Y);
END;
Procedure Tmytray.Mousemove (Shift: TshiftState; x, y: integer);
Begin
IF assigned (foniconmousemove) THEN
FoniconMouseMove (Self, Shift, X, Y);
END;
End.
If the group is made, I believe that after the above explanation, as well as the comment of the code, it should be not difficult to understand. What is the next thing, give me the effect of the tray control, that is, in the designer, when you double-click the component, or right-click the shortcut menu first item, you will pop up an About dialog box to explain my tray component.
This is to use the component editor. There are several classics to say, such as the DEPLPHI developer guide, I have learned from there, but I have encountered some problems, and I have been tortured for a few days. I don't want to introduce it in detail here. I will see those books, probably know, just talk slightly.
The principle is to achieve a subclass TTRAYICONEDITOR inherited from Tcomponiteteditor, and override the following three methods:
Function GETVERBCOUNT: Integer; Override;
Function GETVERB (INDEX: Integer): String; Override;
Procedure Executeverb (Index: Integer); OVERRIDE;
It is possible to understand as:
Getverbcount specifies the number of items for the control shortcut menu
Getverb specifies the name of the related items in the shortcut menu
Executeverb performs actions after clicking the shortcut menu item
Then call registercomponiteneditor (TMYTRAY, TTRAYICONEDITOR) in the Register method;
The first parameter is the component class name, the second is the class name of the component editor.
And the above method must reference Designintf, Designeditors.
When I did a problem after I did this, there was no problem with the compilation and installation. I created a test program, pull a tray component, double-click it, and there is no problem with the reference dialog box, and right click on the menu. But when I run the test program, there is such a compilation error:
[Fatal Error] Unit1.pas (7): File Not Found: 'Designeditors.dcu'
This makes me feel pain for a few days, the book is said so, there should be no mistakes. Later, after exploring, I found a solution.
The solution is to place the component editor class in another unit and reference my pallet component unit in this unit.
And installed. This can work normally, this editor unit is as follows:
UNIT ABOUTTRAY;
Interface
Uses
Sysutils, Classes, Designintf, Designeditors, Forms,
Mytray;
Type
TTRAYICONEDITOR = Class (Tcomponenteditor)
Function GETVERBCOUNT: Integer; Override;
Function GETVERB (INDEX: Integer): String; Override;
Procedure Executeverb (Index: Integer); OVERRIDE;
END;
PROCEDURE register;
IMPLEMentation
/// TTRAYICONEDITOR
Procedure TTRAYICONEDITOR.EXECUTEVERB (INDEX: Integer);
Begin
Case Index of
0: Application.MessageBox ('Hello, this is the wind-made pallet component !!', 'About');
END;
END;
Function TTRAYICONEDITOR.GETVERB (INDEX: Integer): String;
Begin
Case Index of
0: Result: = 'About mytray';
END;
END;
Function TTRAYICONEDITOR.GETVERBCOUNT: INTEGER;
Begin
RESULT: = 1;
END;
PROCEDURE register;
Begin
RegistercomponiteTeditor (TMYTRAY, TTRAYICONEDITOR); END;
End.
At this point, the pallet assembly is complete, pull it in the form designer, double click, pop up dialog box
The content is: "Hello, this is the wind-made pallet component !!". Haha, you succeeded.
As a final content made by the component, I want to use a bag to put all my component units and put it in my own new panel.
Before doing this, remove the components that are previously installed. Know how to delete, if you don't know, please see me in the first one.
Then open all component units, put registercomponents ('Samples', [TcoolMemo]);
Samples is changed to Wind. Then save
Then, in the IDE, point - "New-" Other ...
The new items dialog box, select the New page, and select the package,
A new package editor is popping up here.
Point File- "Save first in the IDE. Save the package editor. Save in the folder where the unit is located
All of my component units are placed in the Delphi7 / Mycom folder. So this package is of course saved here.
Then, the Add Editor is added to add all the component units, and of course, the component editor unit that is mentioned above is also included.
After adding, the Compile on the editor is completed, and then INSALL.
Success, look at the panel. All previous components are all in the Wind panel.
At this time, my mission has finished.
Conclusion
This time the team is finally finished, maybe someone will laugh, I am shallow, think so simple, is it necessary to take it? Maybe it is relatively simple, but some people will need it, I believe that my article will help them. Because these are what I have learned, I have encountered problems and solve it. So I personally feel very precious. And after writing these articles, I remember these knowledge tougey. This kind of interest is nothing.
Here, thank you for reading, maybe there is a chance to see you next time, but now I have to say goodbye. I wish you a happy.