Saturday, 7 August 2010

Resizable WPF Windows without Minimize and Maximize buttons

When I first started looking at WPF, one of the attractions was the ease with which WPF layout options can be used to create windows with resizable content. I pretty quickly got the idea that resizable windows should be the rule, rather than the exception.

The ability to resize a window is controlled by the Window’s ResizeMode property. When you set ResizeMode to CanResize or CanResizeWithGrip you get a window that can be resized. You also get minimize and maximize buttons in the window’s title bar.

Setting a Window’s WindowStyle property to ToolWindow creates a window with a thin title bar, a close button and no minimized or maximize buttons. However, (in addition to looking a bit odd) this still allows access to the minimize and maximize commands from the system menu:

 toolwindow

WPF’s Window class does not provide any way to create a resizable window that cannot be minimized.

You might think that’s fair enough for a top level window, but it’s not so good for secondary windows, especially modal dialogs.

Modal dialogs should never minimize. When a modal dialog is displayed, the owner window is disabled until the dialog is closed. If the modal dialog is minimized, the owner window remains disabled – to the user it looks like the application just locked up.

The Solution

WPF windows are just like any other as far as the operating system is concerned. The window’s title bar and borders are provided by the operating system. When you set the WindowStyle and ResizeMode properties, WPF uses the SetWindowLong Windows API call to modify the style and extended style flags for the window.

As far as the operating system is concerned, the appearance of the minimize and maximize buttons is controlled by the WS_MINIMIZEBOX and WS_MAXIMIZEBOX flags in the window style. If we want to control the appearance of these buttons for ourselves, we can set and clear these two flags with a combination of calls to the GetWindowLong and SetWindowLong functions.

To implement this behaviour, I created the ExtraWindowStyles class. This class provides attached properties CanMinimize and CanMaximize which can be applied to a window to hide (or show) the minimize and maximize buttons.

Example usage:

<Window x:Class="ExtraWindowStyles.TestWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ews="clr-namespace:ExtraWindowStyles"
    Title="TestWindow"
    ResizeMode="CanResizeWithGrip"
    ews:ExtraWindowStyles.CanMinimize="false" 
    ews:ExtraWindowStyles.CanMaximize="false" 
    Height="300" 
    Width="300"
>

testwindow

Implementing this behaviour using attached properties makes it straightforward to use with styles:

<Style x:Key="myDialogStyle" TargetType="Window">
    <Setter Property="ews:ExtraWindowStyles.CanMinimize" 
            Value="false" 
    />
    <Setter Property="ews:ExtraWindowStyles.CanMaximize" 
            Value="false" 
    />
</Style>

Download the source here: ExtraWindowStyles.cs