Component production five (tray components)

zhaozj2021-02-16  136

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.

转载请注明原文地址:https://www.9cbs.com/read-8907.html

New Post(0)