Home
DelphiZeus
9. Non-Modal Dialogs,
DIALOGEX Resource Templetes
and Scroll Bars

Home



DIALOGEX Resource Templetes
The DIALOGEX Resource Creation Templete has more Options to offer you for your Dialog templetes. The DIALOG Resource-Type that was used in Lesson eight was left over from windows 16-bit, this DIALOGEX is a more recent and more versital way to do templete dialogs. But most of the time the DIALOG resource templete will have enough to produce the dialog, since the EX additions may not be needed. The DIALOGEX allows the following additions to the DIALOG-
  • Help IDs on the dialog itself as well as on controls within the dialog.
  • Use of the EXSTYLE statement for the dialog itself as well as on controls within the dialog.
  • Font weight and italic settings for the font to be used in the dialog.
  • Control-specific data for controls within the dialog.
  • More Style and ExStyle options are recognized for controls.
  • Use of the BEDIT, IEDIT, and HEDIT predefined system class names.
The BEDIT, IEDIT, and HEDIT are special EDIT classes that are used for hand-writting input and recogition, and I will Not deal with those here.

I could not see much of an advantage for using DIALOGEX instead of DIALOG in the resource templete, except if you want to add the helpID for the Caption Help button in the Dialog window. The EX Styles do not seem very useful for a dialog box window, so if you do not need the help or Ex styles, then you may as well use the DIALOG templete creation.



The syntax and coding methods for the DIALOGEX is very much like the DIALOG Resource-Type creation code. And looks like this -
nameID DIALOGEX [ load-mem] x, y, width, height [ , helpID]
[ optional-statements]
  BEGIN
  control creation statement
  control creation statement
  END
The paramrters for the DIALOGEX are listed below -

Parameters

nameID - Identifies the dialog box. This is either a unique text character name or a unique 16-bit unsigned integer value in the range 1 to 65,535.

load-mem - Optional, , Specifies loading and memory attributes for the resource. The only value that you can use here is DISCARDABLE. Most Dialogs use the DISCARDABLE, because it tells the system not to save the templete memory when it is not used. But most templetes use very little memory, so there is not much advantage to having DISCARDABLE. I will use it because you should always free memory that is not being used.

x - Specifies the location on the screen of the left side of the dialog, in dialog units.

y - Specifies the location on the screen of the top of the dialog, in dialog units.

width - Specifies the width of the dialog, in dialog units.

height - Specifies the height of the dialog, in dialog units.

helpID - Optional, , Specifies a numeric expression indicating the ID used to identify the dialog during WM_HELP processing.

optional-statements - Specifies options for the dialog box. Some of these Options are Not used by the windows system, and some are used for methods I will not cover in this lesson, The following Options are listed here but I will not use or talk about them - (CHARACTERISTICS, CLASS, LANGUAGE, MENU, VERSION).
You can use zero or more of the following option statements -

STYLE -Specifies the window creation styles of the dialog box.
EXSTYLE -Specifies the Extended Styles of the dialog box. Use the Window Creation Extended Styles here.
CAPTION - Specifies the "text characters" for the caption of the dialog box if it has a title bar.
FONT pointsize, typeface, weight, italic
    pointsize - Specifies the size, in points, of the font.
    typeface - Specifies the name of the font typeface. This parameter must be enclosed in double quotation marks (").
    weight - Specifies a numeric expression for the font weight, zero for Normal, 700 for Bold.
    italic - A value of zero will have a normal font and a Value of One will have an Italic font.

          the following are Not Covered in this lesson.
CHARACTERISTICS - Specifies a user-defined Cardinal value for use by resource tools. This value is not used by Windows and I will not cover this.
CLASS - Specifies a 16-bit unsigned integer or a text character string, enclosed in double quotation marks ("), that identifies the class of the dialog box. Used Only if you need to set the class of a Dialog to your own created windows Class.
LANGUAGE -language, sublanguage
    Specifies the language of the dialog box.
MENU -   Specifies the menu to use on the Dialog. This value is either the text character name of the menu or the integer identifier of a resource templete Menu.
VERSION -Specifies a user-defined Cardinal value. This is intended for use by resource scaning tools and is not used by Windows.

Here is some code for a DIALOGEX resource creation file -
MyExDlg DIALOGEX DISCARDABLE 10, 10, 171, 120
     STYLE WS_POPUP | WS_CAPTION
     EXSTYLE WS_EX_DLGMODALFRAME
     CAPTION " My Dialog Box"
     FONT 11, "Arial", 0, 1
     BEGIN
     CTEXT "Title of My Dialog Box"  200, 41, 3, 109, 10
     LTEXT "some Static control text"  202, 5, 15, 176, 8
     CONTROL "", 600, "SCROLLBAR", SBS_HORZ, 4, 60, 160, 8
     DEFPUSHBUTTON "OK" IDOK, 70, 104,  32, 12, WS_TABSTOP
     END
This DIALOGEX is just about the same as the DIALOG creation we have used before, with a couple of differences. . You should notice that there is a EXSTYLE option and it has the window creation Ex-Style of WS_EX_DLGMODALFRAME, this EXSTYLE was Not availible in the DIALOG resource creation. Look at the FONT option, it has two additional parameters than the DIALOG  FONT  has, the first additional number is for the Font Weight, you can put 0 for normal font and 700 for Bold font, the next number sets the Italic, a zero for normal and a One for Italic font. The Control creation is the same code methods that are used in the DIALOG. Wth both the DIALOG and the DIALOGEX, many dialog control "Style" and "ExStyle" options are ignored for templete control creation.

Different Measurements - The DIALOGEX still uses Dialog units and Not pixels for it's measurements, However, the Dialog units for the Horizontal measurements use a different Conversion factor than what was used for the DIALOG templete, the DIALOGEX does NOT use a conversion factor of Four for the horizontal measurements, it uses a conversion factor of about   3.57 for "Small Fonts"  . So you will need to adjust your horizontal measurements (x and width) if you change a DIALOG to a DIALOGEX resource creation.

Using the DIALOGEX in functions
Once you have compiled the resource file (.RES) with the brcc32.exe compiler, you can call the EX templete Dialog in exactly the same way that you did the regular DIALOG, with the DialogBox( ) and CreateDialog( ) functions. It will also use the exact same DialogProc message handling that the regular DIALOG did.



Non-Modal Dialogs
To get a Non-Modal Dialog Box, you would create it with the CreateDialog( ) function. Which is defined in the windows.pas unit as -
function CreateDialog(hInstance: HINST; lpTemplateName: PChar;
  hWndParent: HWND; lpDialogFunc: TFNDlgProc): HWND;
The parameters are the same as the DialogBox( ) function, but the Result is different. The hInstance parameter is the program's hInstance, the lpTemplate is the text name for the DIALOG in the program's resources, , the hWndParent parameter is for the owner or parent window of the dialog box, ussually the main Window, the lpDialogFunc is a Pointer type where you put the address of the Function that will do the Message processing for the dialog box. And the function result is the Handle of the new dialog box window. (The TFNDlgProc type for the lpDialogFunc parameter, is NOT defined as a special type, it is just given a plain old "Pointer" type, I guess the name of TFNDlgProc type is suppose to be descriptive, but it can be confusing when you try to match types). The messages in the DialogProc are used much the same as with a DIALOG templete, except that you do NOT end a Non-Modal Dialog with the EndDialog( ) function. . you must Destroy the dialog window with the DestroyWindow( ) function. You should look at the code in the Dialogs Ex Program below to see examples of these methods.



Scroll Bars
The window's Scroll Bar Class offers you a control that can be adjusted to change the position of a small scroll bar (thumb box), which will set a scroll position number value in a Range of values (from a minimum to a maximum), it will also give visuall indication of the "Relative" position of this number's value. Since Scroll Bars can automatically be added to some standard controls (a Multi-Line Edit control), they are a familar control type to most users. A scroll bar is most often used to "Scroll" or move a window's visible content, to scroll what is out of view of the client area, , and to bring into view the portions of the content that extend beyond the borders of the window. Scroll Bars should be included in any window for which the content of the client area extends beyond the window's borders. Scroll Bars can also be used without scrolling the contents of a window. The programming methods for using a scroll bar are unlike any of the controls we have covered before. To place a scroll bar on a window (or control) during creation, you can include the WS_HSCROLL or WS_VSCROLL in the window's creation style for CtraeteWindow( ), and a Scroll Bar will be placed on that window. To Create a separate scroll bar control (window), you can use the CreateWindow( ) function with the SCROLLBAR window class. There are several scroll bar styles used for it's creation -
Scroll Bar StyleMeaning
SBS_HORZCreates a horizontal scroll bar. If neither the SBS_BOTTOMALIGN nor SBS_TOPALIGN style is specified, the scroll bar will have the height, width, and position specified by the parameters of CreateWindow. You can NOT use this style with the SBS_VERT style.
SBS_VERTCreates a vertical scroll bar. If you do Not specify the SBS_RIGHTALIGN nor the SBS_LEFTALIGN style, the scroll bar will have the height, width, and position specified by the parameters of CreateWindowEx.
The next 4 styles are used with either the SBS_HORZ or SBS_VERT style, to place a Scroll Bar relative to the window Rect of another window, you place the window rect in the dimention parameters of the CreateWindow function. The scroll bar will be the system standard scroll bar size and aligned on the "Side" of the rectangle in the style.
SBS_BOTTOMALIGNAligns the bottom edge of the scroll bar with the bottom edge of the rectangle defined by the CreateWindowEx parameters x, y, nWidth, and nHeight. The scroll bar has the default height for system scroll bars. Use this style with only the SBS_HORZ style.
SBS_LEFTALIGNAligns the left edge of the scroll bar with the left edge of the rectangle defined by the size parameters of CreateWindow. The scroll bar will have the default width for system scroll bars. Use this style only with the SBS_VERT style.
SBS_RIGHTALIGNAligns the right edge of the scroll bar with the right edge of the rectangle defined by the size parameters of CreateWindowEx. The scroll bar will have the default width for system scroll bars. Use this style only with the SBS_VERT style.
SBS_TOPALIGNAligns the top edge of this Scroll Bar with the top edge of the rectangle defined by the size parameters of CreateWindow. The scroll bar has the default height for system scroll bars. Use this style only with the SBS_HORZ style.
The next 2 sytles create a Scroll Bar that does NOT look like a scroll bar, it will look like the "Size Box" that's placed in the lower Right corner of a Main window (Form) with a Status Bar. The SBS_SIZEGRIP style will have a raised edge and 3 lines in it and the SBS_SIZEBOX will just be a 18 x 18 pixel button color square, without lines.
SBS_SIZEGRIPCreates a small Size Box (Edge Drag Box), which does NOT look like a scroll bar. If you do Not add the SBS_SIZEBOXBOTTOMRIGHTALIGN or the SBS_SIZEBOXTOPLEFTALIGN style, the size box has the height, width, and position specified by the size parameters of CreateWindow, however only the standard (18 x18 pixel) SizeBox will be drawn in the Lower Right corner of the CreateWindow measurement parameters.
SBS_SIZEBOXSame as SBS_SIZEGRIP, but without a raised edge or drag lines, this is left over from 16-Bit windows and is not used with 32-Bit windows.
SBS_SIZEBOXBOTTOMRIGHTALIGNAligns the lower right corner of this Size Box with the lower right corner of the rectangle specified by the size parameters of CreateWindow. The size box has the default size for system size boxes. Use this style only with the SBS_SIZEGRIP style.
SBS_SIZEBOXTOPLEFTALIGNAligns the upper left corner of this Size Box with the upper left corner of the rectangle specified by the size parameters of CreateWindow. The size box has the default size for system size boxes. Use this style only with the SBS_SIZEGRIP style.


I will use only two Scroll Bar Creation styles in this program, the SBS_HORZ and the SBS_VERT styles. These 2 styles used without the "Align" styles, are for independent self contained scroll bars.

The "Align" styles can be used used to set the position of a Scroll bar relative to a rectangle's dimentions given in the Left, Top, Width, Height parameters of CreateWindow function. These "Aligned" Scroll Bars will NOT have the dimentions of the CreateWindow function, but will always be the standard scroll bar size and be aligned to the "Side" of the rectangle given in that style. This is intended to be used to set a Scroll Bar as the scroll Bar for another window, you place the other window's dimentions in the Scroll Bars CreateWindow dimention parameters and the Scroll bar will be aligned on a side of that window.
SBS_SIZEGRIP - This is a very unusuall Scroll Bar style, It does not look like a scroll bar, or function (in code or user interaction) like a scroll bar. A "Size Grip" scroll bar is a small square (about 16x16 pixels) that you will see on a "Status Bar" right hand side, with a raised edge and 3 diagonal lines. A SBS_SIZEGRIP scroll bar has the default system processing for a SizeGrip, the cursor will change to a "Sizing" cursor over this scroll bar, AND you can Drag this small Size Grip rectangle to resize the Scroll bar window. I guess that this may be useful somehow, but I did not see a way to use this type of scroll bar, so I will not use this style at all here. The SBS_SIZEBOX style is simalar but without the SizeGrip painting, and is a style from 16-Bit windows, so I will not talk about it here.


A CreateWindow( ) function for a Scroll Bar might look like this -
hScrollBar := CreateWindow('SCROLLBAR', nil, WS_CHILD or 
                        SBS_VERT or WS_VISIBLE or WS_TABSTOP, 
                        8, 8, 14, 240, hParent, 0, hInstance, nil);
This uses the SBS_VERT style without any of the Align styles, so the Top (8), Left(8), Width (14) and Height (240) would be the dimentions for the scroll bar, just like the other controls we have made before. Please notice that the WS_TABSTOP window's style is included, if you want the user to have keyboard input to the scroll bar you must include this Tab Stop Style.



Setting up a Scroll Bar After Creation
Scroll Bars have properties that you can use to have your code set numerical values when the user moves the scroll bar. There is the "Range" values, which is a Minimum and Maximum integer value, there is a Position integer value, which is the placement of the "Thumb bar" on the scroll bar. There is a "PageSize" value, which will change the amount of space the Thumb bar tales up in the scroll bar track. When a Scroll Bar is created it will have the system default settings for it's Position, Minimum, Maximum, and PageSize. Position will be Zero, minimum will be Zero, maximum will be 100, and Page Size will be Zero. To set your Scroll Bar values you will need to use several functions like SetScrollRange( ), SetScrollPos( ), and SetScrollInfo( ). You would set the scroll bar Range (minimum and maximum) with the SetScrollRange( ) function. Windows.pas defines this function as -
function SetScrollRange(hWnd: HWND; // handle of window with scroll bar 
                        nBar,  // scroll bar flag 
                        nMinPos,  // Miminum Range
                        nMaxPos: Integer; // Maximum Range
                        bRedraw: BOOL  // redraw flag
                        ): BOOL; // Result is True if successful
The hWnd parameter is the Handle of a control that has scroll bars (like an Edit) or the Handle of a scroll bar, the nBar parameter is used to tell the system which scroll bar on a control to change, you use the SB_HORZ for the horizontal bar and the SB_VERT for the vertical bar. If the Handle parameter is a Scroll Bar, then you set the nBar to SB_CTL. The nMinPos will set the Minimum value of the Range and the nMaxPos will set the Maximum for the scroll bar Range. If you want the scroll bar to be repainted, you would set the bRedraw parameter to True. If this function succeeds then the Result will be True.

You can set the thumb bar Position with the SetScrollPos( ) function, defined as -
function SetScrollPos(hWnd: HWND; // handle of window with scroll bar 
                      nBar,   // scroll bar flag 
                      nPos: Integer;  // new position of scroll box 
                      bRedraw: BOOL  // redraw flag
                      ): Integer; // Result is previous Position
Again, you can have a Control with scroll bars OR a Scroll Bar for the Handle parameter. Just like the SetScrollRange function, the nBar can be set to SB_HORZ or SB_VERT for a Control with scroll bars, or for a Scroll Bar handle it should be SB_CTL. The nPos will set the position of the thumb bar, which must be within the scrolling range minimum and maximum. If you want the scroll bar to be redrawn, you set the bRedraw to True. The Result of this function is the previous Postion of the scroll bar.

You can set All of the scroll bar's properties with a single function, called SetScrollInfo( ) , this is a more versitle function and can set the Range, Position and Page Size.
It is defined in windows.pas as -
function SetScrollInfo(hWnd: HWND; // handle of window with scroll bar 
                       BarFlag: Integer; // scroll bar flag 
                       const ScrollInfo: TScrollInfo; 
                       Redraw: BOOL  // redraw flag
                       ): Integer; // Result is the current position 
This uses the TScrollInfo record for the settings parameters, it is defined as -
  tagSCROLLINFO = packed Record
    cbSize: Cardinal; // set to SizeOf(TScrollInfo)
    fMask: Cardinal; // Specifies the scroll bar parameters to set.
    nMin: Integer; // Sets the Minimum scrolling position range.
    nMax: Integer; // Sets the Maximum scrolling position range.
    nPage: Cardinal; // Sets the Page Size, length of Thumb Bar.
    nPos: Integer; // Sets the position of the Thumb Bar.
    nTrackPos: Integer; // not used in the SetScrollInfo( ).
  end;

  PScrollInfo = ^TScrollInfo;
  TScrollInfo = tagSCROLLINFO;
  SCROLLINFO = tagSCROLLINFO;
You must set the cbSize to the Size of this Record (28) with SizeOf(TScrollInfo). The fMask has the flags to tell the system which scroll bar settings (Range, Position, PageSize) to Change, this can be a combination of the following values -
  • SIF_ALL -   Combination of SIF_PAGE, SIF_POS, and SIF_RANGE.
  • SIF_DISABLENOSCROLL -   If the scroll bar's new parameters make it unnecessary, use this to disable the scroll bar instead of removing it.
  • SIF_POS -   Use the nPos parameter as the scroll Thumb Bar position.
  • SIF_RANGE -   Use the nMin and nMax members as the Minimum and Maximum values for the scrolling range.
  • SIF_PAGE -   Use the nPage member as the page size for a proportional scroll bar.
If you need to set all 3 (Range, Position, PageSize), you use the SIF_ALL, with SIF_POS the nPos will be read to set the thumb position. If SIF_RANGE is used, the nMin and nMax values will set the minimum and maximunum range for the scroll bar and the SIF_PAGE will read the nPage to adjust the thumb bar length. The nTrackPos is only used with the GetScrollInfo( ) function. You can see code examples in the Dialogs Ex Program below.



Using the Scroll Bar Messages
The System sends a WM_HSCROLL or a WM_VSCROLL message to the Scroll Bar's parent whenever the user interacts with a scroll bar with the mouse or keyboard. The WM_HSCROLL is sent for a Horizontal, and the WM_VSCROLL is sent for a Vertical scroll bar. The LParam of these messages has the Handle of the scroll bar, the WParam has two data sections in the HIWORD and LOWORD parts of that Integer. The HIWORD(WParam) has the nPos value, which is the Position of the thumb bar, , and the LOWORD(WParam) has a nScrollCode in it and it's values can be ONE of the following -
  • SB_BOTTOM, SB_RIGHT   -   Scrolls to the bottom or right.
  • SB_ENDSCROLL   -   Ends scroll.
  • SB_LINELEFT, SB_LINEUP   -   Scrolls left or up by one unit.
  • SB_LINERIGHT, SB_LINEDOWN   -   Scrolls right or down by one unit.
  • SB_PAGELEFT, SB_PAGEUP   -   Scrolls left or up by a preset page amount.
  • SB_PAGERIGHT, SB_PAGEDOWN   -   Scrolls right or down by preset page amount.
  • SB_THUMBPOSITION   -   Scrolls to the absolute position. The current position is specified by the HIWORD(wParam) parameter.
  • SB_THUMBTRACK   -   Drags scroll box to the specified position. The current position is specified by the HIWORD(wParam) parameter.
  • SB_TOP, SB_LEFT   =   Scrolls to the top or left.
NOTICE - that there are Two names given for the same numeric value, but the names are for Horizontal and Vertical scroll bars, so SB_BOTTOM and SB_RIGHT are the same numeric value ( 7 ) and can be interchanged for better description acording to the type of scroll bar (I will use just the Left and Right designations even with a vertical scroll bar).

There is NO default system processing for these scroll bar messages (except with the SBS_SIZEGRIP style), the position of the thumb bar will never be changed by the system, even if the user drags it with the mouse, it will just go back to where it statred. You must process these scroll bar messages and use the SetScrollPos( ) or SetScrollInfo( ) function to change the position of the thumb bar. You will need to set the Minimum and Maximum values for the scroll range, before you show the scroll bar, you will also need to determine a value for a "Line" increase or decrease, and a value for a "Page" change amount. Then when the SB_LINELEFT is processed you will decrease the thumb bar position by a "Line" amount (often a value of One). When a SB_PAGELEFT is processed, you will decrease the thumb bar position by a "Page" amount. I find it better to have variables in my program that are used to store the Minimum, Maximum and thumb bar Position for a scroll bar, Otherwize you will need to use a function to get these valuse from the scroll bar. A TScrollInfo record which is used to Set and Get a scroll bar's parameters, can also be used to "Store" the values like position, minimum and maximum that are needed to process a scrroll bar message.

The following code is an example for a typical scroll bar for a scroll message, WM_VSCROLL in this case. There are code examples of this in the Dialogs Ex Program code below. There are 5 integer variables in the following code, Position1, Minimum1, Maximum1, Line1, and Page1, ALL of these have been set to the values for this scroll bar BEFORE this message processing is done, usually these are set during the set up of the scroll bar. The Position1 variable is the current position of the thumb bar, the Minimum1 and Maximum1 would be the extents of the Range for the scroll bar. The Line1 and Page1 would be the values that you have determined to be used for the Line and Page amounts for scroll bar changes. These two get their name from "Text" scrolling operations, like in NotePad.exe, where a "Line" is a line of text, and a "Page" is a page of text. Many of the scroll bars that you will use, will Not be for text scrolling, so you will need to calculate your own sizes for Line and Page movement. For a general guideline, the Line size can be about 100th of the total range, and a Page size about a 10th or 20th of the range. The CanScroll is a Boolean variable, it is used here to Stop the movement done in the MoveIt procedure, Notice that the SB_THUMBTRACK sets CanScroll to False, and then CanScroll is tested for the MoveIt procedure.
  WM_VSCROLL: 
    begin
    CanScroll := True;
    case LOWORD(wParam) of
      SB_LINEUP: if Position1 > Minimum1 + Line1 - 1 then 
        Dec(Position1, Line1) else Position1 := Minimum1;
      SB_LINERIGHT: if Position1 < Maximum1 - Line1 + 1 then 
        Inc(Position1, Line1) else Position1 := Maximum1;
      SB_PAGELEFT: if Position1 > Minimum1 + Page1 - 1 then 
        Dec(Position1, Page1) else Position1 := Minimum1;
      SB_PAGERIGHT: if Position1 < Maximum1 - Page1 + 1 then 
        Inc(Position1, Page) else Position1 := Maximum1;
      SB_THUMBTRACK: 
        begin
        Position1 := SmallInt(HIWORD(WParam));
        CanScroll := False;
        end;
      SB_THUMBPOSITION: Position1 := SmallInt(HIWORD(WParam)); 
      SB_LEFT: Position1 := Minimum1;
      SB_RIGHT: Position1 := Maximum1;
      end;
    SetScrollPos(lParam, SB_CTL, Position1, True);
    if CanScroll then MoveIt;
    end;




Dialogs Ex Program

This Dialogs Ex Program will use a DIALOGEX to create a resource templete, and there will be one scroll bar created on the Main Form, which will move the Dialog window Up and Down the screen, when you move the scroll thumb bar. The Dialog window will have three scroll bars, One for the Red color, one for the Green color and one for the Blue color, when you move these scroll bars the individual color component (Red, Green, Blue) will be changed for the brush color that does the bacground for the Main Form and the Dialog window. The dialog window will also process the WM_MOVE message, so that when you move the dialog window, the thumb bar on the main form's scroll bar will move with the vertical placement of the dialog.


Dialog Resource Creation Code
You will need to create a Dialog Templete in a Resource file much like you did with the previous Dialogs program. The code for this Dlg2.RC file is below. Remember that the Units for control placement are NOT pixels but Dialog Units. In the "Second" dialog, I have added the EXSTYLE WS_EX_APPWINDOW just to show you that you can add the EX styles to the DIALOGEX resource creation, but the WS_EX_APPWINDOW is unnessary for this dialog and just here to show you where to add the EX styles. You should notice that the FONT has two additional parameters, I have set the last one to 1, so the font will be Italic. I have added the DISCARDABLE resource parameter to the code, this will tell the system not to keep this templete in memory when it is not used, for almost all Dialogs, you should add the DISCARDABLE parameter. Since there are three scroll bars in this dialog, I create each of them in a different way, the first is created with a SCROLLBAR control name, The second one is created with a CONTROL resource Name and the "SCROLLBAR" is added as the Class Type. The third one is created in the DialogProc WM_INITDIALOG message, using the CreateWindow message. . . all three scroll bars are about the same, even though they were created in different ways.

For the "Child" dialog, I have used a number (55) instead of a Text name, to idenify this dialog, numbers are generally used more than text to identify resource data. But when you use the dialog creation functions like CreateDialog( ), you will need to "Translate" this Dialog ID number to a resource "name" by using the function MakeIntResource(55), you can see this in the code for DoChildDialog procedure below. Notice that in the DIALOGEX   STYLE there is a WS_CHILD, this is nessary because this dialog will be on the main Form, much like a VCL TPanel. I have added the EX style of WS_EX_CLIENTEDGE so the dialog will have a sunken border. Child dialogs can be usefull to have a Container for several controls that are unlikely to change durring design time, but if you need to move or alter the controls while designing, it may be easier to create all of the windows in code, otherwise you will have to complile the resource and add it to your program to see how it looks.

For this Dialogs Ex Program program here is the code for the "Dlg2.rc" file -
Second DIALOGEX DISCARDABLE 8, 6, 171, 120
     STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU
     EXSTYLE WS_EX_APPWINDOW
     CAPTION " Second Dialog Box"
     FONT 11, "Arial", 0, 1
     BEGIN
     CTEXT "Title of Second Dialog Box"  200, 41, 3, 109, 10
     LTEXT "Move The Scroll Bars to change the Color"  202, 5, 15, 176, 8
     SCROLLBAR 600, 4, 38, 160, 8, SBS_HORZ | WS_TABSTOP
     CONTROL "", 601, "SCROLLBAR", SBS_HORZ | WS_TABSTOP, 4, 60, 160, 8
     DEFPUSHBUTTON "OK" IDOK, 70, 104,  32, 12, WS_TABSTOP
     END

55 DIALOGEX DISCARDABLE 80, 31, 90, 52
     STYLE WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS
     EXSTYLE WS_EX_CLIENTEDGE
     FONT 11, "Times New Roman", 700, 0
     BEGIN
     CTEXT "This is the Child Dialog Box"  200, 2, 1, 86, 10
     CTEXT "Center Text"  201, 8, 12, 74, 8
     LTEXT "?"  202, 5, 21, 81, 8
     AUTOCHECKBOX "Check Me", 300, 5, 30, 56, 8
     AUTOCHECKBOX "a Check Box", 301, 5, 40, 56, 8
     END


Program Code
This program shows how to create and use scroll bars and Non-Modal dialog boxes. You must have the Dlg2.RES file complied from the code above. You will use the CreateDialog( ) function for a Non-Modal dialog, and when you look at the DialogProc( ), you will see that it is used in the same way as the last Dialog's program, except the Dialog Window is not destroyed with the EndDialog( ) function, instead the DestroyWindow(hWnd) function is used. I have moved the Dialog closing code to the DoClose( ) function inside the DialogProc. In the WM_INITDIALOG message code, there are functions that set up the Range and position for the two Scroll Bars created in the dialog templete. See the SetScrollRange( ) and SetScrollPos( ) functions, since these scroll bars do Not change their Range or Page size I use constant values for the Range. The range for these three scroll bars corresponds to the color componenet "Byte' type, which is Zero to 255. The third Scroll Bar is created in the WM_INITDIALOG message, and not in the dilaog resource templete. I will set the "Page Size" for this third scroll bar, even though the range never changes, usually the Page size is a for proportional thumb bar that changes it's length with the relative area of window data that is being scrolled. You will see that the ScrollInfo2 record is filled with the settings for the Range, Position, and Page Size for this third scroll bar and its fMask is set to SIF_ALL so all the values will be used in the SetScrollInfo( ) function.

You should look at the code below the CreateWindow( ) function for the Main form's Scroll Bar near the end of this code. A TScrollInfo record called SInfo1 filled with the data collected for the Screen's Work Rectangle's height, and the Dialog window height. This SInfo1 will contain all five of the values needed for the minimum, maximum, position, page amount and line amount, to set the Scroll Bar parameters with SetScrollInfo( ) and these values will be used again in the WM_VSCROLL message of the programs MessageProc function. Look at the code in the WM_VSCROLL message, this code can be used for many different scroll bars, without much change, because a TScrollInfo record can contain all of the values needed.

This program will adjust all of the sizes for the Font and controls according to the computer's Display "Font Size", it uses the procedure SetPixelsPerInch to get the current Pixels Per Inch of the system screen DC, and then uses the DoSize( ) function to adjust all of the dimention sizes for the font and controls.

See the comments in the code for more information -

program Dialogs2;
{this program uses a Non-Modal Dialog Boxs in Dlg2.RES and
has Scroll Bars}

uses
  Windows, Messages, SmallUtils;

{$R *.RES}
{$R Dlg2.RES}
// the Dlg2.RES has the dialog resource templetes

const
{because 0 and 1 are used so many times in a Program, I use
the Constant Values Zero, One and Two}
Zero = 0;
One = 1;
Two = 2;
tffive = 255;
ID_ExitBut = 10;
ID_DlgBut = 11;
ID_DlgChdBut = 12;
{I use three ID constants for the form's buttons}

var
wClass: TWndClass;
hForm1, Font1, Brush1, hDialog, hChild,
hScrollBar, DlgHPos, PixelPerInch: Integer;
mainMsg: TMSG;
Color1, InvColor: Cardinal;
Rect1, workRect: TRect;
SInfo1: TScrollInfo;
AryColorPos: Array[Zero..Two] of Cardinal;
ArySB_brush: Array[Zero..Two] of Integer;


procedure SetPixelsPerInch;
var
sDC: Integer;
begin
{this is a simple way to get a sizing mutiplier, by getting the
current Pixels Per Inch of the system screen DC, which will Change
with the users Settings for their Display Font Size}
sDC := GetDC(Zero);
PixelPerInch := GetDeviceCaps(sDC, LOGPIXELSY);
ReleaseDC(Zero, sDC);
end;

function DoSize(aSize: Integer): Integer;
begin
{You will need to multiply the dimention aSize in Pixels by the
Current PixelsPerInch and then divide that by the
PixelsPerInch that you are desingning this in, I was in the standard
Small Font 96 Pixels Per Inch at design time,
BE SURE to check and see what your Screen PixelPerInch as you are designing
and replace the value 96 with YOUR DISPLAY Pixels Per Inch}
Result := MulDiv(aSize, PixelPerInch, 96);
{the windows system function MulDiv( ) will Multiply the first two
integers and then divide that result by the third integer and Round
it off to the nearest integer value}
end;

function InverseColor(Color: Cardinal): Cardinal;
var
Red, Green, Blue: Byte;
begin
{this function will calculate the Inverse Color
by subtracting the Red, Green and Blue values from 255}
Red := abs(tffive - GetRValue(Color));
Green := abs(tffive - GetGValue(Color));
Blue := abs(tffive - GetBValue(Color));

{if the color is mid-Grey the Inverse will also be mid-Grey and will not
be readable, so I test for mid-Grey and set the Result to Black}
if (Red < 147) and (Red > 107) and (Blue < 147) and (Blue > 107) and
(Green < 147) and (Green > 107) then
Result := Zero else
Result := RGB(Red, Green, Blue);
end;


function DialogProc(hWnd, Msg, wParam, lParam: Integer): Integer; stdcall;
var
CtrlRect:TRect;
i, hScroll: Integer;
PaintS: TPaintStruct;
Size1: TSize;
ScrollInfo2: TScrollInfo;

  procedure DoClose;
    begin
    SetDlgItemText(hForm1, ID_DlgBut,'Show Dialog');
    InvalidateRect(hForm1, nil, True);
  {I will Invalidate the Main Form Window to display the New
  background Color}
    if DestroyWindow(hWnd) then
      begin
      hDialog := Zero;
      Result := One;
      end;
    {when a Dialog Box is NOT Modal, , use the DestroyWindow
instead of the EndDialog to Kill the Dialog Box}
    end;

    
begin
Result := Zero;
case Msg of
WM_INITDIALOG:
  begin
  SendMessage(hWnd, WM_SETICON, One, LoadIcon(Zero,IDI_QUESTION));
  SendDlgItemMessage(hWnd,200,WM_SETFONT,Font1, Zero);
  GetWindowRect(hWnd, Rect1);
{I will save the dimentions of this Dialog in the gobal Rect1 variable
because I will use this Rect1 in the WM_MOVING message and the WM_VSCROLL
message of the Main Form Window}
  MoveWindow(hWnd, DlgHPos, SInfo1.nPos, Rect1.Right - Rect1.Left,
             Rect1.Bottom - Rect1.Top, False);
{I will move this Dialog Window to the DlgHPos and ScrollInfo1.nPos,
which are recorded if the user moves this dialog window,
OR changes the main form's Scroll Bar}

{In this Dialog Box there will be three Horizontal Scroll Bars, each
created in a Different way. Scroll Bar 600 is Created with the standard
Dialog RC file SCROLLBAR designation, see the code in the Dlg2.rc}
  for i := Zero to One do
    begin
    hScroll := GetDlgItem(hWnd, i + 600);
    SetScrollRange(hScroll,SB_CTL, Zero, tffive, False);
    SetScrollPos(hScroll, SB_CTL, AryColorPos[i], False);
    end;
{Scroll Bar 601 is created with the Dialog RC file CONTROL designation
and it's Class as SCROLLBAR}

{Scroll Bar 602 is created here in the WM_INITDIALOG, using the CreateWindow
function, NOTICE that I use the MapDialogRect function, to get the size
Rectangle for the new scroll bar, because a Dialog Box will be Resized
according to the "Font Size" for your System Display settings}
  SetRect(CtrlRect,4,82,164,90);
  MapDialogRect(hWnd, CtrlRect);
{the MapDialogRect is the only way to get correct dialog measurement
conversions using the DIALOGEX resource creation, the DialogBaseUnits
for horizontal measurements changes with the DIALOGEX-
For some reason, when you use the DIALOGEX resource creation, the
Horizontal DialogBaseUnits conversion changes from 4 to about 3.57
for "Small Fonts"}
  hScroll := CreateWindow('SCROLLBAR', nil, WS_CHILD or SBS_HORZ or
                     WS_VISIBLE or WS_TABSTOP, CtrlRect.Left,
                     CtrlRect.Top, CtrlRect.Right - CtrlRect.Left,
                     CtrlRect.Bottom - CtrlRect.Top,
                     hWnd, 602, hInstance, nil);
        {set the Scroll Bar's ID to 602 in the hMenu Parameter}
  ScrollInfo2.cbSize := SizeOf(ScrollInfo);
  ScrollInfo2.fMask := SIF_ALL;
{the fMask is set to SIF_ALL, so that all of the scroll bar's settings
will be read and set in the SetScrollInfo function}
  ScrollInfo2.nMin := Zero;
  ScrollInfo2.nMax := 282{255};
{you must include the (nPage - 1) in the nMax to get the correct nPos Maximum}
  ScrollInfo2.nPage := 28{Zero};
{I do NOT need to set a nPage value here, I set this to show you how, The
nPage is normally used for Scroll Bars that will change their nMax settings
as the Size of what they are scrolling changes, the size of the nPage is
suppose to indicate to the user, what the relative scrolling range is}
  ScrollInfo2.nPos := AryColorPos[Two];
  ScrollInfo2.nTrackPos := Zero;
  SetScrollInfo(hScroll, SB_CTL, ScrollInfo2, False);
  end;

WM_HSCROLL:
  begin
{the WM_HSCROLL message is sent to a Scroll Bar's Parent window, whenever
the scroll bar thumb is moved, A Scroll Bar does NOT automaticaly change it's
position when the user moves the scroll bar, you will need to get information
from the LOWORD(wParam) and then use the SetScrollPos or SetScrollInfo
functions to change the position of the scroll bar}
  i := GetWindowLong(lParam, GWL_ID)-600;
{the LParam has the Handle of the Scroll Bar, since Dialogs do not generally
use "Handles" for reference, I will get the ID number with
 GetWindowLong(lParam, GWL_ID) and subtract 600 to get an Index for the
 AryColorPos Array}
  case LOWORD(wParam) of
    {the LOWORD(wParam) has the ScrollCode, which tells you what was done to
     the Scroll Bar to generate this message, because a Scroll Bar operation
     depends on it's settings and what the user does to it, there is no
     "Default" operation for Scroll Positioning of the ScrollCodes}
    SB_LINELEFT: if AryColorPos[i] > Zero then Dec(AryColorPos[i], One);
    SB_LINERIGHT: if AryColorPos[i] < tffive then Inc(AryColorPos[i], One);
 {the LineLeft and LineRight are sent for clicks on the arrow buttons}
    SB_PAGELEFT: if AryColorPos[i] > 10 then Dec(AryColorPos[i], 10) else
                    AryColorPos[i] := 0;
    SB_PAGERIGHT: if AryColorPos[i] < 245 then Inc(AryColorPos[i], 10) else
                    AryColorPos[i] := tffive;
  {the PageLeft and PageRight are sent for clicks on the scroll track
  and the user pressing the PageUp and PageDown keys}
    SB_THUMBTRACK: AryColorPos[i] := HIWORD(WParam);
  {the ThumbTrack is sent when the user moves the Thumb bar with the mouse}
    SB_LEFT: AryColorPos[i] := Zero;
    SB_RIGHT: AryColorPos[i] := tffive;
  {SB_LEFT and SB_RIGHT are sent when the user presses the Home and End keys}
    end;
  SetScrollPos(lParam, SB_CTL, AryColorPos[i], True);
{you will need to call the SetScrollPos function, to set a new position for
the scroll bar thumb, it will NOT change it's postion for any user input
reason, (mouse, Keyboard), until you call a function that changes it's position}
  Color1 := RGB(AryColorPos[Zero],AryColorPos[One], AryColorPos[Two]);
{Color1 is the current Color used for the background brushes}
  InvColor := InverseColor(Color1);
  PaintS.hdc := GetDC(hWnd);
  wClass.hbrBackground := CreateSolidBrush(Color1);
{a new brush is created in the new Color1, it is set as the background
brush with a call to SetClassLong with the GCL_HBRBACKGROUND, the old brush
handle is returned and it's object is Deleted}
  DeleteObject(SetClassLong(hForm1, GCL_HBRBACKGROUND, wClass.hbrBackground));
  ArySB_brush[i] := CreateSolidBrush(AryColorPos[i] shl (i shl 3));
{the ArySB_brush contains the Brush used for the WM_CTLCOLORSCROLLBAR message,
the Color of this brush is calculated from the individual Red, Green, and Blue
color of the scroll bar Position, which is in the AryColorPos[i], I use the
shl to multiply the color value and "Shift" it into the correct color position
of the color's Cardinal value}
  SelectObject(PaintS.hdc, wClass.hbrBackground);
{a rectangle is drawn on the Dialog Box with the New color in it, 
so the user can see what the new color is}
  Rectangle(PaintS.hdc, Zero, Zero, DoSize(40), DoSize(30));
  GetTextExtentPoint32(PaintS.hdc, '888', One, Size1);
  GetWindowRect(lParam, CtrlRect);
{when using Dialog Boxes, you should get the Position of a Control with
 GetWindowRect( ), because the position can change according to the
 settings for the size of the system text, so using a single pixel value
 like 42 will be correct in systems where the system font is the same,
 but WRONG in systems where the system display font is different}
  ScreenToClient(hWnd, CtrlRect.TopLeft);

{with the change in a Scroll bar position, I will draw the new position
in the color text above the Scroll bar that is moved, the "case i of"
will get the Scroll Bar in use, zero for scrollbar 600}
  case i of
  0: begin
     SetBkColor(PaintS.hdc,$FF);
     SetTextColor(PaintS.hdc,$3380);
     end;
  1: begin
     SetBkColor(PaintS.hdc,$FF00);
     SetTextColor(PaintS.hdc,$8033);
     end;
  2: begin
     SetBkColor(PaintS.hdc,$FF0000);
     SetTextColor(PaintS.hdc,$FFFF00);
     end;
  end; // case i
  hScroll := Length(Int2Str(AryColorPos[i]));
{I use the hScroll as the integer that holds the text length
so I will not need to have another variable}
  TextOut(PaintS.hdc,CtrlRect.Left+80, CtrlRect.Top - (Size1.cy+Two),'       ', 7);

{to make sure the previous text is erased, I draw the text for 7 spaces
you could use a PatBlt or Rectangle, but you would need to create a hBrush}
  TextOut(PaintS.hdc,CtrlRect.Left+82+(12-(hScroll*4)),
          CtrlRect.Top - (Size1.cy+Two), PChar(Int2Str(AryColorPos[i])),
          hScroll);
{the CtrlRect.Left+82+(12-(hScroll*4))  will get a "Center" position
for the text in the 7 spaces Color block}
  ReleaseDC(hWnd, PaintS.hdc);
  end;

WM_COMMAND: if LOWORD(wParam) = IDOK then
              DoClose;

WM_CLOSE: DoClose;

WM_PAINT:
  begin
  BeginPaint(hWnd, PaintS);
  SelectObject(PaintS.hdc, wClass.hbrBackground);
{the wClass.hbrBackground Brush has the brush with the Color set
by the Color Scroll Bars on this dialog}
  Rectangle(PaintS.hdc, Zero, Zero, DoSize(40), DoSize(30));
  GetWindowRect(GetDlgItem(hWnd,600), CtrlRect);
{because the Dialog Box can be a different size according to the Display's
"Font Size" setting, I need to get each Scroll Bars Window Rectangle
in order to paint the Text in the correct position over the scroll bar}
  ScreenToClient(hWnd, CtrlRect.TopLeft);
{the CtrlRect is in Screen coordinates so you need to convert the TopLeft
to the client coordinates for the dialog window with ScreenToClient}
  GetTextExtentPoint32(PaintS.hdc, '888', One, Size1);
{because the size of the "System" font may change, I get the vetical
Height of the font with GetTextExtentPoint32 to use in the TextOut functions}
  SetBkColor(PaintS.hdc,$FF);
  SetTextColor(PaintS.hdc,$3380);
{I will Draw Colored Text over each Scroll Bar to tell the User the Color
that the scroll bar will change}
  TextOut(PaintS.hdc,CtrlRect.Left+22, CtrlRect.Top -
          (Size1.cy+Two),' Red ', 5);
  i := length(Int2Str(AryColorPos[Zero]));
  TextOut(PaintS.hdc,CtrlRect.Left+80, CtrlRect.Top -
          (Size1.cy+Two),'       ', 7);
{I will draw 7 text "Space" charaters to erase the previous Text Draw}
  TextOut(PaintS.hdc,CtrlRect.Left+82+(12-(i*4)),
          CtrlRect.Top - (Size1.cy+Two), PChar(Int2Str(AryColorPos[Zero])), i);
  GetWindowRect(GetDlgItem(hWnd,601), CtrlRect);
  ScreenToClient(hWnd, CtrlRect.TopLeft);
  SetBkColor(PaintS.hdc,$FF00);
  SetTextColor(PaintS.hdc,$8033);
  TextOut(PaintS.hdc, CtrlRect.Left+22, CtrlRect.Top -
          (Size1.cy+Two),' Green ',7);
  i := length(Int2Str(AryColorPos[One]));
  TextOut(PaintS.hdc,CtrlRect.Left+80, CtrlRect.Top -
          (Size1.cy+Two),'       ', 7);
  TextOut(PaintS.hdc,CtrlRect.Left+82+(12-(i*4)),
          CtrlRect.Top - (Size1.cy+Two), PChar(Int2Str(AryColorPos[One])), i);
  GetWindowRect(GetDlgItem(hWnd,602), CtrlRect);
  ScreenToClient(hWnd, CtrlRect.TopLeft);
  SetBkColor(PaintS.hdc,$FF0000);
  SetTextColor(PaintS.hdc,$FFFF00);
  TextOut(PaintS.hdc, CtrlRect.Left+22, CtrlRect.Top -
          (Size1.cy+Two),' Blue ',6);
  i := length(Int2Str(AryColorPos[Two]));
  TextOut(PaintS.hdc,CtrlRect.Left+80, CtrlRect.Top -
          (Size1.cy+Two),'       ', 7);
  TextOut(PaintS.hdc,CtrlRect.Left+82+(12-(i*4)),
           CtrlRect.Top - (Size1.cy+Two), PChar(Int2Str(AryColorPos[Two])), i);
  ScreenToClient(hWnd, CtrlRect.BottomRight);
  SetBkColor(PaintS.hdc, $FFFFFF);
  SetTextColor(PaintS.hdc,Zero);
{I will Draw the Text below the bottom Scroll Bar to show the relative 
values of the Scroll Positions from 0 to 127 to 255}
  TextOut(PaintS.hdc, CtrlRect.Left+13, CtrlRect.Bottom+4,'0',One);
  TextOut(PaintS.hdc, CtrlRect.Left+(( CtrlRect.Right-CtrlRect.Left) shr One)
          -13, CtrlRect.Bottom+4,'127', 3);
  TextOut(PaintS.hdc, CtrlRect.Right-28,CtrlRect.Bottom+4,'255',3);
  EndPaint(hWnd,PaintS);
  end;
WM_CTLCOLORDLG: Result := wClass.hbrBackground;
{The WM_CTLCOLORDLG Result will set the hBrush used for the Erase 
 Background of a Dialog box}
WM_CTLCOLORSCROLLBAR:
    begin
{the WM_CTLCOLORSCROLLBAR will get the hBrush used to fill
the shaft area of a Scroll bar}
    i := GetWindowLong(lParam, GWL_ID)-600;
    Result := ArySB_brush[i];
    end;
WM_CTLCOLORSTATIC:
    begin
    SetBkColor(wParam, $FFFFA0);
    Result := wClass.hbrBackground;
    end;
WM_MOVING:
    begin
{when this Dialog box is moved the Screen Position will be recorded
in the DlgHPos and ScrollInfo1.nPos, the Scroll Bar Position on the
main Window will be changed to the new vertical Position of this Dialog}
    if DlgHPos <> PRect(lParam).Left then
      DlgHPos := PRect(lParam).Left;

    if PRect(lParam).Top <> SInfo1.nPos then
      begin
      if PRect(lParam).Top < SInfo1.nMin then
        begin
  {to avoid this dialog from being draged past the Scroll Bar's Limits
  I will test the PRect(lParam).Top with the ScrollInfo1.nMin and set the
  PRect(lParam).Top to the Top of the Work Area, which is ScrollInfo1.nMin}
        PRect(lParam).Top := SInfo1.nMin;
        PRect(lParam).Bottom := SInfo1.nMin + Rect1.Bottom - Rect1.Top;
        end;
      if PRect(lParam).Top > SInfo1.nMax then
        begin
        PRect(lParam).Top := SInfo1.nMax;
        PRect(lParam).Bottom := SInfo1.nMax + Rect1.Bottom - Rect1.Top;
        end;
      SInfo1.nPos := PRect(lParam).Top;
      SetScrollPos(hScrollBar, SB_CTL, SInfo1.nPos, True);
{sometimes it is convient to record the position of a Non-Modal Dialog box,
in case the user moves it to a position that they like better, the
ScrollInfo1.nPos and DlgHPos record the moved position of this dialog,
so if it is shown again it will be in the same position}
      end;
    end;
end;
end;

procedure DoDialog;
begin
if hDialog <> Zero then
  begin
  SendMessage(hDialog, WM_CLOSE, Zero, Zero);
  Exit;
  end;

hDialog := CreateDialog(hInstance, 'Second', hForm1, @DialogProc);
{the  CreateDialog function will make a NON-Modal  Dialog Box and return
a Handle for that window (the dialog box)}
ShowWindow(hDialog, SW_SHOWNORMAL);
{the dialog is Not created with the WS_VISIBLE, so you will need to Show it}
if hDialog <> Zero then
  SetDlgItemText(hForm1, ID_DlgBut,'Destroy Dialog');
end;

function ChildProc(hWnd, Msg, wParam, lParam: Integer): Integer; stdcall;
var
wRect: TRect;
begin
{this is the message process for the Child Dialog Box}
Result := Zero;
case Msg of
  WM_INITDIALOG:
    begin
{WM_INITDIALOG is where you set your Dlg Items properties. There are
special DlgItem functions (SendDlgItemMessage, SetDlgItemText) to help
with this.}
    GetWindowRect(hWnd, wRect);
    SetDlgItemText(hWnd, 201, PChar('This Dialog Width is '+
                                     Int2Str(wRect.Right - wRect.Left)));
    SetDlgItemText(hWnd, 202, 'Check the check Box');
    end;
  WM_COMMAND:
    if LOWORD(wParam) = 300 then
    if IsDlgButtonChecked(hWnd, 300) = BST_CHECKED then
    SetDlgItemText(hWnd, 202,'Check Box is Checked') else
    SetDlgItemText(hWnd, 202,'UnChecked the Check Box');
  WM_CTLCOLORDLG: Result := Brush1;
  WM_CTLCOLORSTATIC:
        begin
        SetBkColor(wParam, $FFFFA0);
        Result := Brush1;
        end;
  end;
end;

procedure DoChildDialog;
begin
if hChild <> Zero then
  begin
  if DestroyWindow(hChild) then
    begin
{I use DestroyWindow to kill the Child Dialog}
    SetDlgItemText(hForm1, ID_DlgChdBut, 'Show Child Dialog');
    hChild := Zero;
    end;
  Exit;
  end;

hChild := CreateDialog(hInstance, MakeIntResource(55), hForm1, @ChildProc);
{unlike other Dialog Boxes I have done before, I use a Number instead
of a Text identifier for the DIALOGEX resource templete, so I need to
use the MakeIntResource( ) function to get the Number 55 to a PChar
used for resource identifiers}
if hChild <> Zero then
  SetDlgItemText(hForm1, ID_DlgChdBut, 'Hide Child Dialog');
end;

function MessageProc(hWnd, Msg, wParam, lParam: Integer): Integer; stdcall;
var
PaintS: TPaintStruct;
i: Integer;
CanScroll: Boolean;
begin
case Msg of
  WM_CLOSE: DestroyWindow(hDialog);
{The system will destroy the hDialog, but I have included this to be sure
there are no memory leaks}
  WM_COMMAND: if LOWORD(wParam) = ID_ExitBut then
                PostMessage(hForm1,WM_CLOSE, Zero, Zero)
                else if LOWORD(wParam) = ID_DlgBut then DoDialog
                else if LOWORD(wParam) = ID_DlgChdBut then DoChildDialog;
  WM_PAINT: begin
    BeginPaint(hWnd, PaintS);
    SetTextColor(PaintS.hdc,InvColor);
    SetBkMode(PaintS.hdc, TRANSPARENT);
    TextOut(PaintS.hdc, DoSize(44), DoSize(39), 'Move Scroll Bar to Move Dialog Box', 34);
    SelectObject(PaintS.hdc, Font1);
    TextOut(PaintS.hdc, DoSize(140), DoSize(9), 'Dialog Demo 2', 13);
    EndPaint(hWnd, PaintS);
    end;


  WM_DESTROY: begin
{you must DeleteObject for the BackGround Brush that you created}
    for i := Zero to Two do
      DeleteObject(ArySB_brush[i]);
    DeleteObject(SetClassLong(hWnd, GCL_HBRBACKGROUND,
                 GetStockObject(GRAY_BRUSH)));
    PostQuitMessage(Zero);
    end;

  WM_VSCROLL:
    begin
{Like the WM_HSCROLL used in the DialogProc, you will need to get the
scroll bar nScrollCode in the LOWORD(wParam) and then set the Scroll
Position with the SetScrollPos. All of the Scroll Bar's numeric values
are in the SInfo1 variable}
    CanScroll := True;
    with SInfo1 do
    case LOWORD(wParam) of
      SB_LINEUP: if nPos > nMin+ nTrackPos - One then
        Dec(nPos, nTrackPos) else nPos := nMin;
      SB_LINEDOWN: if nPos < nMax - nTrackPos + One then
        Inc(nPos, nTrackPos) else nPos := nMax;
      SB_PAGEUP: if nPos > Integer(nPage) - One then
        Dec(nPos, nPage) else nPos := nMin;
      SB_PAGEDOWN: if nPos < nMax - Integer(nPage) + One then
        Inc(nPos, nPage) else nPos := nMax;
      SB_THUMBTRACK:
        begin
        nPos := SmallInt(HIWORD(WParam));
        //CanScroll := False;
        end;
{I have TypeCast the HIWORD(WParam) to a SmallInt so negative values
can be read correctly}
      //SB_THUMBPOSITION: nPos := SmallInt(HIWORD(WParam));
{I have commented out the SB_THUMBPOSITION message and the CanScroll := False;
in the SB_THUMBTRACK, you can Un-Comment these and then the Dialog box will
not move untill you release the thumb bar with the mouse button}
      SB_TOP: nPos := nMin;
      SB_BOTTOM: nPos := nMax;
      end;  // case

    SetScrollPos(lParam, SB_CTL, SInfo1.nPos, True);
{the code above for a WM_VSCROLL can be used for many different scroll bars,
because the SInfo1 record has all of the values needed for testing and 
positioning the thumb bar}
    if (hDialog <> Zero) and CanScroll then
    MoveWindow(hDialog, DlgHPos, SInfo1.nPos, Rect1.Right -
               Rect1.Left,Rect1.Bottom - Rect1.Top,True);
    {when the user moves this scroll bar, the Dialog window will
    move Up and Down on the Screen}
    end;  // WM_VSCROLL
end; // case Msg
Result := DefWindowProc(hWnd,Msg,wParam,lParam);
end;

procedure SetColors;
var i: Integer;
begin
{the AryColorPos is an Array that holds the Scroll Bar Positions
for the three Scroll bars on the Dialog box, the Range of these scroll bars
is zero to 255, corresponding to the range of a Byte variable. . . .
these values are also the Red, Green and Blue components
of the Color1 color value}
for i := Zero to Two do
  begin
  AryColorPos[i] := 127;
  ArySB_brush[i] := CreateSolidBrush(AryColorPos[i] shl (i shl 3));
  end;
//Color1 := RGB(AryColorPos[Zero],AryColorPos[One], AryColorPos[Two]);
{the code below is equivalent to the code above, I use the shl below to
change the byte values into the correct color value position of the Color1}
Color1 := AryColorPos[Zero] or (AryColorPos[One] shl 8) or (AryColorPos[Two] shl 16);
end;

begin  //  MAIN PROGRAM BEGIN / / / / / / / / / / / / / / /
hChild := Zero;
hDialog := Zero;
InvColor := Zero;

SetPixelsPerInch;
SetColors;

Brush1 := CreateSolidBrush($FFFFA0);
Font1 := CreateFont(DoSize(-16), Zero, Zero, Zero, FW_BOLD, Zero, Zero, Zero,
           ANSI_CHARSET, OUT_DEFAULT_PRECIS,
           CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
           VARIABLE_PITCH or FF_SWISS, 'Arial');


with wClass do
  begin
  style := CS_PARENTDC or CS_BYTEALIGNWINDOW;
  hInstance := sysInit.hInstance;
  hIcon := LoadIcon(hInstance,'MAINICON');
  lpfnWndProc :=  @MessageProc;
  hbrBackground := CreateSolidBrush(Color1);
  lpszClassName := 'Form Class';
  hCursor := LoadCursor(Zero,IDC_ARROW);
  end;

RegisterClass(wClass);

{I will use the DoSize( ) function on ALL of the pixel dimention parameters
in the CreateWindow functions, This will make the Form and all of it's
controls resize according to the current Pixel Per Inch of the Display, ,
However, the main form should just Have Scroll bars added if the Pixels
Per Inch is increased, because the size of your Main Form may be larger
than the Screen's Work Rectangle}

SetRect(Rect1,Zero,Zero, DoSize(406), DoSize(260));
if not AdjustWindowRect(Rect1, WS_CAPTION or WS_MINIMIZEBOX or
                        WS_SYSMENU, False) then
  SetRect(Rect1,Zero,Zero, DoSize(414), DoSize(288));

hForm1 := CreateWindow(wClass.lpszClassName, 'Doing Dialogs',
    WS_CAPTION or WS_MINIMIZEBOX or WS_SYSMENU,
    (GetSystemMetrics(SM_CXSCREEN) shr One)-DoSize(240),
    (GetSystemMetrics(SM_CYSCREEN) shr One)-DoSize(160),
    Rect1.Right-Rect1.Left, Rect1.Bottom-Rect1.Top,
    Zero, Zero, hInstance, nil);

CreateWindow('Button','E x i t',
    WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or BS_TEXT or WS_TABSTOP,
    DoSize(288), DoSize(214), DoSize(86), DoSize(25), hForm1, ID_ExitBut, hInstance, nil);
{All of the button controls will use the ID number for
reference, and not a Handle}

CreateWindow('Button','Show Dialog',
    WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or BS_TEXT or WS_TABSTOP,
    DoSize(52), DoSize(176), DoSize(94), DoSize(25), hForm1, 11, hInstance, nil);
SendDlgItemMessage(hForm1, ID_DlgBut, WM_SETFONT,
                   GetStockObject(ANSI_VAR_FONT), Zero);

CreateWindow('Button','Show Child Dialog',
    WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or BS_TEXT or WS_TABSTOP,
    DoSize(52), DoSize(214), DoSize(114), DoSize(25), hForm1, ID_DlgChdBut, hInstance, nil);
SendDlgItemMessage(hForm1, ID_DlgChdBut, WM_SETFONT,
                   GetStockObject(ANSI_VAR_FONT), Zero);

hScrollBar := CreateWindow('SCROLLBAR', 'sb', WS_CHILD or SBS_VERT or
                           WS_VISIBLE or WS_TABSTOP, DoSize(18), DoSize(8),
                           DoSize(14), DoSize(230), hForm1, Zero, hInstance, nil);
{this scroll bar is created with the SBS_VERT, so it will be a vertical
scroll bar, you need to set the WS_TABSTOP, so this will get keyboard input}


if not SystemParametersInfo(SPI_GETWORKAREA, Zero, @workRect,Zero) then
SetRect(workRect, Zero, Zero, GetSystemMetrics(SM_CXSCREEN),
        GetSystemMetrics(SM_CYSCREEN)-DoSize(32));
{the Scroll Bar on the main Form will move the Dialog box up and down
on the screen. I need to get the work Rectangle of the screen,
so I can set the ScrollInfo1 (scroll bar info, the scroll bar's range,
page, and positioning) with the work rectangle's
measurements which will set the scroll bar's range and position}

DlgHPos := (GetSystemMetrics(SM_CXSCREEN) shr One) - DoSize(130);
{the DlgHPos has the X position of the Dialog window}

SInfo1.cbSize := SizeOf(TScrollInfo);
{I will use the SInfo1 to set the scroll bar with the SetScrollInfo( )
function, The values in this SInfo1 will be used in the scroll bar
message processing to get the Minimum and Maximum values, page and Line sizes}
SInfo1.fMask := SIF_POS or SIF_RANGE;
SInfo1.nMin := workRect.Top;
{SInfo1.nMin will hold the minimum scroll bar range}
SInfo1.nPos := (GetSystemMetrics(SM_CYCAPTION) +
                       (Two * GetSystemMetrics(SM_CYDLGFRAME)));
{the GetSystemMetrics(SM_CYCAPTION) will get the height of a normal Caption
the GetSystemMetrics(SM_CYDLGFRAME) will get the frame width}

{SInfo1 is the Scroll Bar Info, the nMin is the Work
Rectangle Top. The nMax is the Height of the work rectangle
minus the height of the Dialog Box}

{I will use the GetDialogBaseUnits to get the height of the Dialog box window}
SInfo1.nMax := workRect.Bottom - workRect.Top -
             (SInfo1.nPos+MulDiv(120+DoSize(7),HIWORD(GetDialogBaseUnits), 8));
{the SInfo1.nMax will hold the Scroll Bar Maximum, which is the
WorkRectangle Height minus the Dialog's height}
{MulDiv(120+DoSize(7),HIWORD(GetDialogBaseUnits), 8)  will give you the Client Area
Height of the Dialog Box, the height in the templete is 120, which needs
to be mutiplied by the HIWORD(DialogUnits) and divided by eight. Then I add
SInfo1.nPos for the Caption and subtarct that fron the workRect height}
SInfo1.nTrackPos := SInfo1.nMax div 100;
{SInfo1.nTrackPos is NOT a Track position, it is the amount of pixels the
scroll bar will move for a "Line UP or DOWN" click or key input, since
the nTrackPos is Not used in the SetScrollInfo function, I will use it to
store the "Line" size for this scroll bar}
SInfo1.nPage := SInfo1.nMax div 14;
{SInfo1.nPage will NOT be used by the SetScrollInfo function, since the
SIF_PAGE flage is Not included, but I use it to store the amount of pixels the
scroll bar will move for a "Page UP or DOWN" click or key input}

SInfo1.nPos := SInfo1.nMax shr One;
{the ScrollInfo1.nPos variable will hold the Scroll Bar's Position which
is also the vertical Y position of the Dialog Box window,}

SetScrollInfo(hScrollBar, SB_CTL, SInfo1, False);
{the SetScrollInfo function will set the parameters of a scroll bar according
to the Flags set in the ScrollInfo1.fMask, so here it will set the Range and
Position}

ShowWindow(hForm1, SW_SHOWDEFAULT);
while GetMessage(mainMsg,Zero,Zero,Zero) do
  if not IsDialogMessage(hForm1, mainMsg) then
  begin
  TranslateMessage(mainMsg);
  DispatchMessage(mainMsg);
  end;
DeleteObject(Font1);
DeleteObject(Brush1);
end.
Be sure to move all of the scroll bars and see the effect that they have. You might move the position of the Dialog Window while it is visible, and see that the scroll bar on the main form move with it's position. Be sure to Un-Comment the SB_THUMBPOSITION and CanScroll in the WM_VSCROLL message, to see what this does to the Dialog window movement by draging the thumb bar with the mouse.

                           

Next
We have used a Dialog. It's time to learn how to create and use Menus and Listboxes in Lesson 10
  10. Menus and List Boxes


       

Lesson -     One  Two  Three  Four  Five  Six  Seven  Eight  Nine  Ten  Eleven  Twelve  Thirteen  Fourteen




H O M E