Bubbled86
Bubbled86

Reputation: 491

Disable Close Button In Title Bar of a WPF Window (C#)

I'd like to know how to disable (not remove/hide) the Close button in a WPF window. I know how to hide it which makes the window's title bar look like this:

enter image description here

But I want to disable it meaning it should look like this:

enter image description here

I'm scripting in C# and using WPF (Windows Presentation Foundation).

Upvotes: 19

Views: 45138

Answers (5)

shelleybutterfly
shelleybutterfly

Reputation: 3247

If you would like a more generic version of Yoav's accepted answer that doesn't require adding Win API calls to your Window class, here's a extension class and method:

namespace WinApi
{
    using System.Runtime.InteropServices;
    using System.Windows.Interop;

    public static class WinApi
    {
        [DllImport("user32.dll")]
        public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

        [DllImport("user32.dll")]
        public static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);

        const uint MF_BYCOMMAND = 0x00000000;
        const uint MF_GRAYED = 0x00000001;

        const uint SC_CLOSE = 0xF060;

        public static void DisableCloseButton(this System.Windows.Window window)
        {
            // Disable close button
            IntPtr hwnd = new WindowInteropHelper(window).EnsureHandle();
            IntPtr hMenu = GetSystemMenu(hwnd, false);

            if (hMenu != IntPtr.Zero)
                EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
        }
    }
}

Then call it from your Window like so:

this.DisableCloseButton();

// or

WinApi.DisableCloseButton(this);

Since the extension uses EnsureHandle() you don't need to hook OnSourceInitialized() in your Window.

Be aware that EnsureHandle() raises OnSourceInitialized(), so don't call this until after you have done anything you want to happen prior to that call.

You can call new WindowInteropHelper(this).Handle() in your Window code if you need to check whether the handle has already been created.

Upvotes: 0

Yoav
Yoav

Reputation: 3386

Try this:

public partial class MainWindow : Window
{

    [DllImport("user32.dll")]
    static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

    [DllImport("user32.dll")]
    static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);


    const uint MF_BYCOMMAND = 0x00000000;
    const uint MF_GRAYED = 0x00000001;

    const uint SC_CLOSE = 0xF060;

    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);

        // Disable close button
        IntPtr hwnd = new WindowInteropHelper(this).Handle;
        IntPtr hMenu = GetSystemMenu(hwnd, false);
        if (hMenu != IntPtr.Zero)
        {
            EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
        }
    }
}

Taken from here.

Make sure you set the ResizeMode to NoResize.

Upvotes: 25

rmojab63
rmojab63

Reputation: 3631

This post which an answer using Behavior, GetWindowLong and SetWindowLong:

public class HideCloseButtonOnWindow : System.Windows.Interactivity.Behavior<Window>
{
    #region bunch of native methods

    private const int GWL_STYLE = -16;
    private const int WS_SYSMENU = 0x80000;

    [DllImport("user32.dll", SetLastError = true)]
    private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll")]
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Loaded += OnLoaded;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Loaded -= OnLoaded;
        base.OnDetaching();
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        var hwnd = new System.Windows.Interop.WindowInteropHelper(AssociatedObject).Handle;
        SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
    }
}

How to use it:

<Window x:Class="WpfApplication2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:w="clr-namespace:WpfApplication2">

<i:Interaction.Behaviors>
    <w:HideCloseButtonOnWindow />
</i:Interaction.Behaviors>

</Window>

Upvotes: 2

Erti-Chris Eelmaa
Erti-Chris Eelmaa

Reputation: 26268

You can probably do it with win32 hackery.

I have done it this way: Get CustomChromeWindow(which will eventually look exactly like the one in picture), and just bind Command() property to viewmodel, and then set CanExecuteCommand=false, which will make the button disabled(How does one "disable" a button in WPF using the MVVM pattern?).

There might me this way too: How to disable close button on a window in another process with C++?

Basically, call that code with pInvoke. You can obtain WPF window handle easily.

Upvotes: 0

Rohit
Rohit

Reputation: 10226

You have to override and in OnCLosing event set e.cancel=true

public MyWindow()
{
    InitializeComponent();
    this.Closing += new System.ComponentModel.CancelEventHandler(MyWindow_Closing);
}

void MyWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    e.Cancel = true;
}

Upvotes: 4

Related Questions