Friday, December 4, 2009

Correctly Displaying Windows Forms

If you have an application that launches a number of different forms and the forms can be launched in different ways by different parent forms or different parts of the application, displaying the form correctly relative to the parent form can be tricky.  The way I originally did this was to pass the parent handle of the parent form to the child form.  That’s a little cumbersome because you have to pass the parent handle as an argument every time you launch a child form.  I’ve found a much better way to do that.
I’ve written a small amount of code that calls a Windows API to get the windows handle of the active window and I use that to launch all forms.  It works great.  Here’s the code:

Imports System.Drawing
Imports System.Windows.Forms

Public Class WindowManager

Private Sub New()

End Sub

Public Shared ReadOnly Property ActiveWindowPointer() As IntPtr
Get
Try
Return WindowsAPI.GetActiveWindow()
Catch
Return 0
End Try
End Get
End Property

Public Shared ReadOnly Property ForegroundWindowPointer() As IntPtr
Get
Try
Return WindowsAPI.GetForegroundWindow()
Catch
Return 0
End Try
End Get
End Property

Public Shared ReadOnly Property ForegroundWindow() As IWin32Window
Get
Return New WindowWrapper(ForegroundWindowPointer)
End Get
End Property

Public Shared ReadOnly Property ActiveWindowBounds() As Rectangle
Get
Dim r As WindowsAPI.RECT = New WindowsAPI.RECT(0, 0, 0, 0)
Dim found As Boolean = WindowsAPI.GetWindowRect(ActiveWindowPointer, r)
If found Then
Return r.Rectangle
Else
Return Screen.PrimaryScreen.Bounds
End If
End Get
End Property

Public Shared Sub CenterFormToActiveWindow(ByVal form As Form)
Try

Dim point As Point = New Point()
Dim formSize As Size = form.Size
Dim workingArea As Rectangle = ActiveWindowBounds
point.X = (workingArea.X + (workingArea.Width / 2)) - (formSize.Width / 2)
point.Y = (workingArea.Y + (workingArea.Height / 2)) - (formSize.Height / 2)
form.Location = point

Catch

End Try

End Sub

End Class

Public Class WindowWrapper
Implements IWin32Window

Dim _hwnd As IntPtr

Public Sub New(ByVal handle As IntPtr)
_hwnd = handle
End Sub

Public ReadOnly Property Handle() As IntPtr Implements IWin32Window.Handle
Get
Return _hwnd
End Get

End Property

End Class


Declare Function GetActiveWindow Lib "user32" Alias "GetActiveWindow" () As IntPtr
Declare Function GetForegroundWindow Lib "user32" () As IntPtr
Public Declare Function GetWindowRect Lib "user32" (ByVal HWnd As IntPtr, _
ByRef lpRect As RECT) As Boolean


The way to use this is simple:
  • If you’re launching a dialog form, just use the following line of code:


    frm.ShowDialog(WindowManager.ForegroundWindow)

    and it will launch the form relative to the currently active Window.

  • That approach works for all dialog forms - if you’re launching a non-dialog form, you have to set the parent form inside the form constructor before you launch the form.
This approach has saved a lot of time and resulted in much cleaner code.

Chuck

0 comments:

Post a Comment