[WPF] Hide the “Window Buttons” (minimize, restore and close) and the icon of a window

No “Red-X”? Then I can’t close the window! How to achieve this reaction in WPF applications.

Even if it is not always the user friendliest way, there are situation in which you do no want the user to close a window using the “red X” in the upper right corner of a window’s non client area (sometimes only called “Close Button” ;-) ).

In most cases this behavior is expected from Dialogs which usually do not have “Minimize” and “Maximize” buttons. So it’s a common practice to hide the whole so called “System Menu”, “Control Box” or “System Bar”.

Windows Forms

It’s quite easy to do so in Windows Forms: Simply set the ControlBox property to false:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        //We want to have a dialog
        this.FormBorderStyle = FormBorderStyle.FixedDialog;
        //Hide the System Menu / Control Box / System Bar
        this.ControlBox = false;
    }
}

WPF

However, a Window does not provide a property similar to WinForms’ ControlBox.

Window with close button First, we set the ResizeMode to NoResize to have an effect like FixedDialog in WinForms:

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Width="200"
        Height="75"
        ResizeMode="NoResize" />

Window without close buttonThe upper image on the right shows the result. To make it look like on the lower image, we have to use the Windows API:

internal static class Win32
{
    public const int GWL_STYLE = (-16);
    public const UInt32 SWP_FRAMECHANGED = 0x0020;
    public const UInt32 SWP_NOSIZE = 0x0001;
    public const UInt32 SWP_NOMOVE = 0x0002;
    public const int WS_SYSMENU = 0x00080000;

    [DllImport("user32.dll", SetLastError = true)]
    public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
    public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
    {
        if (IntPtr.Size == 8)
            return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);

        return new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32()));
    }
    [DllImport("user32.dll", EntryPoint = "SetWindowLong")]
    private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, int dwNewLong);
    [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
    private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern int SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
}

internal sealed class CloseButtonHider
{
    private readonly WindowInteropHelper interopHelper;
    private int windowLong;
    private bool dirty;

    public Window Window
    {
        get;
        private set;
    }
    public bool IsButtonHidden
    {
        get;
        private set;
    }

    private bool IsSourceInitialized
    {
        get
        {
            return this.WindowHandle != IntPtr.Zero;
        }
    }
    private IntPtr WindowHandle
    {
        get
        {
            return this.interopHelper.Handle;
        }
    }

    public CloseButtonHider(Window window)
    {
        Contract.Requires<ArgumentNullException>(window != null);
        Contract.Ensures(Object.Equals(this.Window, window));

        this.Window = window;
        this.interopHelper = new WindowInteropHelper(window);

        if (!this.IsSourceInitialized)
            this.Window.SourceInitialized += this.Window_SourceInitialized;
    }

    public void Hide()
    {
        Contract.Ensures(this.IsButtonHidden);

        if (this.IsButtonHidden)
            return;

        this.IsButtonHidden = true;

        this.ApplyChanges();
    }
    public void Show()
    {
        Contract.Ensures(!this.IsButtonHidden);

        if (!this.IsButtonHidden)
            return;

        this.IsButtonHidden = false;

        this.ApplyChanges();
    }

    private void ApplyChanges()
    {
        if (!this.IsSourceInitialized)
        {
            this.dirty = true;
            return;
        }

        this.GetWindowLong();
        this.SetSysMenu(!this.IsButtonHidden);
        this.ApplyWindowLong();

        this.dirty = false;
    }

    private void GetWindowLong()
    {
        Contract.Requires(this.IsSourceInitialized);

        this.windowLong = Win32.GetWindowLong(this.WindowHandle, Win32.GWL_STYLE);
    }
    private void ApplyWindowLong()
    {
        Contract.Assert(this.IsSourceInitialized);

        this.SetWindowLong();
        this.RefreshWindow();
    }
    private void RefreshWindow()
    {
        Contract.Requires(this.IsSourceInitialized);

        int result = Win32.SetWindowPos(this.WindowHandle, IntPtr.Zero, 0, 0, 100, 100, Win32.SWP_FRAMECHANGED | Win32.SWP_NOMOVE | Win32.SWP_NOSIZE);
        if (result == 0)
            throw new Win32Exception(Marshal.GetLastWin32Error());
    }
    private void SetWindowLong()
    {
        Contract.Requires(this.IsSourceInitialized);

        IntPtr result = Win32.SetWindowLongPtr(this.WindowHandle, Win32.GWL_STYLE, new IntPtr(this.windowLong));
        if (result == IntPtr.Zero)
            throw new Win32Exception(Marshal.GetLastWin32Error());
    }
    private void SetSysMenu(bool value)
    {
        this.windowLong = this.windowLong.Set(Win32.WS_SYSMENU, value);
    }

    private void Window_SourceInitialized(object sender, EventArgs e)
    {
        this.Window.SourceInitialized -= this.Window_SourceInitialized;
        Contract.Assume(this.IsSourceInitialized);

        if (this.dirty)
            this.ApplyChanges();
    }

    [ContractInvariantMethod]
    private void CloseButtonHiderInvariant()
    {
        Contract.Invariant(this.interopHelper != null);
        Contract.Invariant(this.Window != null);
    }
}

internal static class BitExtensions
{
    public static int Set(this int value, int flag)
    {
        return BitExtensions.Set(value, flag, true);
    }
    public static int Reset(this int value, int flag, bool state)
    {
        return BitExtensions.Set(value, flag, false);
    }
    public static int Set(this int value, int flag, bool state)
    {
        if (state)
            return value | flag;

        return value & (~flag);
    }
}

CloseButtonHelper

CloseButtonHelper encapsulates the logic for hiding and restoring the close button. It provides the public methods Show and Hide.

Note that we have to pay attention whether the window has already been created when calling Hide or Show.

Using the new “System.Windows.Interactivity” assembly (brought by Expression Blend 3), we now have the following behavior:

public sealed class HideCloseButtonBehaiviour
    : Behavior<Window>
{
    private CloseButtonHider hider;

    protected override void OnAttached()
    {
        this.hider = new CloseButtonHider(this.AssociatedObject);

        this.hider.Hide();

        base.OnAttached();
    }
    protected override void OnDetaching()
    {
        this.hider.Show();

        base.OnDetaching();
    }
}

Usage

A simple sample Window:

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:WpfApplication1="clr-namespace:WpfApplication1"
        Title="FooWindow"
        Width="200"
        Height="75"
        ResizeMode="NoResize">
    <Interactivity:Interaction.Behaviors>
        <WpfApplication1:HideCloseButtonBehaiviour />
    </Interactivity:Interaction.Behaviors>
</Window>

Do you know a simpler way? Please let me know!

DotNetKicks Image
About these ads

11 Responses to “[WPF] Hide the “Window Buttons” (minimize, restore and close) and the icon of a window”

  1. DotNetShoutout Says:

    [WPF] Hide the “Window Buttons” (minimize, restore and close) and the icon of a window…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  2. tobi Says:

    i have seen that you used contracts. what are your experiences with the static contract checker? i found it rather useless at this point because it misses so many cases.

  3. winsharp93 Says:

    >> what are your experiences with the static contract checker? i found it rather useless at this point because it misses so many cases.
    I will cover the static checker in a further part of my “Code Contracts” series.
    I totally agree with you: In its current state of development, the static checker is quite useless.
    However, there will (hopefully) be lots of improvements and optimizations. Maybe it will be useful in the future.
    And I see no disadvantages in using Code Contracts – so I use them.

  4. Janosh Says:

    Thanks for this article. It saved me a lot of time :) Nice job.

  5. trex Says:

    if anyone interested, i came across this article that give the ability to disable Maximize and/or Minimize buttons without changing ResizeMode…

    Hope it helps.

    http://code-in-action.blogspot.com/2009/04/hiding-disabling-maximize-andor.html

  6. Hemel Says:

    Try WindowStyle=”None” in your Window XAML. This will remove buttons and title.

  7. Hemel Says:

    Try also WindowStyle=”ToolWindow” ;)

  8. fighter Says:

    One way to fix this problem is explained in below link:

    http://www.a2zmenu.com/Blogs/Silverlight/WPF-Hide-minimize-button-of-a-window.aspx

  9. no___body Says:

    I do know a simpler way:

    //…
    this.SourceInitialized += (x, y) =>
    {
    this.DisableButtons();
    };
    //…

    internal static class WindowExtensions
    {
    [DllImport("user32.dll")]
    internal extern static int SetWindowLong(IntPtr hwnd, int index, int value);
    [DllImport("user32.dll")]
    internal extern static int GetWindowLong(IntPtr hwnd, int index);

    internal static void DisableButtons(this Window window)
    {
    const int GWL_STYLE = -16;
    const int WS_SYSMENU = 0×80000;
    IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle;
    int value = GetWindowLong(hwnd, GWL_STYLE);
    SetWindowLong(hwnd, GWL_STYLE, value & ~WS_SYSMENU);
    }
    }

  10. Th0ma5 Says:

    Similar to “no___body”s post, there is an article telling this way within the Loaded event of the window – see http://stackoverflow.com/questions/743906/how-to-hide-close-button-in-wpf-window/958980#958980


Comments are closed.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: