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