0

VB2012: In order to do some calculations in my main form I need to know the form size of a secondary form. The form size may change from user to user depending on OS and theme. I understand that the client size stays the same. However I think I am not doing something correctly as I get different numbers depending on where I call for the form size.

As an illustration here is my main form where on the load event I attempt to get the size of an Alert form

Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    'get the default width and height of an Alert form.
    Dim frmW As Integer = frmAlert.Width 'in pixels
    Dim frmH As Integer = frmAlert.Height 'in pixels
    Dim frCsW As Integer = frmAlert.ClientSize.Width 'in pixels
    Dim frmCsH As Integer = frmAlert.ClientSize.Height 'in pixels
    Debug.Print("frmW={0} frmH={1} frCsW={2} frmCsH={3}", frmW, frmH, frCsW, frmCsH)
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    'set up a new alert form
    Dim frm As New frmAlert

    'show the alert form
    frm.StartPosition = FormStartPosition.CenterParent
    frm.Show()          'with this option the Alert Forms stay on the screen even if the Main form is minimized.
End Sub

Now the Alert form is set with FormBorderStyle=FixedDialog, ControlBox=False, MaximizeBox=False, and MinimizeBox=False and in the Alert form I have this on the load event:

Private Sub frmAlert_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Debug.Print("me.width={0} me.height={1} cs.width={2} cs.height={3}", Me.Width, Me.Height, Me.ClientSize.Width, Me.ClientSize.Height)
End Sub

and here is the debug output

frmW=380 frmH=168 frCsW=374 frmCsH=162
me.width=390 me.height=200 cs.width=374 cs.height=162

As expected the client size is the same but the total form size is different. I am trying to wrap my head around the differences in .Height and .Width. No other code exists to change the form properties. The second debug statement matches the form Size in the IDE designer. Why are the dimensions different? How would I properly query to get the form size from another form?

sinDizzy
  • 1,300
  • 7
  • 28
  • 60
  • 2
    My guess is that because the form has not yet been shown when printing the first line, Windows hasn't added a border to it yet. The client size stays the same but the total size increases when the window is shown and the border is added, thus your second result. – Visual Vincent Apr 19 '18 at 20:31
  • That makes sense. now how could I determine the correct size? I am experimenting with SystemInformation.FixedFrameBorderSize, etc but even then I don't get the same answer. – sinDizzy Apr 19 '18 at 20:47
  • There you go vincent :) I deleted it for you – Zeddy Apr 19 '18 at 20:49
  • @Zeddy : Good :). FWIW if you wanted to post an image you could've uploaded it through the answer editor, then just copied the link to the comments. – Visual Vincent Apr 19 '18 at 20:52
  • This looks like it may be a _default form instance_ issue. In `frmMain_Load` you reference `frmAlert` (probable default instance) and in `Button1_Click` you are explicitly creating it `Dim frm As New frmAlert`. – TnTinMn Apr 19 '18 at 20:53
  • @sinDizzy : Without showing the form the answer is: you can't. You'll never be able to fully predict the size of the form since it all depends on the user's theme, DPI settings, etc. etc... I think your best bet would be to show it hidden at first (set `Opacity` to `0`), then check its size. – Visual Vincent Apr 19 '18 at 20:54
  • @TnTinMn : Default instance or not, the forms should be exactly the same when instantiated. It isn't (most likely) until he shows one of them that it changes its size. – Visual Vincent Apr 19 '18 at 20:56
  • @Vincent - Thanks Good to know you can upload an image and then add the link in the comments. – Zeddy Apr 19 '18 at 20:59
  • Thank you. I just tried it by doing Dim frm as new frmAlert and then frm.Show. It works! The problem here is that I have some code in the frmAlert.Load event that fires off reading of a text datafile. I don't want to do that just to get the form size. So maybe a global variable to tell it to skip that code block? – sinDizzy Apr 19 '18 at 20:59
  • A better way would be to create a custom constructor allowing you to do: `Dim frm As New frmAlert(False)` - `False` indicating that the process shouldn't run. – Visual Vincent Apr 19 '18 at 21:01
  • ok I am halfway done with that and it looks like a good solution. So I will post the solution once tested. – sinDizzy Apr 19 '18 at 22:06

2 Answers2

0

Before the form is shown it will have a smaller size compared to when it is visible. This is because when you show the form Windows will do all kinds of stuff to it based on the user's screen and theme settings, such as:

  • Resize it if the user has different DPI settings.
  • Apply borders to it based on the user's selected window theme.
  • (etc.)

Your best bet is to show the form first, then get its size.

If you don't want the form to be visible right away you can set its Opacity property to 0 to make it invisible, then change it back to 1.0 once you need the form to be shown to the user.

Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
0

Ok so based on @Visual Vincent's suggestion I created a constructor when creating a new form. This goes in frmAlert.Designer.vb

Partial Class frmAlert
    Private mIsProcessFormCode As Boolean = True

    Public Sub New()
        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.mIsProcessFormCode = True
    End Sub

    Public Sub New(ByVal IsProcessFormCode As Boolean)
        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.mIsProcessFormCode = IsProcessFormCode
    End Sub
End Class

Then on the frmMain I add this code:

Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Debug.Print("routine={0}", System.Reflection.MethodBase.GetCurrentMethod.Name)
    Dim frm As New frmAlert(False) 'create an instance of the form without any of the Form processes
    frm.Opacity = 0 'makes the form totally transparent
    frm.Visible = False
    frm.Show()
    Dim frmWidth As Integer = frm.Width
    Dim frHeight As Integer = frm.Height
    Dim frCsWidth As Integer = frm.ClientSize.Width
    Dim frCsHeight As Integer = frm.ClientSize.Height
    frm.Close()
    frm.Dispose()
    Debug.Print("frmWidth={0} frHeight={1} frCsWidth={2} frCsHeight={3}", frmWidth, frHeight, frCsWidth, frCsHeight)
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Debug.Print("routine={0}", System.Reflection.MethodBase.GetCurrentMethod.Name)

    'set up the alert form normally
    Dim frm As New frmAlert

    'show the alert form
    frm.StartPosition = FormStartPosition.CenterParent
    frm.Show()          'with this option the Alert Forms stay on the screen even if the Main form is minimized.
End Sub

and in the frmAlert I add this code:

Private Sub frmAlert_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
    Try
        'process the form code ONLY if required
        Debug.Print("routine={0} mIsProcessFormCode={1}", System.Reflection.MethodBase.GetCurrentMethod.Name, mIsProcessFormCode)
        If mIsProcessFormCode Then
            'do Closed stuff
        End If
    Catch ex As Exception
        Debug.Print(ex.ToString)
    End Try
End Sub

Private Sub frmAlert_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
    Try
        'process the form code ONLY if required
        Debug.Print("routine={0} mIsProcessFormCode={1}", System.Reflection.MethodBase.GetCurrentMethod.Name, mIsProcessFormCode)
        If mIsProcessFormCode Then
            'do Closing stuff
        End If
    Catch ex As Exception
        Debug.Print(ex.ToString)
    End Try
End Sub

Private Sub frmAlert_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Try
        'process the form code ONLY if required
        Debug.Print("routine={0} mIsProcessFormCode={1}", System.Reflection.MethodBase.GetCurrentMethod.Name, mIsProcessFormCode)
        Debug.Print("me.width={0} me.height={1} cs.width={2} cs.height={3}", Me.Width, Me.Height, Me.ClientSize.Width, Me.ClientSize.Height)
        If mIsProcessFormCode Then
            'process text file
        End If
    Catch ex As Exception
        Debug.Print(ex.ToString)
    End Try
End Sub

and the debug output:

routine=frmMain_Load
routine=frmAlert_Load mIsProcessFormCode=False
me.width=390 me.height=200 cs.width=374 cs.height=162
routine=frmAlert_FormClosing mIsProcessFormCode=False
routine=frmAlert_FormClosed mIsProcessFormCode=False
frmWidth=390 frHeight=200 frCsWidth=374 frCsHeight=162
routine=Button1_Click
routine=frmAlert_Load mIsProcessFormCode=True
me.width=390 me.height=200 cs.width=374 cs.height=162

I believe this is what I want. All frmAlert sizes match what I have in the IDE designer. If you can think of any modifications please let me know. Many thanks.

sinDizzy
  • 1,300
  • 7
  • 28
  • 60