![]() Home |
16. More Form Windows More Kinds of Top-Level Windows |
![]() Home |
In earlier lessons of DelphiZeus, you have seen some ways to create windows as a Top Level window (Form in a Delphi term). Here I will give some more methods for creating and using some windows that are not a child window on your form's client area. I have shown you how to make a window with the WS_POPUP style in Lesson-6 "Brushes and Pens", and how to create the standard system "Dialog" windows from a templete in Lesson-8 "Using Windows Dialogs". In this lesson I will show some more ways to use windows with the WS_POPUP style, like a "User Information" window. I will also create another main window (Form) that is not a Pop-Up and has it's own Task-Bar button. There are ways to make a "Tool" window, and you can have your program's "Tools" and options on this window. I also show you how to "Dock" three top-level windows, so they move together, as if they are attatched to each other. There are also examples for two API animation effects. |
| I have explanations and information about several methods for top level windows here. You can read through these below, and you may see something that you would like to try and use. The program code to use for this lesson is in the TopWnd.dpr program code, and the TopWndU.pas unit code below. There are code examples there to show you Pop-Up window creation, docking windows, window animation, and other methods. Pop-Up windows can be very useful, and have many ways that you can configure them for different looks and different actions. In Lesson-6 a pop-up was used for a "Splash" opening window. You can have pop-up information windows, user dialog and "Tool" windows, and docking windows.
Make a Top Level Window hTopForm := CreateWindow(topClass.lpszClassName, 'Top Level Form',
WS_CAPTION or WS_MINIMIZEBOX or WS_SYSMENU or WS_VISIBLE,
50, 6, 348, 264, 0, 0, hInstance, nil);This window is NOT owned, and will act much like the main form window, it is not hidden when the main form is minimized and can be behind (hidden) by the main form. I will call this kind of window an "UnOwned Top-Level" window. If you need to have separate forms (windows) for user interaction, each with it's own task-bar button, you can use this method. However - please Notice that this window does NOT have an Owner (Parent) window, so if the main form is closed and destroyed, it does NOT call for this Top level window to be destroyed, so you should add code that will destroy this Non-Owned top-level window when the main form is destroyed, or when the program stops execution. You can see an example of this in the TopWndU.pas unit code below, in the procedure ShowTopLevel;
Pop-Up Windows If you include the WS_POPUP style when you create a window and set the hWndParent parameter to your main Form's Handle, then you will get a window that "Pops Up" off of the owner window's client area, so it is Not a child window of the Main Form (placed on it's window area), , but is shown as a top level (Form) window on the screen (you should NOT have the WS_CHILD style flag, if you have a WS_POPUP flag bit). . you saw this done with the Splash window in lesson 6 - Brushes and Pens. - hSplash := CreateWindow(wClass.lpszClassName,
'Pop Up Form',WS_POPUP or WS_VISIBLE,
100, 50, 300, 230, hForm1, 0, hInstance, nil);All Pop-Up windows should be "Owned", so you should have a valid window handle in the hParent parameter of CreateWindow( ), , hForm1 in the code above. You can create this Pop-Up window with the "Style" flags you need, like WS_CAPTION and WS_SYSMENU, to make the type of window you want to work with. You should read the Win32 API Help for "CreateWindow" about the WS_POPUP style. All of the Forms (TForms) created with the Delphi VCL have the "Application" window as their parent (owner) and they have the WS_POPUP style. You can see code for this in the procedure ShowPopUpWnd( ) in the TopWnd.pas unit below. The windows system will keep all Pop-Up windows (templete Dialogs also) in Front of the Owner window (window Z order). You can NOT move the Owner window to be in Front of (hide) any of it's Pop-Ups. The system will also handle Pop-Up window visibility if the owner window is minimized, when the owner window is minimized the pop-ups will be hidden, and they will be made visible when the owner is restored. You should notice that the WS_POPUP form windows do NOT normally have a task bar button (but can be set to have one, see below). Also the system will "group together" the owner window and all of it's top-level Pop-Up windows, when the user input focus changes between top-level windows. If any of the Pop-Up windows are clicked or tabed to and get focus, then the whole group of windows (owner and pop-ups) goes to the top of the Z-Order for screen view.Technical Note - Top Level windows use the hMenu parameter in the CreateWindow( ) function as a "Menu Handle", , NOT as a control ID number. So if you have the WS_POPUP in your style parameter, you will need the hMenu as zero, or a Handle to a "Main Menu". If you place an ID number there (hMenu), it will NOT create any window, the CreateWindow( ) function will fail, and return zero. Pop-Up Windows in the Task bar hTaskBarPopUp := CreateWindowEx(WS_EX_APPWINDOW,fClass.lpszClassName,
'Task Bar PopUp', WS_OVERLAPPEDWINDOW or WS_POPUP or WS_VISIBLE,
68, 100, 220, 140,hForm1, Zero, hInstance, nil);You can see code for this in the procedure ShowPopUpWnd( ) in the TopWnd.pas unit below. Even though this PopUp form has a task bar button, it will be hidden when the owner is minimized, and other wize behave like other PopUps. If you need a "Separate" top-level window, that is not hidden by minimize then you will need to create an "UnOwned" top-level window.Top-Level WS_POPUP Windows Minimize - When your program with pop-up windows is running, you should be sure to "Minimize" these pop-ups to see where the small "Iconic" minimized window blocks are placed. The system will do all of the graphical display for this minimize action, a "Main Form" (first top-level created) goes to the task-bar button, a normal pop-up goes to the lower left corner of the work area on minimize, if there is a WS_EX_APPWINDOW in the ExStyle then it will go to it's task-bar button. Tool Windows hToolForm := CreateWindowEx(WS_EX_TOOLWINDOW,formClass.lpszClassName,
'Tool', WS_CAPTION or WS_SYSMENU or WS_POPUP or WS_VISIBLE,
100, 60, 228, 150, hForm1, 0, hInstance, nil);You can see some example code for this in the procedure ShowToolWnd; for the program code below. You can create these Pop-Up windows as "Helper" windows or "Tool" access windows, however, the templete Dialogs can be better for this sometimes. You should experiment and use this WS_POPUP style to see what you can do with it. IsDialogMessage( ) with more than one Top-Level while GetMessage(mainMsg,Zero,Zero,Zero) do
if not IsDialogMessage(GetActiveWindow, mainMsg) then
begin
TranslateMessage(mainMsg);
DispatchMessage(mainMsg);
end;This GetActiveWindow function will get the handle of the top-level window (in that thread) that currently has keyboad focus, just what you need! And this is a simple and effective way to have Tab-Key navigation in all top-level windows.
Docking Top-Level Windows var
pRect1: PRect;
hDwp1: Cardinal;
WM_MOVING: begin
pRect1 := PRect(LParam);
hDwp1 := BeginDeferWindowPos(2); // set for two windows movement
hDwp1 :=DeferWindowPos(hDwp1, // handle to system Defer structure
hForm1, // handle to first window to position
HWND_BOTTOM, // Z-order handle, NOT used since SWP_NOZORDER
pRect1.Left, // new horizontal position
pRect1.Top, // new vertical position
1, // width, NOT used here because of SWP_NOSIZE
1, // height
SWP_NOSIZE or SWP_NOZORDER or SWP_NOACTIVATE // flags
);
//you need to call DeferWindowPos once for each window allocated
//in the BeginDeferWindowPos(2) function
hDwp1 := DeferWindowPos(hDwp1, hPopForm, HWND_BOTTOM,
pRect1.Left-OffLeft, // use an OffSet position for the "Other" form
pRect1.Top-OffTop,
1, 1,SWP_NOSIZE or SWP_NOZORDER or SWP_NOACTIVATE);
// be sure to get the NEW hDwp1 handle from DeferWindowPos above for
EndDeferWindowPos(hDwp1);
Result := 1;
Exit; // you must exit so DefWindowProc is NOT called
end; In my code below I have two windows docked to the main form. These windows are created in the DoDocking procedure, since I want these two windows to be "Display Only" and never get keyboard focus, I have added code for this. The doMoving( ) function has the Defer Window functions, and uses an "Array of Records" with window information for all three windows that are moved together, an array called aryDock.
A System Control as a Pop-Up Window hListBox := CreateWindow('LISTBOX', 'LB 1',
WS_POPUP or WS_VSCROLL or WS_BORDER,
100, 100, 46, 70, hForm1, 0, hInstance, nil);This will allow you to "Pop Up" a separate temporary system control as a window, that can hover 'over or beside' some other control, item or object that needs user input from that pop-up control. You see this control behaviour with the Combo-Box "Drop Down" List-Box, and the "List View" control (as used in the window's Explorer) when you are changing the display name of a "List View" item, (a system "EDIT" control, "Pops Up" as a top level window over the List-View item, and is used to get user text input to re-name item). Most of the time you will need to Sub-Class this pop-up system control, also many times you will need this to be a very "Temporary" input window, that will vanish if you click on anything else on screen.
Temporary Pop-Up Windows case Msg of
WM_ACTIVATE: if LOWORD(wParam) = WA_INACTIVE then
PostMessage(hWnd, WM_CLOSE, 0, 0);This is a very useful method for many kinds of "Temporary" windows, like pop-up user information and reminder windows and quick input dialog windows. You can see methods for these in the TopWndU unit code below, a Pop-Up system List Box in the procedure DoPopListBox; and that list box's sub-classed Window Proc in the function LBFunc( );. . In the DoDocking procedure a system STATIC control is shown as a "Reminder", it is sub-classed so it is a temporary menu vanish action window.
Another way to Sub-Class a System Control var
lbClass: TWinClass;
pSysFunc: Pointer;
begin
GetClassInfo(0, 'LISTBOX', lbClass);
// use zero for system hInstance above
lbClass.hInstance := SysInit.hInstance;
// you MUST change the lbClass.hInstance from zero
lbClass.style := lbClass.style and not CS_GLOBALCLASS;
// remove Global class from the style flags
pSysFunc := lbClass.lpfnWndProc;
lbClass.lpfnWndProc := @LBFunc;
lbClass.lpszClassName := 'LB CLass';
// rename Class name from 'LISTBOX'
RegisterClass(lbClass);
hListBox := CreateWindow(lbClass.lpszClassName, 'LB 1',
WS_VISIBLE or WS_VSCROLL, 8, 8, 82, 130,
hForm, Zero, hInstance, nil);
end;For most controls that you may want to Sub-Class, you will not need to use this extra method, however, if you need to change some property of the control's "Win Class", (which the system does not allow for a system Win Class, like the Style), then you may want to try this method. You can see the some code used for this method of Sub-Class, if you look at the procedure DoPopListBox; in the TopWndU unit code below.
Invisible Message Windows
API Animation Effects for Windows DrawAnimatedRects Function - function DrawAnimatedRects( hWnd: HWND; // handle of top-level window with caption text idAni: Integer; // must be IDANI_CAPTION const rectFrom: TRect; // rectangle location for begin animation const rectTo: TRect // rectangle location for end of animation ): BOOL; // returns true if successfulThe parameters for this function are -
For top level windows, use the IDANI_CAPTION flag, and have the rectTo or rectFrom TRect as the window Rectangle for that window. This function does NOT affect the window in the hWnd parameter, it does NOT hide or show that window, or move that window. You will almost always need to show or hide this window with your own code. Code example below - var hPopForm: Integer; // hidden form window handle hButton: Integer; // button handle toRect, fromRect: TRect; begin GetWindowRect(hPopForm, toRect); GetWindowRect(hButton, fromRect); DrawAnimatedRects(hPopForm, IDANI_CAPTION, fromRect, toRect); ShowWindow(hPopForm, SW_SHOW); end;You can see an examole for this in the TopWndU.pas unit code below, in the procedure ShowTopLevel; Technical Note - The IDANI_OPEN and IDANI_CLOSE flags, used alone, would not ever work for me, it would show nothing that I could see on screen. The window's API help indicates that the hWnd has something to do with a "Clipping" area, but I saw NO evidence of this clipping. Also it says that you can use zero as the hWnd parameter, but this did NOT work for me, there was nothing visible on screen. Also, this DrawAnimatedRects( ) function did not work for me if I placed a WS_CHILD window handle in the hWnd parameter (like a button handle). If you want this animation effect for a child window, you can use your main form handle (hForm1) in the hWnd parameter, since this function has NO effect on that window and is not clipped to that window. AnimateWindow Function - function AnimateWindow( hWnd: HWND; // handle of window to show or hide dwTime: DWORD; // speed of the animation dwFlags: DWORD // flags for animation type and direction ): BOOL; // returns True if successfulThis function will return True is it is succesful. The hWnd parameter is the handle of the window that will be made visible or hidden. The dwTime parameter sets the "Speed" of the animation movement. The API help documents say this value is for microseconds to play the animation. I am not sure about that, but I do know that the lower this value is, the faster the animation effect. I would suggest you begin with a default value of 300 for dwTime, and run the animation so you can see it, and then increase or decrease this value until the speed of the effect is what you like to see. I use a dwTime value of 320 in my TopWndU.pas program code below. This movement speed seems to be consistant (look the same speed) with different operating systems (Win 98, Win 2000, Win XP), and different hardware (CPU speed, Video card). But for movement effects (not blend-fade effects), the size of the window that is animated makes a real difference in how you set this dwTime, smaller windows use smaller time of movement (the blend-fade effect is different). The dwFlags parameter is set for the kind of effect (movement, direction, fade) shown. Please note that unlike some other functions, this function has Two default settings that are NOT listed in the flag values, the first is AW_SHOW, so by default this will show a window. The next is AW_ROLL, by default this will use the "Roll" animation. This means that if you want to show the window or use the roll animation, you place nothing for these in the the dwFlags parameter, it will show the window with a roll effect. Please Note - that if the AW_ROLL or AW_SLIDE are used, you MUST have a direction flag, if there is no direction flag the function fails. Also, the direction flags are ignored for the AW_CENTER and AW_BLEND effects. AW_HIDE - Hides the window. By default, the window is shown. You must include this when you want to hide the window. AW_ACTIVATE - Activates the window when shown. Do not use this value with AW_HIDE. AW_SLIDE - Uses the slide animation. By default, roll animation is used. This flag is ignored when used with AW_CENTER. AW_HOR_POSITIVE - Animates the window with a horizontal "left to right" movement. AW_HOR_NEGATIVE - Animates the window with a horizontal "right to left" movement. AW_VER_POSITIVE - Animates the window with a vertical "top to bottom" movement. AW_VER_NEGATIVE - Animates the window with a vertical "bottom to top" movement. AW_CENTER - Makes the window appear to expand outward from the window center point when shown, or collapse inward if AW_HIDE is used. The various direction flags have no effect. Newer Effect Using this function can add some animation effects to your program for windows that you show or hide, and the system will usually produce effects that look good. For any "System" control (Button, listBox, Edit), you will see the full complete window in the effect. However, one visually "Bad" thing for these effects on "Non-System" windows, is that the system does NOT do any WM_PAINT message drawing on a "non-system" window, so any text, rectangles, polygons, or bitmaps in your WM_PAINT operation will NOT be seen on the effects window screen display, even when the animation is finished and that window is in full view. |
| Here is the code for this TopWnd program. First I have the .DPR program code in the program TopWnd; . It is like the dpr code you have seen before here at DelphiZeus. The unit code for TopWndU.pas is next. |
program TopWnd;
uses
TopWndU in 'TopWndU.pas';
{$R *.RES}
{$R Theme.RES}
// Theme.RES for XP Themes, you can leave it out
begin
if MakeForm then
DoMsgLoop;
end. |
| In this TopWnd.pas code, I create the main form in the function MakeForm: Boolean;, , and I create the main form controls (buttons) in the procedure MakeControls;. The form and the buttons on it are created with methods I have used in lessons before, so I will not say anything about that. The first four functions are used for the docking of 2 pop-up windows to the main form. The DoDocking procedure creates two pop-up windows on the left and right sides of hForm1, it fills the aryDock array with the docking information. The window's handles and position offSets are put in the array. Since I want these to be "Display Only" top-level windows, I must add code to be sure that they NEVER get keyboard focus, so I use the SetWindowPos( ) function to show them, without giving them focus. In DoDocking a user "Reminder" window is created that is a system STATIC control, this is sub-classed into the STFunc function, so it will be destroyed when it looses focus. The DoPopListBox procedure will create a window that acts like a system List-Box control, here I show you a way to make a local window that acts like a system control with another way to "Sub-Class" using the GetClassInfo( ) function. This is one of several places I have code to animate a window with the AnimateWindow( ) function. The other functions and procedures are mentioned in the explanations above, but you should be able to tell what they do from their names. You should notice that I use the same window class formClass for many of the top-level windows created here, so the Window-Proc in the MsgFunc( ) function has code for several different windows. Code for the TopWndU.pas unit - |
unit TopWndU;
{this TopWndU unit has code examples for creating differen Top-Level windows.
Like pop-up windows, tool windows, and docked windows}
interface
function MakeForm: Boolean;
// the MakeForm function creates the main form and all of it's controls
procedure DoMsgLoop;
// the DoMsgLoop procedure runs the GetMessage Loop
implementation
uses
Windows, Messages, CommCtrl, SmallUtils;
type
{TDock is the record that is used to store information used to
dock 3 windows together in the doMoving( ) function}
TDock = Record
Count, hWnd: Cardinal; // docked window Count and Handle for window
OffLeft, OffTop: Integer; // offset of the window position
end;
const
Zero = 0;
// below are ID numbers for control windows
ID_PopUpBut = 100;
ID_ToolBut = 101;
ID_TopBut = 102;
ID_DockBut = 103;
ID_SliderBut = 104;
ID_EditBut = 105;
ID_NewToolBut = 110;
ID_TaskBarBut = 111;
ID_Edit = 112;
ID_PopListBut = 113;
ID_Combo = 300;
ID_Radio1 = 301;
ID_Radio2 = 302;
ID_ListBox = 303;
dockRect: TRect = (Left: 5;Top: 24;Right: 47; Bottom: 38);
{dockRect has the size of the Invalidate area for the two docked windows}
var
formClass, topClass: TWndClass;
VarFont, hForm1, hPopUpForm, hToolForm, hTopForm, hNoShowForm, hAnimate,
hListBox: Integer;
aryDock: Array[Zero..2] of TDock;
// aryDock is used for the Docking windows in the doMoving function
fmLeft, fmTop: Integer;
// fmLeft and fmTop record the position of the main form, to be text on docked forms
ComboText: Array[Zero..2] of Char;
// ComboText is the 2 text characters shown in the combo box
pListBoxFunc: Pointer = nil;
// pListBoxFunc has the system List Box Window Proc
pStaticFunc: Pointer = nil;
// pStaticFunc has the system Static Window Proc
fromRect, toRect: TRect;
// fromRect, toRect are TRect used for window animation
// // / / / the four functions below are for the Docking windows
function doMoving(pRect: PRect): Boolean;
var
hDwp1, i: Integer;
begin
{this function is called by the WM_MOVING message of hForm1.
if the aryDock[Zero].Count is zero then the windows are NOT docked}
if aryDock[Zero].Count <> Zero then
begin
Result := True;
hDwp1 := BeginDeferWindowPos(3);
{ to move windows in the same "refresh" paint, you can use
the BeginDeferWindowPos( ) function }
for i := Zero to aryDock[Zero].Count-1 do
begin
{the pRect comes from the WM_MOVING message, and has the new hForm1 position.
You must call DeferWindowPos( ) for each window to be moved. Be sure to
update the hDwp1 with the result of DeferWindowPos}
with aryDock[i] do
hDwp1 := DeferWindowPos(hDwp1, hWnd, HWND_BOTTOM,
pRect.Left+OffLeft,
pRect.Top+OffTop, 1, 1,
SWP_NOSIZE or SWP_NOZORDER or SWP_NOACTIVATE);
end;
EndDeferWindowPos(hDwp1);
{ when the EndDeferWindowPos( ) is called, the windows are supposed
to be moved together , and repainted in the same cycle}
fmLeft := pRect.Left;
fmTop := pRect.Top;
// fmLeft and fmTop are used to display X an Y position on docked windows
for i := 1 to 2 do
InvalidateRect(aryDock[i].hWnd, @dockRect, True);
end else
Result := False;
{I use the result of this function in the WM_MOVING message, in order to have
the EndDeferWindowPos function work correctly, you MUST BLOCK the WM_MOVING message}
end;
function DockMsgFunc(hWnd,Msg,wParam,lParam:Integer): Integer; stdcall;
var
PaintS: TPaintStruct;
aStr: String;
pPos: PChar;
begin
{this is the Window Proc for the two docked windows}
Result := Zero;
case Msg of
{If you want to have "Display Only" windows you must get and process the
WM_ACTIVATE and WM_MOUSEACTIVATE messages. I want the two docked windows
to be for display of user information ONLY, these windows should NEVER have
keyboard focus. If they are mouse clicked or tabed to, then they must set
the keyboard focus to the main form hForm1}
WM_ACTIVATE:
if (LOWORD(wParam) = WA_ACTIVE) or (LOWORD(wParam) = WA_CLICKACTIVE)then
begin
{test to see if the WM_ACTIVATE message is for WA_ACTIVE, and then call for
focus to go to hForm1 with the SetActiveWindow}
SetActiveWindow(hForm1);
Exit; // be sure to exit and block the WM_ACTIVATE message
end;
WM_PAINT: begin
//for user information I display the Top and Left position of hForm1
BeginPaint(hWnd, PaintS);
SetBkMode(PaintS.hDC, TRANSPARENT);
SelectObject(PaintS.hDC, VarFont);
if hWnd = Integer(aryDock[1].hWnd) then
begin
pPos := 'Left';
aStr := Int2Str(fmLeft);
end else
begin
pPos := 'Top ';
aStr := Int2Str(fmTop);
end;
TextOut(PaintS.hDC, 6, 8, pPos, 4);
TextOut(PaintS.hDC, 6, 24, PChar(aStr), Length(aStr));
EndPaint(hWnd, PaintS);
Exit;
end;
WM_MOUSEACTIVATE: begin
{if you set the Result to MA_NOACTIVATEANDEAT, then the mouse click does
NOT set the focus}
Result := MA_NOACTIVATEANDEAT;
SetActiveWindow(hForm1); // you still have to set hForm1 to keyboard focus
Exit; // be sure to exit and block the WM_ACTIVATE message
end;
end;
Result := DefWindowProc(hWnd,Msg,wParam,lParam);
end;
function STFunc(hWnd,iMsg,wParam,lParam: Integer):Integer; stdcall;
begin
{ this is the Window Proc for the pop-up STATIC control.
this needs to be a "Temporary" window, so I close it when it looses focus}
if (iMsg = WM_ACTIVATE) and (LOWORD(wParam) = WA_INACTIVE) then
PostMessage(hWnd, WM_CLOSE,Zero,Zero);
Result := CallWindowProc(pStaticFunc,hWnd,iMsg,wParam,lParam);
end;
procedure DoDocking;
var
dockClass: TWndClass;
fmRect: TRect;
i, hInfoForm: Integer;
mMsg: TMSG;
begin
{the DoDocking procedure will create or destroy the two docking windows.
First I test the aryDock[Zero].Count for zero, if zero then no dock windows exist}
if aryDock[Zero].Count = Zero then
SetDlgItemText(hForm1, ID_DockBut, 'Hide Dock Forms')
else begin
// if count is more than zero then I need to destroy the windows
SetDlgItemText(hForm1, ID_DockBut, 'Show Dock Forms');
for i := 1 to 2 do
DestroyWindow(aryDock[i].hWnd);
UnRegisterClass('Docker Class',hInstance);
aryDock[Zero].Count := Zero; // set count to zero for no docking
Exit;
end;
// I now Register the Class and create two dock windows
ZeroMemory(@dockClass, SizeOf(dockClass));
with dockClass do
begin
Style := CS_PARENTDC or CS_BYTEALIGNCLIENT;;
hInstance := SysInit.hInstance;
hIcon := LoadIcon(hInstance,'MAINICON');
lpfnWndProc := @DockMsgFunc;
hbrBackground := GetStockObject(WHITE_BRUSH);
lpszClassName := 'Docker Class';
hCursor := LoadCursor(Zero,IDC_ARROW);
end;
if RegisterClass(dockClass) = Zero then
begin
MessageBox(hForm1,'ERROR - Dock Class could NOT be registered','No Class',MB_ICONERROR);
Exit;
end;
GetWindowRect(hForm1,fmRect);
// I use the position of hForm1 in fmRect to set where the two dock windows go
fmLeft := fmRect.Left;
fmTop := fmRect.Top;
{the aryDock has three elements, the first one is used for hForm1, index 1 and 2
are used for the 2 dock windows, their handles and position OffSets}
aryDock[1].hWnd := CreateWindow(dockClass.lpszClassName, 'DFL',
WS_POPUP or WS_BORDER, fmRect.Left-48,fmRect.Top + 37,
48, 48,hForm1, Zero, hInstance, nil);
// do NOT create these windows with the WS_VISIBLE, you do NOT want them to get focus
aryDock[2].hWnd := CreateWindow(dockClass.lpszClassName, 'DFR',
WS_POPUP or WS_BORDER, fmRect.Right,fmRect.Top + 87,
48, 100,hForm1, Zero, hInstance, nil);
aryDock[1].OffLeft := -48;
aryDock[1].OffTop := 37;
// set the offSets to the relative position to hForm1
aryDock[2].OffLeft := fmRect.Right-fmRect.Left;
aryDock[2].OffTop := 87;
aryDock[Zero].Count := 3; // set count to a non-zero to have the docking executed
{these 2 docked windows are for user info ONLY, they should never get keyboard focus.
So I use the SetWindowPos( ) to show these window, and they are not activated}
for i := 1 to 2 do
SetWindowPos(aryDock[i].hWnd, hForm1, 1, 1,1,1,
SWP_NOSIZE or SWP_NOMOVE or SWP_NOACTIVATE or SWP_NOCOPYBITS or SWP_SHOWWINDOW);
while PeekMessage(mMsg, Zero, Zero, Zero, PM_REMOVE) do
DispatchMessage(mMsg); // allows painting of the dock windows
// the next code will make a temporary pop-up STATIC information window
GetWindowRect(GetDlgItem(hForm1, ID_DockBut),fmRect);
hInfoForm := CreateWindow('STATIC', 'Move Main Form to'#10'see Docking effect',
WS_POPUP or WS_BORDER or SS_CENTER, fmRect.Left,fmRect.Bottom,
fmRect.Right-fmRect.Left, 31, hForm1, Zero, hInstance, nil);
SendMessage(hInfoForm,WM_SETFONT,VarFont,Zero);
pStaticFunc := Pointer(SetWindowLong(hInfoForm, GWL_WNDPROC, Integer(@STFunc)));
// subClass this static control to get the WM_ACTIVATE message to close it
AnimateWindow(hInfoForm, 240, AW_SLIDE or AW_VER_POSITIVE or AW_ACTIVATE);
end;
// // / / / the four functions above are for the Docking windows
function LBFunc(hWnd,iMsg,wParam,lParam: Integer):Integer; stdcall;
var
Index1: Integer;
aText: Array[Zero..7] of Char;
procedure ChangeEdit;
begin
// the ChangeEdit procedure gets text from list box, and puts it in the edit
Index1 := CallWindowProc(pListBoxFunc,hWnd,LB_GETCURSEL,Zero,Zero);
if not (Index1 in [Zero..10]) then Index1 := Zero;
aText := '100'#0;
CallWindowProc(pListBoxFunc,hWnd,LB_GETTEXT,Index1, Integer(@aText));
SetDlgItemText(hTopForm, ID_Edit, aText);
SetFocus(GetDlgItem(hTopForm, ID_Edit)); //will close this list box
end;
begin
// this is the Window Proc for the Pop-Up List Box
case iMsg of
WM_ACTIVATE: if LOWORD(wParam) = WA_INACTIVE then PostMessage(hWnd, WM_CLOSE,Zero,Zero);
{ I want this list box to have the "Menu Effect" and Close, if it loses
keyboard focus. I test for WA_INACTIVE in the WM_ACTIVATE message
(happens when keyboard focus is lost) and poat the WM_CLOSE message}
{I also want this List Box to change the edit and close if the user
clicks on a section, like a combo Box List Box does. This is
done in the WM_LBUTTONUP with ChangeEdit procedure}
WM_LBUTTONUP: ChangeEdit;
WM_KEYDOWN: if wParam = VK_RETURN then ChangeEdit; // close with Return Key
WM_GETDLGCODE: begin
// needs all keys for VK_RETURN above
Result := DLGC_WANTALLKEYS;
Exit;
end;
end;
Result := CallWindowProc(pListBoxFunc,hWnd,iMsg,wParam,lParam);
end;
procedure DoPopListBox;
var
fRect: TRect;
c: Cardinal;
lbClass: TWndClass;
begin
{this procedure will create a Pop-Up List Box control, that is NOT a Child
control on a form window, I show you a method for system control creation
that is another way to "Sub-Class" a system control using the GetClassInfo( )
function to get the system Window Proc and Class parameters}
GetWindowRect(GetDlgItem(hTopForm, ID_Edit), fRect);
{below is another way to "Sub-Class" a system control, this is the method the
Delphi VCL uses to subclass it's system controls. The sub-class method below
makes a control that is NOT a system control window, but will fuction and act
just like a system control. You have a few more options in changing this control
than you would have with a system control}
if pListBoxFunc = nil then // test to see if the class has already been registred
begin
{first use GetClassInfo( ) to get the win Class properties for the
system control, 'LISTBOX' in this case, into lbClass}
ZeroMemory(@lbClass, SizeOf(lbClass));
if not GetClassInfo(Zero, 'LISTBOX', lbClass) then
begin
MessageBox(hTopForm, 'ERROR - GetClassInfo', 'ERROR', MB_ICONERROR);
Exit;
end;
{ unlike a system control, you CAN change some of the "Class" parameters. Next
you MUST change the elements of the lbClass to fit a Class for this hInstance.
You can NOT change the Class "Style" in system controls,
but you can change this lbClass.style}
lbClass.hInstance := hInstance; // ALWAYS change the hInstance
lbClass.Style := lbClass.style and not (CS_CLASSDC or CS_PARENTDC or CS_GLOBALCLASS);
// you MUST remove style flages that can cause problems or are un-needed
pListBoxFunc := lbClass.lpfnWndProc;
// record the system window proc in the pListBoxFunc pointer variable for the LBFunc
lbClass.lpfnWndProc := @LBFunc;
// now set the lpfnWndProc to your local Window Proc function
lbClass.lpszClassName := 'LB CLass';
{ be sure to change the Class name from 'LISTBOX' to your own name, or you will
have problems with the standard control class name of 'LISTBOX' later}
lbClass.hCursor := LoadCursor(Zero,IDC_HAND);
// I have changed the class Cursor to the system Hand cursor
RegisterClass(lbClass);
end;
hListBox := CreateWindow('LB CLass', 'LB 1', WS_POPUP or WS_VSCROLL or WS_BORDER,
fRect.Left, fRect.Bottom, 46, 70, hTopForm, Zero, hInstance, nil);
// remember, you can NOT have a control ID number in the hMenu parameter of Pop-Up
// code below could be used to make a pop-up list box, but this is a system window -
{hListBox := CreateWindow('LISTBOX', 'LB 1', WS_POPUP or WS_VSCROLL or WS_BORDER,
fRect.Left, fRect.Bottom,46,70,hTopForm,Zero,hInstance,nil);
pListBoxFunc := Pointer(SetWindowLong(hListBox, GWL_WNDPROC, Integer(@LBFunc))); }
if hListBox = Zero then
begin
MessageBox(hTopForm, 'ERROR - CreateWindow for PopUp List Box FAILED',
'ERROR List Box Create', MB_ICONERROR);
Exit;
end;
SendMessage(hListBox,WM_SETFONT,VarFont,Zero);
// next items are added to list box
for c := 1 to 6 do
SendMessage(hListBox, LB_ADDSTRING, Zero, Integer(PChar(Int2Str(c*100))));
AnimateWindow(hListBox, 320, AW_SLIDE or AW_VER_POSITIVE or AW_ACTIVATE);
{AnimateWindow is used by the system to show ComboBox, drop down list boxs.
It can give your program some graphical "Movement" effects with one line of code}
end;
function TopMsgFunc(hWnd,Msg,wParam,lParam:Integer):Integer; stdcall;
var
PaintS: TPaintStruct;
begin
// this is the Window Proc function for the hTopForm
case Msg of
WM_CLOSE: begin
GetWindowRect(hTopForm, fromRect);
GetWindowRect(GetDlgItem(hForm1, ID_TopBut), toRect);
ShowWindow(hTopForm, SW_HIDE); // hide window before calling DrawAnimatedRects
RedrawWindow(Zero, @fromRect, Zero, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN);
Sleep(0);
// system may not refresh screen until After the DrawAnimatedRects, I call RedrawWindow
DrawAnimatedRects(hTopForm, IDANI_CAPTION, fromRect, toRect);
{DrawAnimatedRects( ) is the animation effect used by system to do the main
window minimize and restore movement effects}
end;
WM_PAINT:
begin
BeginPaint(hWnd, PaintS);
SetBkMode(PaintS.hDC, TRANSPARENT);
SelectObject(PaintS.hDC, VarFont);
TextOut(PaintS.hDC, 10, 10, 'This is a Top Level Window', 26);
TextOut(PaintS.hDC, 10, 30, 'Some text to read here', 22);
EndPaint(hWnd, PaintS);
end;
WM_COMMAND: case LOWORD(wParam) of
IDOK: PostMessage(hWnd,WM_CLOSE,Zero,Zero);
IDCancel: begin
MessageBox(hTopForm, 'A Message',
'Read Me', MB_ICONINFORMATION);
end;
ID_PopListBut: DoPopListBox;
end;
end; // Msg case
Result := DefWindowProc(hWnd,Msg,wParam,lParam);
end;
procedure ShowTopLevel;
var
hBut: Cardinal;
begin
// this procedure will make an independent (not owned) top level window
if IsWindow(hTopForm) then Exit;
hTopForm := CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, topClass.lpszClassName,
'Top Level Un-Owned Form', WS_CAPTION or WS_MINIMIZEBOX or WS_SYSMENU,
50, 6, 348, 264, Zero, Zero, hInstance, nil);
// notice that the hParent parameter is zero, like the main form
CreateWindow('BUTTON', 'Show Msg',
WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or WS_TABSTOP,
8,48,90,22,hTopForm,IDCancel,hInstance,nil);
{the Edit and hBut below (in some ways) will act like a system combo box.
If the button is clicked a drop down list box will slide into view below
the edit control. If you click the listbox that selected value will
be placed in the edit control, and list box vanishes}
SendMessage(CreateWindowEx(WS_EX_CLIENTEDGE,'EDIT','100',
WS_VISIBLE or WS_CHILD or WS_TABSTOP,
250,34,46,21,hTopForm,ID_Edit,hInstance,nil),
WM_SETFONT, VarFont, Zero);
hBut := CreateWindow('BUTTON', 'o',
WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or BS_BITMAP or WS_TABSTOP,
296,35,18,18, hTopForm,ID_PopListBut,hInstance,nil);
SendMessage(hBut,BM_SETIMAGE, IMAGE_BITMAP,
LoadBitmap(Zero,MAKEINTRESOURCE(OBM_DNARROWD)));
SetFocus(CreateWindow('BUTTON', 'Close Me',
WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or WS_BORDER or WS_TABSTOP,
160,6,80,24,hTopForm,IDOk,hInstance,nil));
{below I create a window as a child of the fTopForm, but this window has
the WS_OVERLAPPEDWINDOW style, so it has a Caption and Title bar, so it looks
like a top level window, but it is NOT set up as a MDI, so when it has focus
the title bar stays grey}
CreateWindow(formClass.lpszClassName, 'Chlid Form',
WS_OVERLAPPEDWINDOW or WS_CHILD or WS_VISIBLE or WS_TABSTOP or WS_CLIPSIBLINGS,
28,60,200,120,hTopForm,33,hInstance,nil);
{code below is to have an animated movement graphical effect}
GetWindowRect(hTopForm, toRect);
GetWindowRect(GetDlgItem(hForm1, ID_TopBut), fromRect);
DrawAnimatedRects(hTopForm, IDANI_CAPTION, fromRect, toRect);
// DrawAnimatedRects does NOT affect the hTopForm, so you must call ShowWindow
ShowWindow(hTopForm, SW_SHOW);
end;
procedure ShowPopUpWnd(TaskBar: Boolean = False);
begin
// the TaskBar parameter is set to True for a pop-up Form with a Taskbar button
if TaskBar then
begin
{here I have combined three API functions, SetFocus, CreateWindow and CreateWindowEx.
I do NOT record ANY windows handles, for the button or the form}
SetFocus(CreateWindow('BUTTON', 'Close Me',
WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or WS_BORDER or WS_TABSTOP,
68,80,80,24,
CreateWindowEx(WS_EX_APPWINDOW,formClass.lpszClassName,
'Pop Up in Task Bar', WS_OVERLAPPEDWINDOW or WS_POPUP or WS_VISIBLE,
(GetSystemMetrics(SM_CXSCREEN) div 2)-110,
(GetSystemMetrics(SM_CYSCREEN) div 2)-5,
220, 140,hForm1, Zero, hInstance, nil),
IDOk,hInstance,nil));
{ in the CreateWindowEx( ) function above I have the WS_EX_APPWINDOW flag,
using this flag will give this PopUp a button in the TaskBar}
Exit;
end;
if IsWindow(hPopUpForm) then Exit; // allow only one Popup to be created
{in the CreateWindow function below, I have the WS_POPUP flag, and the parent as
hForm1, with the WS_POPUP the window will be a Top-Level window (Form), not a
Child window on the parent client area}
hPopUpForm := CreateWindowEx(Zero, formClass.lpszClassName, 'Pop Up Window',
WS_OVERLAPPEDWINDOW or WS_POPUP or WS_VISIBLE,
(GetSystemMetrics(SM_CXSCREEN) div 2)-110,
(GetSystemMetrics(SM_CYSCREEN) div 2)-175,
220, 170,hForm1, Zero, hInstance, nil);
SendMessage(CreateWindow('BUTTON', 'PopUp Window'#10'with Task Bar',
WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or WS_TABSTOP or BS_MULTILINE,
12,30,88,36,hPopUpForm,ID_TaskBarBut,hInstance,nil),
WM_SETFONT,VarFont,Zero);
SetFocus(CreateWindow('BUTTON', 'Close Me',
WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or WS_BORDER or WS_TABSTOP,
68,110,80,24,hPopUpForm,IDOk,hInstance,nil));
end;
procedure ChangeButton(pCaption: PChar = nil);
begin
if pCaption = nil then
pCaption := 'Show the'#10'Invisible Tool';
SetDlgItemText(hToolForm, ID_NewToolBut, pCaption);
end;
procedure MakeInvisibleTool;
begin
{there are times you may need a top level window created to just process
messages or be a container for some system object, the user should not see
or use this window, if you set the width and height of a Tool Window to zero
it will be visible to the system, but the user can not see it}
{if you do not need the window to be visible in the system settings, you can have
a window with a width and height, and just set it as hidden}
{first I test to see if the hNoShowForm exists with IsWindow , if it does not exist
I create it. If it exists then I show or hide it}
if IsWindow(hNoShowForm) then
begin
if GetWindowLong(hNoShowForm, GWL_USERDATA) = 2 then
begin
if IsWindowVisible(hNoShowForm) then
begin
ShowWindow(hNoShowForm, SW_HIDE);
ChangeButton;
end else
begin
ShowWindow(hNoShowForm, SW_SHOW);
ChangeButton('Hide the'#10'Invisible Tool');
end;
end else
begin
{if the width and height are zero, I change them to make the window visible}
SetWindowPos(hNoShowForm, Zero, 1, 1,200, 100, SWP_NOMOVE or SWP_NOCOPYBITS or
SWP_NOREPOSITION or SWP_NOZORDER or SWP_SHOWWINDOW);
SetWindowLong(hNoShowForm, GWL_USERDATA, 2);
ChangeButton('Hide the'#10'Invisible Tool');
end;
Exit;
end;
hNoShowForm := CreateWindowEx(WS_EX_TOOLWINDOW,formClass.lpszClassName, 'Invisible',
WS_CAPTION or WS_SYSMENU {or WS_POPUP} or WS_VISIBLE,
10, 10, Zero, Zero, Zero{hForm1}, Zero, hInstance, nil);
{to have an invisible window you need the WS_EX_TOOLWINDOW flag with the width
and height set to zero}
if hNoShowForm <> Zero then
begin
SetWindowLong(hNoShowForm, GWL_USERDATA, 1);
ChangeButton;
InvalidateRect(hForm1, nil, True);
// hForm1 has text on it to tell you that the invisible form exists
end;
end;
procedure ShowToolWnd;
var
pButCap: PChar;
begin
// this procedure makes forms with the smaller caption bar for tool windows
if IsWindow(hToolForm) then Exit;
// you place the WS_EX_TOOLWINDOW flag in the style Ex parameter for a tool window
hToolForm := CreateWindowEx(WS_EX_TOOLWINDOW,formClass.lpszClassName, 'Tool Form',
WS_CAPTION or WS_SYSMENU or WS_POPUP or WS_VISIBLE,
(GetSystemMetrics(SM_CXSCREEN) div 2)-110,
(GetSystemMetrics(SM_CYSCREEN) div 2)-75,
228, 150,hForm1, Zero, hInstance, nil);
if IsWindow(hNoShowForm) then
pButCap := 'Show-Hide the'#10'Invisible Tool'
else
pButCap := 'Invisible Top'#10'Tool Window';
SendMessage(CreateWindow('BUTTON', pButCap,
WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or WS_TABSTOP or BS_MULTILINE,
64,30,88,36, hToolForm, ID_NewToolBut, hInstance, nil),
WM_SETFONT,VarFont,Zero);
SetFocus(CreateWindow('BUTTON', 'Close Me',
WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or WS_BORDER or WS_TABSTOP,
68,90,80,24, hToolForm, IDOk, hInstance, nil));
end;
procedure DoAnimate;
var
fRect: TRect;
c, hCombo: Cardinal;
begin
// this procedure will create and animate a temporary dialog window
if IsWindow(hAnimate) then Exit;
GetWindowRect(hForm1, fRect);
// the hAmimate wildow will be placed on the left edge of hForm1
hAnimate := CreateWindowEx(Zero, formClass.lpszClassName, 'Animate',
WS_POPUP or WS_DLGFRAME, fRect.Left-49, fRect.Bottom -120,
50, 120, hForm1, Zero, hInstance, nil);
// for a dialog window I have a combo box and radio buttons
hCombo := CreateWindow('COMBOBOX','cb1',WS_VISIBLE or WS_CHILD or
CBS_DROPDOWNLIST or WS_CLIPSIBLINGS or WS_TABSTOP or WS_VSCROLL,
4,20,38,110,hAnimate,ID_Combo,hInstance,nil);
SendMessage(hCombo,WM_SETFONT,VarFont, Zero);
for c := 1 to 6 do
SendMessage(hCombo,CB_ADDSTRING, Zero, Integer(PChar(Int2Str(c*10))));
SendMessage(hCombo, CB_SETCURSEL, Zero, Zero);
ComboText := '10'#0; // ComboText is used to store the text for the hAmimate Paint
SendMessage(CreateWindow('BUTTON','Yes',
WS_VISIBLE or WS_CHILD or BS_AUTORADIOBUTTON or BS_TEXT or WS_TABSTOP or WS_GROUP,
2, 44, 40, 25, hAnimate, ID_Radio1, hInstance, nil),
WM_SETFONT, VarFont, Zero);
SendMessage(CreateWindow('BUTTON','No',
WS_VISIBLE or WS_CHILD or BS_AUTORADIOBUTTON or BS_TEXT,
2, 64, 40, 25, hAnimate, ID_Radio2, hInstance, nil),
WM_SETFONT, VarFont, Zero);
SendDlgItemMessage(hAnimate, ID_Radio1, BM_SETCHECK, BST_CHECKED, Zero);
CreateWindow('BUTTON','O K', WS_VISIBLE or WS_CHILD or BS_TEXT,
4, 90, 36, 21, hAnimate, IDOK, hInstance, nil);
AnimateWindow(hAnimate, 320, AW_HOR_NEGATIVE or AW_ACTIVATE);
// AnimateWindow will roll out this window
SetFocus(hCombo);
>// AnimateWindow function does NOT call the WM_PAINT for non-system control windows
// to show the text draw in the WM_PAINT message, you will need to invalidate, below
//InvalidateRect(hAnimate, nil, False);
end;
function MsgFunc(hWnd,iMsg,wParam,lParam:Integer):Integer; stdcall;
var
PaintS: TPaintStruct;
aFlags: Cardinal;
begin
{this Window Proc fuction is used by several different top level windows.
So I have several tests for the hWnd parameter, to see which window to code for.
The main form hForm1, and the "Pop-Up windows and the tool windows and hAmimate
all use this Window Proc}
Result := Zero;
case iMsg of
WM_DESTROY: if hWnd = hForm1 then PostQuitMessage(Zero)
else if hWnd = hToolForm then hToolForm := Zero
else if hWnd = hNoShowForm then
begin
if IsWindow(hToolForm) then
ChangeButton('Invisible Top'#10'Tool Window');
InvalidateRect(hForm1, nil, True);
end;
WM_ACTIVATE: if (hWnd = hAnimate) and (LOWORD(wParam) = WA_INACTIVE) then // $0006;
PostMessage(hWnd, WM_CLOSE,0,0); // hAnimate closes when it loses focus
WM_PAINT: if hWnd <> hNoShowForm then
begin
BeginPaint(hWnd, PaintS);
SetBkMode(PaintS.hDC, TRANSPARENT);
if hWnd = hAnimate then
begin
SetTextColor(PaintS.hDC, $FF);
// The hAmimate form does NOT paint this text when it is animated
TextOut(PaintS.hDC,13,2,ComboText,2);
end else
if hWnd = hForm1 then
begin
TextOut(PaintS.hDC, 23, 2, 'Buttons to Create Forms', 23);
if IsWindow(hNoShowForm) then
begin
SelectObject(PaintS.hDC, VarFont);
TextOut(PaintS.hDC, 30, 162, 'Invisible Tool Window Exists', 28);
end;
end else
if (hWnd = hToolForm) then
begin
SelectObject(PaintS.hDC, VarFont);
TextOut(PaintS.hDC, 38, 2, 'Place User Tool Buttons Below', 29);
end else
begin
SelectObject(PaintS.hDC, VarFont);
TextOut(PaintS.hDC, 1, 1, 'Minimize this Form to see where it goes', 39);
end;
EndPaint(hWnd, PaintS);
Exit;
end;
WM_CLOSE: if (hWnd = hForm1) and
IsWindow(hTopForm) then
SendMessage(hTopForm, WM_CLOSE, Zero,Zero);
WM_COMMAND:
case LOWORD(wParam) of
IDOK: PostMessage(hWnd,WM_CLOSE,Zero,Zero);
ID_PopUpBut: ShowPopUpWnd;
ID_ToolBut: ShowToolWnd;
ID_TopBut: ShowTopLevel;
ID_DockBut: DoDocking;
ID_NewToolBut: MakeInvisibleTool;
ID_TaskBarBut: ShowPopUpWnd(True);
ID_SliderBut: DoAnimate;
ID_EditBut: begin // button to slide out the EDIT control
PaintS.hDC := GetDlgItem(hWnd, ID_Edit);
if IsWindowVisible(PaintS.hDC) then // test to see is edit is visible
begin
// set flags for hide or show
aFlags := AW_SLIDE or AW_HOR_NEGATIVE or AW_HIDE;
SetDlgItemText(hWnd, ID_EditBut, '>');
end else
begin
aFlags := AW_SLIDE or AW_HOR_POSITIVE;
SetDlgItemText(hWnd, ID_EditBut, 'X');
end;
AnimateWindow(PaintS.hDC, 400, aFlags);
if aFlags and AW_HIDE = Zero then
SetFocus(PaintS.hDC); // set focus if showing
end;
ID_Combo: if (HIWORD(wParam) = CBN_SELENDOK) and
(SendMessage(LParam, CB_GETLBTEXT, SendMessage(LParam,
CB_GETCURSEL, Zero, Zero), Cardinal(@ComboText)) <> CB_ERR) then
begin
PaintS.hDC := GetDC(hWnd);
SetBkColor(PaintS.hDC, GetSysColor(COLOR_3DFACE));
SetTextColor(PaintS.hDC, $FF);
TextOut(PaintS.hDC,13,2,ComboText,2);
ReleaseDC(hWnd, PaintS.hDC);
end;
end;
WM_CTLCOLORSTATIC: if hWnd = hForm1 then
begin
//The pop-up Static will be white with red text
SetBkColor(wParam,$FFFFFF);
SetTextColor(wParam,$D6);
Result := GetStockObject(WHITE_BRUSH);
Exit;
end;
WM_MOVING: if (hWnd = hForm1) and doMoving(PRect(LParam)) then
begin
// test docking with doMoving, and block the DefWindowProc
Result := 1;
Exit;
end;
end;
Result := DefWindowProc(hWnd,iMsg,wParam,lParam);
end;
procedure MakeButtons;
const
ButText: Array[Zero..3] of PChar =
('PopUp Form', 'Tool Form', 'Show Top Form','Show Dock Forms');
var
i: Integer;
begin
// this procedure makes all the buttons on hForm1
CreateWindow('BUTTON', '>', WS_VISIBLE or WS_CHILD or WS_TABSTOP,
1, 3, 18, 21, hForm1, ID_EditBut, hInstance, nil);
for i := Zero to High(ButText) do
SendMessage(CreateWindow('BUTTON', ButText[i],
WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or WS_TABSTOP,
47,30+(i*33),104,26,hForm1,ID_PopUpBut+i,hInstance,nil),
WM_SETFONT,VarFont,Zero);
CreateWindow('BUTTON', '<', WS_VISIBLE or WS_CHILD or WS_TABSTOP,
1, 94, 15, 76, hForm1, ID_SliderBut, hInstance, nil);
end;
procedure MakeControls;
begin
hNoShowForm := Zero;
hTopForm := Zero;
// set the Count in the aryDock[Zero] to zero so there is no docking
aryDock[Zero].Count := Zero;
aryDock[Zero].hWnd := hForm1;
// this first aryDock if for the main form and has zero OffSets
aryDock[Zero].OffLeft := Zero;
aryDock[Zero].OffTop := Zero;
// topClass is used for the "Top" window (form)
with topClass do
begin
Style := CS_PARENTDC or CS_BYTEALIGNCLIENT;
hInstance := SysInit.hInstance;
hIcon := LoadIcon(Zero, IDI_EXCLAMATION);
lpfnWndProc := @TopMsgFunc;
hbrBackground := formClass.hbrBackground;
lpszClassName := 'Top Class';
hCursor := formClass.hCursor;
cbClsExtra := Zero;
cbWndExtra := Zero;
lpszMenuName := nil;
end;
RegisterClass(topClass);
MakeButtons;
SendMessage(CreateWindowEx(WS_EX_CLIENTEDGE,'EDIT','Sliding Edit',
WS_CHILD, 21, 3, 80, 21,hForm1,ID_Edit,hInstance,nil),
WM_SETFONT, VarFont, Zero);
// the Edit above is created hidden, and slides out when ID_EditBut is clicked
end;
function MakeForm: Boolean;
begin
// main form creation
ZeroMemory(@formClass, SizeOf(formClass));
with formClass do
begin
Style := CS_PARENTDC or CS_BYTEALIGNCLIENT;
hInstance := SysInit.hInstance;
hIcon := LoadIcon(hInstance,'MAINICON');
lpfnWndProc := @MsgFunc;
hbrBackground := COLOR_BTNFACE+1;
lpszClassName := 'Forms Class';
hCursor := LoadCursor(Zero,IDC_ARROW);
end;
RegisterClass(formClass);
hForm1 := CreateWindow(formClass.lpszClassName, 'Multi Forms',
WS_CAPTION or WS_MINIMIZEBOX or WS_SYSMENU or WS_VISIBLE,
(GetSystemMetrics(SM_CXSCREEN) div 2)-102,
(GetSystemMetrics(SM_CYSCREEN) div 2)-140,
204, 216, Zero, Zero, hInstance, nil);
Result := hForm1 <> Zero;
if Result then
begin
InitCommonControls;
MakeControls;
end;
end;
procedure DoMsgLoop;
var
MainMsg: TMSG;
begin
{You should Notice that the IsDialogMessage( ) now has the
GetActiveWindow function for the hWnd paramater. You need this because
there are many top-level windows in this program}
while GetMessage(mainMsg,Zero,Zero,Zero) do
if not IsDialogMessage(GetActiveWindow, mainMsg) then
begin
TranslateMessage(mainMsg);
DispatchMessage(mainMsg);
end;
// be sure to Destroy un-owned windows with DestroyWindow
DestroyWindow(hNoShowForm);
DestroyWindow(hTopForm);
end;
initialization
VarFont := GetStockObject(ANSI_VAR_FONT);
end. |
I hope you can take time to try and experiment with the different methods presented in the code above.
Having pop-up windows can give you a more flexible way to give an get information for the user.
You may have seen in some of your computer programs different ways to use extra windows, not on the client area.
![]()
Next Page
The next page shows you how to create pop-up windows that are "Modal", and prevent
user from using any program window except the modal window, and how to block code progression.
16A. Modal Windows

H O M E 