Caves Travel Diving Graphics Mizar Texts Cuisine Lemkov Contact Map RSS Polski
Trybiks' Dive Texts Programming Saving / restoring window placements in .NET YAC Software












Rhino Mocks



UI Testing




Saving / restoring window placements in .NET
It seems that saving window positions and sizes (including the application's main window location) should be easy enough. But even now, after so many years of Windows programming, people get it wrong. We need to remember about changing screen resolutions, switching from multiple to single monitors, connecting to a projector, etc. Saving the current window's location just ain't gonna do the trick...

The key to the solution is the pair of Windows API functions: GetWindowPlacement and SetWindowPlacement.

Something like this - saving and restoring a Window's placement (GetSetting/SetSetting are functions that load/save settings through some utility class; LEFT_ID, etc. are just string constants used to define keys under which settings are saved):
  Public Sub LoadWindowPlacement(ByVal key As String, ByVal window As Window)
    Dim windowPlacement As WindowPlacement = GetWindowPlacement(window)
    Dim restoredPosition As New IntegerRect(
      GetSetting(Of Integer)(key + LEFT_ID, windowPlacement.NormalPosition.Left),
      GetSetting(Of Integer)(key + TOP_ID, windowPlacement.NormalPosition.Top),
      GetSetting(Of Integer)(key + WIDTH_ID, windowPlacement.NormalPosition.Width),
      GetSetting(Of Integer)(key + HEIGHT_ID, windowPlacement.NormalPosition.Height))
    windowPlacement.NormalPosition = restoredPosition
    windowPlacement.ShowCommand =
      GetSetting(Of Integer)(key + SHOW_COMMAND_ID, SHOW_MAXIMIZED)
    SetWindowPlacement(window, windowPlacement)
  End Sub
  Public Sub SaveWindowPlacement(ByVal key As String, ByVal window As Window)
    Dim windowPlacement As WindowPlacement = GetWindowPlacement(window)
    SetSetting(Of Integer)(key + HEIGHT_ID, windowPlacement.NormalPosition.Height)
    SetSetting(Of Integer)(key + WIDTH_ID, windowPlacement.NormalPosition.Width)
    SetSetting(Of Integer)(key + TOP_ID, windowPlacement.NormalPosition.Top)
    SetSetting(Of Integer)(key + LEFT_ID, windowPlacement.NormalPosition.Left)
    SetSetting(Of Integer)(key + SHOW_COMMAND_ID, windowPlacement.ShowCommand)
  End Sub
Note that, in the above, we set save/load the NormalPosition (the location of the window in restored state), and handle minimization / maximization separately.

What needs to be done still is we need methods to interface with the Windows API; first, several structures:
  Public Structure IntegerPoint
    Public Property X As Integer
    Public Property Y As Integer
  End Structure
  Public Structure IntegerRect
    Public Property Left As Integer
    Public Property Top As Integer
    Public Property Width As Integer
    Public Property Height As Integer
  End Structure
  Public Structure WindowPlacement
    Public Property Length As Integer
    Public Property Flags As Integer
    Public Property ShowCommand As Integer
    Public Property MinPosition As IntegerPoint
    Public Property MaxPosition As IntegerPoint
    Public Property NormalPosition As IntegerRect
  End Structure
The last structure is the important one - you'll find the description here.

Next, for the functions:
  Public NotInheritable Class WindowUtilities
    Private Sub New()
    End Sub
    Public Const SHOW_NORMAL As Integer = 1
    Public Const SHOW_MINIMIZED As Integer = 2
    Public Const SHOW_MAXIMIZED As Integer = 3
    Public Shared Function GetWindowPlacement(
      ByVal window As Window) As WindowPlacement
      Dim placement As WindowPlacement
        New WindowInteropHelper(window).Handle, placement)
      Return placement
    End Function
    Public Shared Sub SetWindowPlacement(
      ByVal window As Window, ByVal placement As WindowPlacement)
        New WindowInteropHelper(window).Handle, placement)
    End Sub
  End Class
  Friend NotInheritable Class SafeNativeMethods
    Private Sub New()
    End Sub
    Public Shared Function GetWindowPlacement(
      ByVal handle As IntPtr, ByRef placement As WindowPlacement)
      As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    Public Shared Function SetWindowPlacement(
      ByVal handle As IntPtr, ByRef placement As WindowPlacement)
      As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
  End Class
And this is quite enough for the application to behave correctly between sessions, regardless what happens with the screen's resolution, Window's task bar's position, or multi-monitor setup.



No comments yet...


Add a comment (fields with an asterisk are required)
Name / nick *
Mail (will remain hidden) *
Your website
Comment (no tags) *
Enter the text displayed below *





Related pages

AssertWasCalled and Methods Called Multiple Times

AssertWas[Not]Called and Object Properties

Rhino Mocks's AssertWasNotCalled

TFS - The underlying connection was closed: an unexpected error occurred on a receive.

PrivateObject and Out/ByRef parameters

WCF - The underlying connection was closed: an unexpected error occurred on a receive.

PrivateObject, WithEvents, and generics

Accessing private members of base classes


PrivateObject and WithEvents

The creator of this fault did not specify a Reason.

Accessing private and protected members - PrivateObject and PrivateType

Checking Property Change Notifications

Rhino Mocks's AssertWasCalled in VB.NET

First steps with Rhino Mocks (in VB.NET)

Meaningful identifiers

Public fields vs. properties

A Case for FreeAndNIL

Random()'s Determinism

Rounding and precision on the 8087 FPU

Controlling Conditional Defines and Compilation Switches

Registering Extensions

Accessing Protected Members