Results 1 to 19 of 19

Thread: Resizeable VB6 UserForms (and Unicode Form Captions)

  1. #1

    Thread Starter
    Hyperactive Member
    Join Date
    Feb 2015
    Location
    Colorado USA
    Posts
    261

    Resizeable VB6 UserForms (and Unicode Form Captions)

    This system uses a small amount of code in a form module plus a small class module shared by all of your forms to enable you and the user to be able to move and size any form and have all of the controls on the form resize properly. In summary:


    • Form and control resizing is available (including use of the maximize button) all of the time for all new and existing forms. This requires 2 variable declarations and 4 lines of code in each form.
    • The programmer has control over whether resize is allowed, what controls will resize, whether the form retains its height to width ratio as it resizes, etc.
    • Form sizing routines are available to make a form a certain percentage of the screen size, regardless of the screen resolution and size.
    • A form can be maximized without getting distorted.
    • A form’s last size and position can be saved and then restored the next time the form is used. This data can be saved to a file or to the registry. This takes only 10 lines of code for each form.
    • As a bonus, you can now easily set the form title (caption) with any Unicode string you want.
    • Minimal use of Windows API (2 calls).



    Class Module

    In a form that you want to be resizable you should put the following code (cut and paste if necessary). There are more optional things you can add that will be discussed later but the simplest code to provide comprehensive resizing requires only the following a few lines of code in your form (not clResizer):

    First, ensure that the BorderStyle property of the form is set to “2 – Sizable”.

    In the declaration section which is below the Option Explicit statement (which you definitely should be using):

    Code:
    Private frmResize As New clResizer
    Public UniCaption As String
    Then if you have a Form_Load procedure (if not, make one), put this line in it:

    Code:
    frmResize.NewSetup Me ' put after any form setup code in this subroutine
    That’s all that is required to have form resizing that also makes all of the controls on your form resize along with the form and to prevent everything on the form from being distorted by keeping the form’s height/width aspect ratio the same as the original form.

    Normally you will want the form to appear in the same size and position on the user’s screen each time the form is displayed. We will cover various other options later but to have automatic save and restore of your form’s size and position, modify the Form_Load routine as shown below and modify or add the Form_Unload routine as shown below.

    Code:
    Private Sub Form_Load()
    frmResize.NewSetup Me ' put after any form setup code in this subroutine
    If frmResize.GetFormParmsFromFile(App.Path) = 0 Then ' specify "" to read from the registry
       ' either first time through (file does not exist) or had read error
       frmResize.CenterForm 30, 30, True ' center form on screen and make 30% the width of screen
       frmResize.MoveForm 5, 5, False
       End If
    End Sub
     
     
    Private Sub Form_Unload(Cancel As Integer)
    ' put any other code you need to save data from the form here
    If Not Cancel Then
       ' This is our last shot at the form before unloading. It is possible that you
       '  have code to just hide the form and in that case we don't need to save the
       '  form settings because sometime later before the program ends this Form_Unload
       '  routine will be called.
      
       ' Don't write to App.Path in a real EXE, Windows no longer allows writing files
       ' to the Program Files folders
       frmResize.SaveFormParms2File App.Path ' specify "" to write to the registry
       End If
    End Sub

    Displaying a Unicode Caption

    There are several peope and companies who provide Unicode controls to put onto a Form but there is no native way of putting a Unicode title on the base Form itself. This is especially frustrating since VB6 deals with Unicode directly. The problem is that the IDE editor doesn’t do Unicode nor does the code that sets up the form when it is displayed. There is now a public variable in each form called UniCaption. If you set this variable to any string then when the form is displayed UniCaption will be the form caption instead of whatever was used previously as Form.Caption. If you leave this variable blank then whatever you had set as the Form.Caption is used for the Form caption.

    Suppose you have a form named fmTestMe. Suppose you want the string “あいうえお Caption” to be displayed as the caption of fmTestMe. If it never changed you could put the following line in the Form_Load sub of the form:

    Code:
    UniCaption = ChrW$(&H3042) & ChrW$(&H3044) & ChrW$(&H3046) & ChrW$(&H3048) & _
    ChrW$(&H304A) & " Caption"
    Alternatively, you can set the variable from a normal module or another class module or form by specifying the form name (the following snippet assumes the form name is TestForm):

    Code:
    TestForm.UniCaption = ChrW$(&H3042) & ChrW$(&H3044) & ChrW$(&H3046) & ChrW$(&H3048) & _
    ChrW$(&H304A) & " Caption"
    If the value of UniCaption is set before anything else is done with the form then the code you put in the Form_Load routine that calls NewSetup not only sets the size and location of your form, it also takes the value for UniCaption and sets the form caption with it.

    But suppose you want to change the caption one or more times after it has been displayed. First, put the following simple routine in your form code so you can get to the variable and procedure in the class module:

    Code:
    Public Sub SetUnicodeCaption(Caption As String)
    UniCaption = Caption
    frmResize.ShowUniCap
    End Sub
    And then whenever you want to change it after the form has been displayed you would call it like this (if the form name was TestForm and the new Caption string was the variable MyNewCaption):

    TestForm.SetUnicodeCaption(myNewCaption)


    Form Design Considerations

    Fonts

    Since the objective of this system is to enable making your forms larger and smaller with corresponding change in the size and fonts of each control, you should avoid the use of raster fonts in your forms since these scale extremely poorly. Typical Windows raster fonts include:

    8514oem Regular
    ADMUI3Lg Regular
    ADMUI3Sm Regular
    Courier Regular (there is a TrueType version called Courier New that is okay)
    Fixedsys Regular
    Modern Regular
    MS San Serif Regular (this is the VB6 standard font)
    MS Serif Regular
    Roman Regular
    Script Regular
    Small Fonts Regular
    System Bold
    Terminal

    List/Combo Boxes
    Drop-down boxes appear to size properly. If you use a list or combo box that shows more than one items at a time, it is possible that as you resize the form the text at the bottom gets dropped off and a vertical scrollbar appears. That’s because these controls size their own fonts based on the vertical size of the box. I have rarely seen this behavior be any problem but when I did I just set the control’s IntegralHeight property to False and gave a tiny bit more room at the bottom of the control.

    Setting the Form’s Initial Size and Position

    Below are some techniques for setting the initial position of the form. My recommendation is to initially put the form on the same screen as VB and since it is resizable and moveable the user will put it wherever it works best and then we will save and restore that size and position for future re-use.

    Because of the way forms work, once it is displayed the programmer has little control over the position of the form. Generally you will be more concerned about what the user does to items on the form and you won’t be too worried about where the form is or how large it is as long as the user can put it wherever he/she likes and can make it as large or small as desired.

    Programatically we can respond to the Resize event (which we already do) but that is largely driven by the user who is resizing the form. I suppose you could catch this re-sizing event and do something different but I don’t know what. There is no easy way to catch a Move event and the whole purpose of this system is to let the user move the form and re-size as he/she sees fit. So this means that in general we would want to move and or re-size the form just before it is being displayed via the Form.Show command.

    You can put code in the Initialize event for the form but keep in mind that at this point we have not yet had Windows make the form resizable so any attempt to resize the form will not work. Also, if you try something like the following in another module it will not work either:

    Code:
    fmTestMe.CenterForm(50, 50, True)
    fmTestMe.Show
    Anything in a normal module before the Show command basically causes the Initialize event to fire and our code will be executing before the Windows call to enable resizing. The resizing code is called in the Load event which is after Initialize and just before the form is displayed.

    The only way I know of to get code to affect the form after the Show statement is if the form has been Hidden instead of Unloaded.

    My recommendation is to decide what you want to do regarding the form size and location and put the code to do this in the Load event procedure in the code for the Form. You have 3 routines you in the Class module for form location that enable you set the size and position to be centered or anywhere on the screen and with little effort you can derive many others. The code to access these 3 routines will need to be in your Form module code.

    Code:
    Sub CenterForm(WidthPerCent As Single, HeightPerCent As Single, Limit2Screen As Boolean)
    This class module sub enables you to center and optionally resize the form.


    • To size the form based on the available screen width and height
      • WidthPerCent and HeightPerCent are the %'s of the screen width and height respectively
      • To make a form fill up half of the screen width regardless of the screen size and resolution you would specify the following in the form’s Load procedure:


    Code:
    frmResize.CenterForm 50, 50, True
    • Note – As long as Zoomable is True (default), the setting for HeightPerCent is ignored because the code determines the required height to keep the height/width ratio constant.





    • To size the form based on the original size of the form
      • WidthPerCent and HeightPerCent are based on the original form size but negative
      • To make a form be twice the size of the original form you would specify the following in the Load procedure of your form:


    Code:
    frmResize.CenterForm -200, -200, True  ' for 200% but negative
    • Note – As long as Zoomable is True (default), the HeightPerCent parameter is ignored.


    • If limit2Screen is True then the form size is adjusted as necessary to keep it all onscreen.



    Code:
    Sub MoveForm(WidthPerCent As Single, HeightPerCent As Single, Limit2Screen As Boolean)
    This class module sub enables you to move the form and optionally keep it onscreen.


    • To move the form based on the available screen width and height
      • WidthPerCent and HeightPerCent are the %'s of the screen width and height respectively
      • To make a form’s upper left corner go to the middle of the screen regardless of the screen size and resolution you would specify the following in the form’s Load procedure:


    Code:
    frmResize.MoveForm 50, 50, True
    • Note – As long as Zoomable is True (default), the setting for HeightPerCent is ignored because the code determines the required height to keep the height/width ratio constant.



    • To move the form to the specific left and top coordinates
      • WidthPerCent and HeightPerCent are the specific form position values for Left and Top
      • To make a form go to the top left of the screen you would specify this in the Load procedure:


    Code:
    Frmresize.MoveForm 0, 0, True
    • Note – As long as Zoomable is True, the HeightPerCent parameter is ignored.


    • If limit2Screen is True then the form size is adjusted as necessary to keep it all onscreen.


    So if we wanted to make our form be 65% the width of the screen (whatever that may be) and also displayed with the upper left corner 5% of the screen width and height from the screen’s upper left corner we could have a Form_Load routine that looks like this:

    Code:
    Private Sub Form_Load()
    frmResize.NewSetup Me
    frmResize.CenterForm 65, 65, True ' center form on screen and make 65% the width of screen
    frmResize.MoveForm 5, 5, False
    End Sub

    Continued below...
    Attached Files Attached Files
    Last edited by MountainMan; Dec 6th, 2016 at 10:51 PM. Reason: v1.1 See post #9

  2. #2

    Thread Starter
    Hyperactive Member
    Join Date
    Feb 2015
    Location
    Colorado USA
    Posts
    261

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    The previous 2 routines set the form size and position from fractions of the screen size which is generally what you want to do with a form that you designed of a fixed size that has to be displayed on monitors of unknown size. However, if you want to specify where the screen goes by it’s position you would use the FormSize routine shown above. This routine works whether or not you have Resizing on or off but it does honor your settings for Zooming and FontResizing (all of these are discussed next).

    Code:
    Public Sub FormSize(Left As Single, Top As Single, Width As Single, Height As Single, _
    ByVal RatioControl As Boolean)
    One of the passed parameters is RatioControl which is a temporary override for the form aspect ratio control (also discussed below). If you set this True in the call then the width is set but the height is adjusted to meet the form’s original height to width ratio. Generally you will set this True. My preferred parameter is to pass KeepRatio for RatioControl so that it uses whatever I have RatioControl set for (which 99.9% of the time is True anyway…).

    As an example, if you wanted to display the form in the top left of the screen at 1.5 times its original width and height you could use the following line of code in the form module:

    Code:
    frmResize.FormSize 0, 0, Me.Width * 1.5, Me.Height * 1.5, True)

    Controlling the Form

    Below is a discussion of the 4 boolean variables you can use to control how the form behaves when the form’s user attempts to resize the form. You can control whether the form stays resizable, whether it’s aspect ratio (height to width ratio) stays the same, whether the controls on the form change size as the form changes size and whether the font size changes for each control as its size changes.

    All of these are set True in the initialization code so normally you would not have to do anything. I don’t know why you would want to set any of them False but the capability is there for you to do so.

    Normally in the Form_Load event Sub in the form I have the following statement that sets all of the form parameters including these 4 control varaibles.

    Code:
    frmResize.NewSetup Me
    At this point the form is not visible. If you want to change any of the 4 variables, please do it after this line of code otherwise NewSetup will change your variable setting(s) back to True.

    The sample program has a button on its form for each of the 4 variables so that you can see for yourself what the effects are of setting one or more of them to False (i.e., unchecked box on the form).


    Resizing On or Off

    If the class module variable ResizingActive is set to False, no resizing is allowed. You can turn it off or on as desired from the form’s code module. The following shows how to turn it off:

    Code:
    frmResize.ResizeActive = False
    While ResizeActive is False, no resizing occurs and none of the other 3 variables discussed below have any effect regardless of setting.

    Form Aspect Ratio

    If a form is resized, it is normally desirable to have all the controls on the form change size with the form itself. When this happens we all want the ratio of the form height to width to stay the same so that we don’t get distorted-looking text or graphics. The class module clReszer manages this but in order for it to work well we need to keep the aspect ratio the same. When you re-size a form the code continuously checks this ratio and keeps it the same. Run the sample and drag the lower right of the form to change size and notice that it won’t allow you to make the form tall and skinny or short and fat.

    Normally you don’t do anything to enable this and it all just works. But if for some reason you want to display a distorted form you can by turning off the variable KeepRatio in the class module which is easy from the Form code:

    Code:
    frmResize.KeepRatio = False
    While KeepRatio is False everything on the form gets distorted when the user resizes it other than fonts.


    Zooming

    Resizing a form is generally not of much use if the size and position of all of the controls on the form do not change. A large part of the code in the class module is designed to move and resize individual controls on the form whenever the form is resized. If for some reason you don’t want the form controls to move and change with the form’s changing size you can turn off this effect by setting the class module variable Zoomable to False like this (code in the Form):

    Code:
    frmResize.Zoomable = False
    Be aware of something unusual that happens when you maximize a form. Whether your form has Minimize or Maximize buttons is set by the form properties MinButton and MaxButton respectively. Unless your form has the same aspect ratio (height to width ratio) as your screen, when it is maximized it will either have some blank space inserted on the bottom or the form or the right side. If Zoomable is False this blank area will be much larger. If KeepRaio is False the forms on the control will look very distorted. You can experiment with this in the example project.


    Font Resizing

    Normally when controls change size we change the fonts sizes to go with them. This is controlled by the variable CanResizeFonts in class module. If you change the default value of True into False then the fonts don’t change size as the form and controls change size and the text will essentially be too large for the form if the user is shrinking the form or the fonts will get too small if the user is enlargening the form. You could turn this automatic resizing feature off (don’t know why but you can) from the form’s code with this:

    Code:
    frmResize.CanResizeFonts = False

    Individual Controls Resizing (or Not)

    Each control has a “Tag” property which is designed for your use and it typically is unused. If you put the string “Skip” (without the quotes) into the Tag property for a control then when the form is resized that control is not resized. “Skip” can be lower, upper or mixed case.


    Saving and Restoring the Form Position

    A relatively easy thing to do is to save the form’s size and position at the end of the form’s life and then restore that the next time the form is used. You need to decide where to store the form data. There are 4 pieces of data you need to save and later retrieve, the form left, top, width and height values which are all Single data types in my code. Note that this is itotally optional; you do not need to save or restore any of the settings.

    There is a sister version of this system that allows resizing of forms using VBA in hosts like Excel and Word. The example for that system shows saving and restoring data to/from and Excel sheet. In the system described here I have two simple methods in the class module that you can use. One method uses the registry and one uses a small file. As a developer I don’t like to use the registry in development because the name of the program and the names of the forms can and do change a lot, leaving previous registry entries orphaned. But as a user of a program that I have installed into C:\Program Files I can no longer reliably use a file in the program folder because Windows doesn’t like us writing to files in Program Files or Program Files (x86).

    So, combining these two concepts, I use the write-to-file option while I am developing and then I switch to using the registry when my program is stable and doesn’t have the chance of names changing. You are free to use either method or to make up your own.

    This is as simple as when the form is about to be displayed we get the data from the last time it was displayed and we use that for the starting position and then later just before the form is unloaded we will save the parameters so we can sue them the next time the form is loaded whether that is during the same program run or in the future. We do have to take into account that the first time the user loads the form the ending code to save the size/position has not yet run so we don’t have any data to restore at that time. Other than that, saving and restoring form size and position is simple.

    Note – Most of us who develop software have multiple monitors. Many users do but some don’t. If you are using the registry to track your form size and position your users will be okay since their PC will have its own registry. However, if you are using files, ensure that you do not include the positioning files with the package you deploy. If you have the form coming up on a secondary monitor and the user only has one monitor, I am sure you will be getting an irate phone call or email…

    Saving Form Size and Position Data

    As a form is being unoaded, 3 events are triggered that you could possibly use for code to save the form’s parameters. The last one is Form_Terminate and by that point the size and position data have been discarded so you can’t use that one. Form_QueryUnload and Form_Unload are the other two. Both have a Cancel option. Form_QueryUnload is typically used to possibly cancel the form unload depending upon where the call is coming from (e.g., some forms you don’t want to close and unload if it is the result of the user clicking the “X” in the top right of the form).

    You can also use the Form_Unload sub which also has a Cancel button. If Cancel is not set True then this is also just as good of a place for code to store the form size/position. There is a slight difference between QueryUnload and Unload. MSDN says: When an application closes, you can use either the QueryUnload or Unload event procedure to set the Cancel property to True, stopping the closing process. However, the QueryUnload event occurs in all forms before any are unloaded, and the Unload event occurs as each form is unloaded.

    I recommend using the Form_Unload statement because it is possible for the Form_QueryUnload statement to go through okay and then have Cancel set to True in Form_Unload such that the form is not really unloaded. So I use something like the following:

    Code:
    Private Sub Form_Unload(Cancel As Integer)
    ' put any other code you need to save data from the form
    
    If Not Cancel Then
       ' This is our last shot at the form before unloading. It is possible that you
       '  have code to just hide the form and in that case we don't need to save the
       '  form settings because sometime later before the program ends this Form_Unload
       '  routine will be called.
       
       ' Don't write to App.Path in a real EXE, Windows no longer allows writing files
       ' to the Program Files folders.
       frmResize.SaveFormParms App.Path ' specify "" to write to the registry
    End Sub
    This checks the Cancel variable to make sure it is not True (if it is the form isn’t going to be unloaded) and then it calls the function SaveFormParms in the class module clResizer to save the form parameters to either the registry or to a file.

    Above we show “App.Path” being used as the passed string to SaveFormParms. This parameter is the path to the file we use to hold the form position data. It can be anything you want. Once a program is deployed to others such that it will be installed in something like “C:\Program Files (x86)” then you probably don’t want to use App.Path because Windows really doesn’t like anyone writing data to the Program Files folders any more. You could use C:\ProgramData and make a folder for you application.

    The name of the file that is made (if one already exists it is overwritten) is the name of the form followed by "_FormPos.dat". For example, for a form named MyForm the filename is “MyForm_FormPos.dat”. But you don’t need to be concerned about the name since the code we discuss int eh next section to restore the form’s settings when it is loaded looks for this file. You don’t have to do anything with it.

    To instead save the data to the rregistry, pass the string as a blank string. The line above would then be:

    Code:
    frmResize.SaveFormParms "" ' specify a path to write to a file
    When you specify use of the registry, the data are stored inThe data are stored in the registry in “HKEY_CURRENT_USE\Software\VB and VBA Program Settings\” and your project name will eb the Appname and the section containing all of the settings for a form will be keys in a section named after your form name. You don’t really need to do anything with that information unless you want to get rid of the saved data which we wil cover in a bit.

    During development I often change my Project name and my form names so if I use the registry before I quit changing those, I wind up with orphaned form sections in the registry. My recommendation is to use App.Path while you ae in development mode and when you get ready to deploy the package to others you switch to using the registry by changing App.Path to "".

    Suppose for some reason you want to delete the saved form settings. There is a routine at the end of the class module just for this. From within the form, such as within Form_Load procedure you call “DeletePriorSave” and if you pass the path then the settings file will be deleted. If you pass the blank string "" then the registry entries for the form are deleted. For example, to delete the registry settings for a form you would put the following line after you call NewSetup:

    Code:
    frmResize.DeletePriorSave "" ' Kills the registry settings for the form
    OR

    Code:
    frmResize.DeletePriorSave App.Path ' Kills the file save settings for the form
    Last edited by MountainMan; Dec 6th, 2016 at 10:56 PM.

  3. #3

    Thread Starter
    Hyperactive Member
    Join Date
    Feb 2015
    Location
    Colorado USA
    Posts
    261

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    Restoring Form Size and Position

    The place to restore the saved data is in the form’s Load procedure (don’t do it in Initialize because the form is not resizable at that time). Don’t forget to have a check on the data to make sure it is not 0 (i.e., you haven’t yet save the size/position data) or else the code will fail. You had used SaveFormParms to save the data and now in Form_Load you will call GetFormParms to retrieve the data.

    If the data can’t be retrieved the function returns 0 (False). This is what happens the first time you run the code since it has not yet gone to a Form_Unload sub where it will save the data. The example below shows how I handle this by setting the form to be 30% of the main monitor’s width and positioning it where the top left of the form is 5% from the left edge and 5% from the top of the form. If the routine does find the data, it will be re-positioned and re-sized to whatever the user had done the last time the program ran. It even works if the user had maximized the form and then saved it. Not only is the size and position restored but also the values for zooming, font resizing, etc. are restored. It’s like the form was never closed.

    Code:
    If frmResize.GetFormParms(App.Path) = 0 Then
       ' either first time through (file does not exist) or had read error
       frmResize.CenterForm 30, 30, True ' center form & make 30% the width of screen
       frmResize.MoveForm 5, 5, False
       End If
    Just like for SaveFormParms, if you specify a blank string to be passed, GetFormParms looks into the Windows registry; otherwise it treats the passed string as a path to the parameter file.

    If we restore the form to the last place it was displayed, what happens if the monitor it was on is removed? In this case we ignore the Left and Top positions for the screen and we use new Left and Top parameters to ensure that the form is displayed on a screen. If you want to learn more about the space Windows uses for monitors, see this article and this article by Karl E. Peterson regarding his code for multiple monitors.


    Sample Projects

    Resizer Sample 1

    The displayed form contains an image and 4 check boxes. As you drag it around the screen and resize it, it should continue to look just like the same albeit at different sizes. I would put a picture of the form here but the 500kb upload limit gets broken.

    Notice the 4 check boxes. These are the 4 boolean form controls discussed earlier. You can clear or set any or all of the check boxes and then observe the effects on the form. Nothing you do while the form is displayed is saved or restored so you can distort the form all you want…


    Resizer Sample 2

    The second sample is a simple form with various controls on it. There is a list box that allows you to select a short song to be played using the PC’s speakers (the API Beep statement). This example also shows how the from can be moved and resized. Unlike the first sample, this one saves its size and position. It is set up to write a small file to the same folder where you put the sample. If you would rather write to/from the registry that is easily done by replacing “App.Path” in the Load and Unload sub with "". The next time you show this form in this sample project the form will be in the same place it was when it was unloaded the previous time. This includes maximizing the form on one of your screens.


    Class Module clResizer

    Using this module is very simple from the programmer’s standpoint. Normally we have to use “Set” to get the class module going and “Unload” to end the module and free up variable memory. Generally you should not use the New keyword in the Dim statement for the class module because the class module is instantiated when the module is run you can never get rid of it. However that’s just what we want here. We want to get an instance of the class module when the form gets initialized and we want it to go away when the form goes away. So in the form module we have the line:

    Code:
    Private frmResize As New clResizer
    and we don’t have to think about it further. It is there for access by our form for the whole time the form exists and we can use it at will and not worry about getting rid of it later because it goes away when the form does.

    The class module does all of the calculations for the screens, form and positioning. It is much better to have all of this in one class module shared by all resizable forms instead of putting the code into each form.

    Only two Windows API calls are made, a call to DefWindowProc which we use to set the Unicode caption for our form and one when the form is setup that determines the available virtual screen area.

    Code:
    Private Declare Function DefWindowProcW Lib "user32" ( _
    ByVal hWnd As Long, _
    ByVal wMsg As Long, _
    ByVal wParam As Long, _
    ByVal lParam As Long) _
    As Long
    
    Private Declare Function GetSystemMetrics Lib "user32.dll" (ByVal nIndex As Long) As Long

    Visual Basic for Applications (VBA)

    VBA has a similar but simpler forms package than VB6. A system very similar functionality to the VB6 Form Resizer has been developed and is available for VBA forms.
    Last edited by MountainMan; Dec 6th, 2016 at 11:16 PM.

  4. #4
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    673

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    good code..thanks to share

  5. #5
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    FYI

    Code:
    Private WithEvents Frm As Form
    will let your class "listen" / subscribe to the Form's Resize event.

    Also you might want to make your sizing code Multi-Monitor aware.

  6. #6

    Thread Starter
    Hyperactive Member
    Join Date
    Feb 2015
    Location
    Colorado USA
    Posts
    261

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    The code is inherently multi-monitor aware. If the user moves the form onto a different monitor and then saves to the registry or a file, the next time the code is run, the form comes up wherever it was when it was saved. What I didn't do is to allow the programmer pick the starting monitor. I thought a lot about doing that and even have the code for it but I took it out because:

    1) Rather than the programmer deciding which monitor to use in the multi-monitor setup I decided that with the save/restore the user can put it wherever he/she wants one time and then it is there from then on. I thought that was a better solution than the programmer picking the monitor, especially since the programmer really has no idea what configuration they may be (1, 2, 4 or some other number of monitors, which one the user might want the main or child forms on, since dimensions of each may be different then the coding to ensure the form fits onscreen gets more complicated, etc.). I trust the user will put the form wherever is best and be done with it.

    2) the code to play with an unknown number of monitors with potentially different sizes seemed too much hassle for a little gain, if there is gain at all. I have the code that allows the programmer to put the form anywhere on any monitor if anyone wants it.

    Regarding the WithEvents comment, I mainly work with VBA code and we don't do a whole lot of that there so I will have to look into it. Thanks for the suggestion.

  7. #7
    Frenzied Member
    Join Date
    Mar 2008
    Posts
    1,210

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    >If the user moves the form onto a different monitor and then saves to the registry or a file, the next time the code is run, the form comes up wherever it was when it was saved.

    Not a good scenario if a Form was last displayed/ closed on a monitor which is subsequently disconnected because the Form will then next be displayed off-screen. That situation should be detected so the Form can be/ is shown on an available monitor.

  8. #8
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    MonitorFromWindow with MONITOR_DEFAULTTONEAREST gets a handle to closest monitor
    and then use GetMonitorInfo to get the working rect of that particular monitor (rcWork).
    then you can use those coordinates for centering/clipping and calculating percentages.

    I wouldn't mess with the initial startup screen of a form, but if you're restoring it to another monitor I would at a minimum clip it to the current available monitor on startup. (Like Calc.EXE)

  9. #9

    Thread Starter
    Hyperactive Member
    Join Date
    Feb 2015
    Location
    Colorado USA
    Posts
    261

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    v1.1 is now in post #1 and all the text in #1-#3 is updated to reflect some changes.

    I added code in the class module to detect each time the form is moved/resized to ensure that if the top left corner of the form is outside of the range of the virtual desktop coordinates it is put back in the virtual space. The only way that doesn't correspond to a "real" monitor is if there are weird gaps in the virtual desktop space (see this article). I think the odds of a monitor disappearing is small and then further that the bottom right of the virtual space is out of view of any real monitor is even smaller.

    I also took Dexwerx's advice and put the Resize event in the class module which meant even fewer lines required in each form. Now the basic resizability of the form and all of its controls only takes 3 lines of code in each form.

  10. #10
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    Quote Originally Posted by MountainMan View Post
    I think the odds of a monitor disappearing is small
    That's because you don't use multiple multi-monitor systems with KVM(s).

    I have 3 systems (2 with dual-video, 1 with 3 video) on separate disconnected networks, and also a corporate machine/laptop setup where I use the laptop screen in conjunction with a monitor, and I unplug the monitor often.

    disappearing monitors is an everyday occurrence.

    luckily all the apps i use respond to WM_DISPLAYCHANGE

  11. #11
    PowerPoster Arnoutdv's Avatar
    Join Date
    Oct 2013
    Posts
    5,871

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    Quote Originally Posted by DEXWERX View Post
    MonitorFromWindow with MONITOR_DEFAULTTONEAREST gets a handle to closest monitor
    and then use GetMonitorInfo to get the working rect of that particular monitor (rcWork).
    then you can use those coordinates for centering/clipping and calculating percentages.
    Quote Originally Posted by DEXWERK
    disappearing monitors is an everyday occurrence.

    luckily all the apps i use respond to WM_DISPLAYCHANGE
    Do you have a sample for this?

  12. #12
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    Quote Originally Posted by Arnoutdv View Post
    Do you have a sample for this?
    Here's my CenterWindow function. If I remember correctly it's a direct translation from MFC.
    Code:
    'C Style Logical Not (!) vs VB Bitwise Inversion (~)
    'C's ! operator does an implicit conversion to bool before inversion
    Public Function NOT_(ByVal Expr As Long) As Long
        'NOT_ = Not CBool(Expr)
        NOT_ = (Expr = 0&)
    End Function
    
    Public Function CenterWindow(hWnd As Long, Optional hWndCenter As Long) As Long
        If (hWndCenter) Then
            Dim dwWindowStyle As Long
            dwWindowStyle = GetWindowLong(hWndCenter, GWL_STYLE)
            If (NOT_(dwWindowStyle And WS_VISIBLE) Or (dwWindowStyle And WS_MINIMIZE)) Then _
                hWndCenter = NULL_
        End If
    
        Dim mi As MONITORINFO
        mi.cbSize = LenB(mi)
        Dim rcCenter As RECT
        Dim rcScreen As RECT
        If (hWndCenter) Then
            Call GetMonitorInfo(MonitorFromWindow(hWndCenter, MONITOR_DEFAULTTONEAREST), mi)
            rcScreen = mi.rcWork
            Call GetWindowRect(hWndCenter, rcCenter)
        Else
            Call GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), mi)
            rcScreen = mi.rcWork
            rcCenter = mi.rcWork
        End If
        
        Dim rc As RECT
        Call GetWindowRect(hWnd, rc)
        
        Dim W&, H&, X&, Y&
        W = rc.Right - rc.Left
        H = rc.Bottom - rc.Top
        X = (rcCenter.Left + rcCenter.Right - W) \ 2&
        Y = (rcCenter.Top + rcCenter.Bottom - H) \ 2&
        If (X < rcScreen.Left) Then
            X = rcScreen.Left
        ElseIf (X > rcScreen.Right - W) Then
            X = rcScreen.Right - W
        End If
        If (Y < rcScreen.Top) Then
            Y = rcScreen.Top
        ElseIf (Y > rcScreen.Bottom - H) Then
            Y = rcScreen.Bottom - H
        End If
        
        Call SetWindowPos(hWnd, NULL_, X, Y, -1&, -1&, SWP_NOSIZE Or SWP_NOZORDER Or SWP_NOACTIVATE)
    End Function
    as for responding to WM_DISPLAYCHANGE
    Code:
    Option Explicit
    
    Implements ISubclass
    
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowW" (Optional ByVal lpClassName As Long, Optional ByVal lpWindowName As Long) As Long
    Private Declare Function GetWindow Lib "user32" (ByVal hWnd As Long, ByVal uCmd As Long) As Long
    
    Private m_hWnd As Long
    
    Public Property Get hWnd() As Long
        hWnd = m_hWnd
    End Property
    
    Public Property Get Owner(ByVal hWnd As Long) As Long
        Const GW_OWNER As Long = 4&
        Owner = GetWindow(hWnd, GW_OWNER)
    End Property
    
    Private Sub Class_Initialize()
        If (Forms.Count) Then
            m_hWnd = Owner(Forms(0&).hWnd)
        Else
            App.Title = Right$("0000" & Hex$(ObjPtr(Me)), 4) & App.Title
            m_hWnd = FindWindow(0&, StrPtr(App.Title))
            App.Title = Mid$(App.Title, 5)
        End If
        SetSubclass m_hWnd, Me
    End Sub
    
    Private Sub Class_Terminate()
        RemoveSubclass m_hWnd, Me
    End Sub
    
    
    Private Function ISubclass_SubclassProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal dwRefData As Long) As Long
        Const WM_SETTINGCHANGE As Long = &H1A&
        Const WM_DISPLAYCHANGE As Long = &H7E&
        Const WM_KEYUP As Long = &H101&
    
        Select Case uMsg
        Case WM_DISPLAYCHANGE
            DebugOut "display change"
        Case WM_SETTINGCHANGE
            DebugOut "setting change!"
        Case WM_KEYUP
            DebugOut "KeyPress!"
        End Select
        ISubclass_SubclassProc = DefSubclassProc(hWnd, uMsg, wParam, lParam)
    End Function
    edit:
    NULL_ is the constant 0&
    Last edited by DEXWERX; Dec 8th, 2016 at 12:01 PM.

  13. #13
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    673

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    Quote Originally Posted by MountainMan View Post
    This system uses a small amount of code in a form module plus a small class module shared by all of your forms to enable you and the user to be able to move and size any form and have all of the controls on the form resize properly. In summary:


    • Form and control resizing is available (including use of the maximize button) all of the time for all new and existing forms. This requires 2 variable declarations and 4 lines of code in each form.
    • The programmer has control over whether resize is allowed, what controls will resize, whether the form retains its height to width ratio as it resizes, etc.
    • Form sizing routines are available to make a form a certain percentage of the screen size, regardless of the screen resolution and size.
    • A form can be maximized without getting distorted.
    • A form’s last size and position can be saved and then restored the next time the form is used. This data can be saved to a file or to the registry. This takes only 10 lines of code for each form.
    • As a bonus, you can now easily set the form title (caption) with any Unicode string you want.
    • Minimal use of Windows API (2 calls).



    Class Module

    In a form that you want to be resizable you should put the following code (cut and paste if necessary). There are more optional things you can add that will be discussed later but the simplest code to provide comprehensive resizing requires only the following a few lines of code in your form (not clResizer):

    First, ensure that the BorderStyle property of the form is set to “2 – Sizable”.

    In the declaration section which is below the Option Explicit statement (which you definitely should be using):

    Code:
    Private frmResize As New clResizer
    Public UniCaption As String
    Then if you have a Form_Load procedure (if not, make one), put this line in it:

    Code:
    frmResize.NewSetup Me ' put after any form setup code in this subroutine
    That’s all that is required to have form resizing that also makes all of the controls on your form resize along with the form and to prevent everything on the form from being distorted by keeping the form’s height/width aspect ratio the same as the original form.

    Normally you will want the form to appear in the same size and position on the user’s screen each time the form is displayed. We will cover various other options later but to have automatic save and restore of your form’s size and position, modify the Form_Load routine as shown below and modify or add the Form_Unload routine as shown below.

    Code:
    Private Sub Form_Load()
    frmResize.NewSetup Me ' put after any form setup code in this subroutine
    If frmResize.GetFormParmsFromFile(App.Path) = 0 Then ' specify "" to read from the registry
       ' either first time through (file does not exist) or had read error
       frmResize.CenterForm 30, 30, True ' center form on screen and make 30% the width of screen
       frmResize.MoveForm 5, 5, False
       End If
    End Sub
     
     
    Private Sub Form_Unload(Cancel As Integer)
    ' put any other code you need to save data from the form here
    If Not Cancel Then
       ' This is our last shot at the form before unloading. It is possible that you
       '  have code to just hide the form and in that case we don't need to save the
       '  form settings because sometime later before the program ends this Form_Unload
       '  routine will be called.
      
       ' Don't write to App.Path in a real EXE, Windows no longer allows writing files
       ' to the Program Files folders
       frmResize.SaveFormParms2File App.Path ' specify "" to write to the registry
       End If
    End Sub

    Displaying a Unicode Caption

    There are several peope and companies who provide Unicode controls to put onto a Form but there is no native way of putting a Unicode title on the base Form itself. This is especially frustrating since VB6 deals with Unicode directly. The problem is that the IDE editor doesn’t do Unicode nor does the code that sets up the form when it is displayed. There is now a public variable in each form called UniCaption. If you set this variable to any string then when the form is displayed UniCaption will be the form caption instead of whatever was used previously as Form.Caption. If you leave this variable blank then whatever you had set as the Form.Caption is used for the Form caption.

    Suppose you have a form named fmTestMe. Suppose you want the string “あいうえお Caption” to be displayed as the caption of fmTestMe. If it never changed you could put the following line in the Form_Load sub of the form:

    Code:
    UniCaption = ChrW$(&H3042) & ChrW$(&H3044) & ChrW$(&H3046) & ChrW$(&H3048) & _
    ChrW$(&H304A) & " Caption"
    Alternatively, you can set the variable from a normal module or another class module or form by specifying the form name (the following snippet assumes the form name is TestForm):

    Code:
    TestForm.UniCaption = ChrW$(&H3042) & ChrW$(&H3044) & ChrW$(&H3046) & ChrW$(&H3048) & _
    ChrW$(&H304A) & " Caption"
    If the value of UniCaption is set before anything else is done with the form then the code you put in the Form_Load routine that calls NewSetup not only sets the size and location of your form, it also takes the value for UniCaption and sets the form caption with it.

    But suppose you want to change the caption one or more times after it has been displayed. First, put the following simple routine in your form code so you can get to the variable and procedure in the class module:

    Code:
    Public Sub SetUnicodeCaption(Caption As String)
    UniCaption = Caption
    frmResize.ShowUniCap
    End Sub
    And then whenever you want to change it after the form has been displayed you would call it like this (if the form name was TestForm and the new Caption string was the variable MyNewCaption):

    TestForm.SetUnicodeCaption(myNewCaption)


    Form Design Considerations

    Fonts

    Since the objective of this system is to enable making your forms larger and smaller with corresponding change in the size and fonts of each control, you should avoid the use of raster fonts in your forms since these scale extremely poorly. Typical Windows raster fonts include:

    8514oem Regular
    ADMUI3Lg Regular
    ADMUI3Sm Regular
    Courier Regular (there is a TrueType version called Courier New that is okay)
    Fixedsys Regular
    Modern Regular
    MS San Serif Regular (this is the VB6 standard font)
    MS Serif Regular
    Roman Regular
    Script Regular
    Small Fonts Regular
    System Bold
    Terminal

    List/Combo Boxes
    Drop-down boxes appear to size properly. If you use a list or combo box that shows more than one items at a time, it is possible that as you resize the form the text at the bottom gets dropped off and a vertical scrollbar appears. That’s because these controls size their own fonts based on the vertical size of the box. I have rarely seen this behavior be any problem but when I did I just set the control’s IntegralHeight property to False and gave a tiny bit more room at the bottom of the control.

    Setting the Form’s Initial Size and Position

    Below are some techniques for setting the initial position of the form. My recommendation is to initially put the form on the same screen as VB and since it is resizable and moveable the user will put it wherever it works best and then we will save and restore that size and position for future re-use.

    Because of the way forms work, once it is displayed the programmer has little control over the position of the form. Generally you will be more concerned about what the user does to items on the form and you won’t be too worried about where the form is or how large it is as long as the user can put it wherever he/she likes and can make it as large or small as desired.

    Programatically we can respond to the Resize event (which we already do) but that is largely driven by the user who is resizing the form. I suppose you could catch this re-sizing event and do something different but I don’t know what. There is no easy way to catch a Move event and the whole purpose of this system is to let the user move the form and re-size as he/she sees fit. So this means that in general we would want to move and or re-size the form just before it is being displayed via the Form.Show command.

    You can put code in the Initialize event for the form but keep in mind that at this point we have not yet had Windows make the form resizable so any attempt to resize the form will not work. Also, if you try something like the following in another module it will not work either:

    Code:
    fmTestMe.CenterForm(50, 50, True)
    fmTestMe.Show
    Anything in a normal module before the Show command basically causes the Initialize event to fire and our code will be executing before the Windows call to enable resizing. The resizing code is called in the Load event which is after Initialize and just before the form is displayed.

    The only way I know of to get code to affect the form after the Show statement is if the form has been Hidden instead of Unloaded.

    My recommendation is to decide what you want to do regarding the form size and location and put the code to do this in the Load event procedure in the code for the Form. You have 3 routines you in the Class module for form location that enable you set the size and position to be centered or anywhere on the screen and with little effort you can derive many others. The code to access these 3 routines will need to be in your Form module code.

    Code:
    Sub CenterForm(WidthPerCent As Single, HeightPerCent As Single, Limit2Screen As Boolean)
    This class module sub enables you to center and optionally resize the form.


    • To size the form based on the available screen width and height
      • WidthPerCent and HeightPerCent are the %'s of the screen width and height respectively
      • To make a form fill up half of the screen width regardless of the screen size and resolution you would specify the following in the form’s Load procedure:


    Code:
    frmResize.CenterForm 50, 50, True
    • Note – As long as Zoomable is True (default), the setting for HeightPerCent is ignored because the code determines the required height to keep the height/width ratio constant.





    • To size the form based on the original size of the form
      • WidthPerCent and HeightPerCent are based on the original form size but negative
      • To make a form be twice the size of the original form you would specify the following in the Load procedure of your form:


    Code:
    frmResize.CenterForm -200, -200, True  ' for 200% but negative
    • Note – As long as Zoomable is True (default), the HeightPerCent parameter is ignored.


    • If limit2Screen is True then the form size is adjusted as necessary to keep it all onscreen.



    Code:
    Sub MoveForm(WidthPerCent As Single, HeightPerCent As Single, Limit2Screen As Boolean)
    This class module sub enables you to move the form and optionally keep it onscreen.


    • To move the form based on the available screen width and height
      • WidthPerCent and HeightPerCent are the %'s of the screen width and height respectively
      • To make a form’s upper left corner go to the middle of the screen regardless of the screen size and resolution you would specify the following in the form’s Load procedure:


    Code:
    frmResize.MoveForm 50, 50, True
    • Note – As long as Zoomable is True (default), the setting for HeightPerCent is ignored because the code determines the required height to keep the height/width ratio constant.



    • To move the form to the specific left and top coordinates
      • WidthPerCent and HeightPerCent are the specific form position values for Left and Top
      • To make a form go to the top left of the screen you would specify this in the Load procedure:


    Code:
    Frmresize.MoveForm 0, 0, True
    • Note – As long as Zoomable is True, the HeightPerCent parameter is ignored.


    • If limit2Screen is True then the form size is adjusted as necessary to keep it all onscreen.


    So if we wanted to make our form be 65% the width of the screen (whatever that may be) and also displayed with the upper left corner 5% of the screen width and height from the screen’s upper left corner we could have a Form_Load routine that looks like this:

    Code:
    Private Sub Form_Load()
    frmResize.NewSetup Me
    frmResize.CenterForm 65, 65, True ' center form on screen and make 65% the width of screen
    frmResize.MoveForm 5, 5, False
    End Sub

    Continued below...
    Code:
    If HeightPerCent > 0 Then H = -HeightPerCent & Screen.Height / 100! Else H = OHeight * -HeightPerCent / 100!
    what this mean?

  14. #14
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    597

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    Quote Originally Posted by xxdoc123 View Post
    Code:
    If HeightPerCent > 0 Then H = -HeightPerCent & Screen.Height / 100! Else H = OHeight * -HeightPerCent / 100!
    what this mean?
    He already explained:
    ' Centers the form onscreen.
    ' If WidthPerCent > 0 then value is the width of the form relative to the width of the screen.
    ' For example, 65 says make the form centered ont he screen with the width 65% of the
    ' screen regardless of the size and resolution of the screen.
    ' If WidthPerCent is < 0 then it is assumed that the negative of the value is the % multiplier
    ' of the original form size. For example, -200 says make the width 200% of the original form.
    ' HeightPerCent is the same as WidthPerCent except (obviously) it is for height, not width.
    ' Note that if KeepRatio is True then the height setting is ignored and it is set to whatever
    ' necessary to maintain the original height:width ratio.
    ' If Limit2Screen is True then the dimensinos might be adjusted to keep the form totally onscreen.
    Code:
       If HeightPerCent > 0 Then
          H = -HeightPerCent & Screen.Height / 100!
       Else
         H = OHeight * -HeightPerCent / 100!
       End If
    '! means Single
    Last edited by DaveDavis; Jul 30th, 2018 at 08:20 PM.

  15. #15
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    673

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    Quote Originally Posted by DaveDavis View Post
    He already explained:

    Code:
       If HeightPerCent > 0 Then
          H = -HeightPerCent & Screen.Height / 100!
       Else
         H = OHeight * -HeightPerCent / 100!
       End If
    '! means Single
    can you test if KeepRatio is not True?you can test !you will have a error !

  16. #16
    New Member
    Join Date
    Aug 2018
    Posts
    8

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    Buenas
    Cuando se cambia el tamaño del formulario parpadea mucho ¿se puede evitar esto?

    Translate:
    Good
    When you change the size of the form it blinks a lot, can you avoid this?

  17. #17
    New Member
    Join Date
    Aug 2018
    Posts
    8

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    Buenas
    Cuando se cambia el tamaño del formulario parpadea mucho ¿se puede evitar esto?

    Translate:
    Good
    When you change the size of the form it blinks a lot, can you avoid this?

  18. #18
    New Member
    Join Date
    Aug 2018
    Posts
    8

    Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    Disculpen, se repitió el mensaje por error y no puedo borrarlo

    TRANSLATE
    Excuse me, the message was repeated by mistake and I can not delete it

  19. #19
    Hyperactive Member
    Join Date
    May 2018
    Location
    Russia
    Posts
    315

    Exclamation Re: Resizeable VB6 UserForms (and Unicode Form Captions)

    v4.1 (2022) doesn't work with "true" unicode:
    Code:
    With frmResize
       .NewSetup Me
       mUniCaption = ChrW$(-17827)
       .SetParms mIsModeless, mUniCaption, mUseLastPos, mSavePosAtClose
    End With
    This method does work.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width