[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 button

The 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!

Site Search

Who's Online

Abbiamo 7 visitatori e nessun utente online

Contacts

Stats

Visite agli articoli
203642
banner mym
banner mym