Disable Close Button In Title Bar of a WPF Window (C#) - 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:
But I want to disable it meaning it should look like this:
I'm scripting in C# and using WPF (Windows Presentation Foundation).

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.

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;
}

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>

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.

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.

Related

Hook up click event after AppendMenu of 3rd party Application

i'm trying to add a new MenuItem using DLL Fucntions imported of the user32.dll using DLLImort to a third party application out of my WPF app.
No I'd like to get the click event of the newly generated MenuItem. Any ideas?
Here's the code so far. I know there are functions of SetWindowHookEx or something else, but I'm stuck.
It's some test code and not bulletproofed..
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
private static extern IntPtr GetMenu(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr GetSubMenu(IntPtr hMenu, int nPos);
[DllImport("user32.dll")]
private static extern int GetMenuItemCount(IntPtr hMenu);
[DllImport("user32.dll")]
private static extern bool InsertMenuItem(IntPtr hMenu, uint uItem, bool
fByPosition, [In] ref MENUITEMINFO lpmii);
[DllImport("user32.dll")]
private static extern bool DrawMenuBar(IntPtr hWnd);
internal const UInt32 MIIM_FTYPE = 0x00000100;
internal const UInt32 MF_STRING = 0x00000000;
internal const UInt32 MF_OWNERDRAW = 0x00000100;
const uint MF_POPUP = 0x00000010;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern bool AppendMenu(IntPtr hMenu, MenuFlags uFlags, uint uIDNewItem, string lpNewItem);
[DllImport("user32.dll")]
static extern IntPtr CreatePopupMenu();
[Flags]
public enum MenuFlags : uint
{
MF_STRING = 0,
MF_BYPOSITION = 0x400,
MF_SEPARATOR = 0x800,
MF_REMOVE = 0x1000,
MF_POPUP = 0x00000010,
}
[StructLayout(LayoutKind.Sequential)]
struct MENUITEMINFO
{
public uint cbSize;
public uint fMask;
public uint fType;
public uint fState;
public uint wID;
public IntPtr hSubMenu;
public IntPtr hbmpChecked;
public IntPtr hbmpUnchecked;
public IntPtr dwItemData;
public string dwTypeData;
public uint cch;
public IntPtr hbmpItem;
// return the size of the structure
public static uint sizeOf
{
get { return (uint)Marshal.SizeOf(typeof(MENUITEMINFO)); }
}
}
public MainWindow()
{
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
createMenuEntry();
}
private void createMenuEntry()
{
Process[] proceses = Process.GetProcessesByName("spotify");
Process process = proceses.Where(e => e.MainWindowTitle == "Spotify").First();
IntPtr handle = process.MainWindowHandle;
IntPtr mainMenu = GetMenu(handle);
int mainMenuItemCount = GetMenuItemCount(mainMenu);
AppendMenu(mainMenu, MenuFlags.MF_STRING, 555, "TestEntry");
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
//HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
//source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// Handle messages...
Debug.WriteLine((int)wParam);
if (((int)wParam == 555))
{
MessageBox.Show("Click");
}
return IntPtr.Zero;
}
}
Thanks for any ideas or suggestions in advance.
Your first step is to put down the C# and understand how the native menu API works. Start here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms647553.aspx
I strongly recommend that you create a new C++ project and write a simple program to add a menu and respond to clicks.
The key information is found in the documentation I linked to, with my emphasis:
When the user chooses a command item, the system sends a command message to the window that owns the menu. If the command item is on the window menu, the system sends the WM_SYSCOMMAND message. Otherwise, it sends the WM_COMMAND message.
You need to intercept that message. I suspect that means to need to use a global WH_CALLWNDPROC hook. That's going to need an unmanaged DLL to implement the hook.

Application stuck in full screen?

To reproduce my problem please do the following:
Create a new Windows Form Application in C#.
In the Properties window of Form1 set FormBorderStyle to None.
Launch program and press Windows+Up.
Now you are stuck in full screen.
In the default FormBorderStyle setting the MaximizeBox property to false will disable the Windows+Up fullscreen shortcut.
If the FormBorderStyle is set to None Microsoft decided it would be a good idea to disable all the Windows+Arrow key shortcuts except for the up arrow and then disable the disabling of the MaximizeBox property.
Is this a glitch? Any simple way to disable this shortcut function the selfsame way it is disabled on all the other FormBorderStyles?
Windows does this by calling SetWindowPos() to change the position and size of the window. A window can be notified about this by listening for the WM_WINDOWPOSCHANGING message and override the settings. Lots of things you can do, like still giving the operation a meaning by adjusting the size and position to your liking. You completely prevent it by turning on the NOSIZE and NOMOVE flags.
Paste this code into your form:
private bool AllowWindowChange;
private struct WINDOWPOS {
public IntPtr hwnd, hwndInsertAfter;
public int x, y, cx, cy;
public int flags;
}
protected override void WndProc(ref Message m) {
// Trap WM_WINDOWPOSCHANGING
if (m.Msg == 0x46 && !AllowWindowChange) {
var wpos = (WINDOWPOS)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));
wpos.flags |= 0x03; // Turn on SWP_NOSIZE | SWP_NOMOVE
System.Runtime.InteropServices.Marshal.StructureToPtr(wpos, m.LParam, false);
}
base.WndProc(ref m);
}
When you want to change the window yourself, simply set the AllowWindowChange field temporarily to true.
Trap the WM_GETMINMAXINFO message which will allow you to specify the maximized size and location of your form. Technically your form will still change state to Maximized, but it will appear the same since we specify the maximized size/position to be the same as the normal state of the form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public struct POINTAPI
{
public Int32 X;
public Int32 Y;
}
public struct MINMAXINFO
{
public POINTAPI ptReserved;
public POINTAPI ptMaxSize;
public POINTAPI ptMaxPosition;
public POINTAPI ptMinTrackSize;
public POINTAPI ptMaxTrackSize;
}
public const Int32 WM_GETMINMAXINFO = 0x24;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_GETMINMAXINFO:
MINMAXINFO mmi = (MINMAXINFO)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(MINMAXINFO));
mmi.ptMaxSize.X = this.Width;
mmi.ptMaxSize.Y = this.Height;
mmi.ptMaxPosition.X = this.Location.X;
mmi.ptMaxPosition.Y = this.Location.Y;
System.Runtime.InteropServices.Marshal.StructureToPtr(mmi, m.LParam, true);
break;
}
base.WndProc(ref m);
}
}
Overriding the ProcessCmdKey (protected method in Form) explicitly allow us to apply custom hook and can be used in your scenario. This essentially allow us to override built-in keystroke handling.
Note: Following example demonstrate the idea of how to handle different keystroke or combination of it. Now, you probably need to fine tune the following code to work inline with your scenario. Eg: Ideally changing the FormBorderStyle or Form Size when user press the LWin+Up arrow.
public partial class Form1 : Form
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.LWin | Keys.Up))//Left windows key + up arrow
{
FormBorderStyle = FormBorderStyle.FixedDialog;
return true;
}
if (keyData == Keys.Escape) //Form will call its close method when we click Escape.
Close();
return base.ProcessCmdKey(ref msg, keyData);
}
}
Updated on How to disable windows Key in your case Lwin or RWin
public partial class Form1 : Form
{
// Structure contain information about low-level keyboard input event
[StructLayout(LayoutKind.Sequential)]
private struct KBDLLHOOKSTRUCT
{
public Keys key;
public int scanCode;
public int flags;
public int time;
public IntPtr extra;
}
//System level functions to be used for hook and unhook keyboard input
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int id, LowLevelKeyboardProc callback, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hook);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hook, int nCode, IntPtr wp, IntPtr lp);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string name);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern short GetAsyncKeyState(Keys key);
//Declaring Global objects
private IntPtr ptrHook;
private LowLevelKeyboardProc objKeyboardProcess;
public Form1()
{
ProcessModule objCurrentModule = Process.GetCurrentProcess().MainModule;
objKeyboardProcess = new LowLevelKeyboardProc(captureKey);
ptrHook = SetWindowsHookEx(13, objKeyboardProcess, GetModuleHandle(objCurrentModule.ModuleName), 0);
InitializeComponent();
}
private IntPtr captureKey(int nCode, IntPtr wp, IntPtr lp)
{
if (nCode >= 0)
{
KBDLLHOOKSTRUCT objKeyInfo = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lp, typeof(KBDLLHOOKSTRUCT));
if (objKeyInfo.key == Keys.RWin || objKeyInfo.key == Keys.LWin) // Disabling Windows keys
{
return (IntPtr)1;
}
}
return CallNextHookEx(ptrHook, nCode, wp, lp);
}
private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
MessageBox.Show(e.KeyChar.ToString());
}
}
Check this solution - it removes Maximize/Minimize/Titlebar/Border by API calls.
public partial class Form1 : Form
{
// import necessary API functions to get and set Windows styles for P/Invoke
[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);
// define constants like they are named in SDK in order to make source more readable
const int GWL_STYLE = -16;
const int GWL_EXSTYLE = -20;
const int WS_MINIMIZEBOX = 0x00020000;
const int WS_MAXIMIZEBOX = 0x00010000;
const int WS_CAPTION = 0x00C00000;
const int WS_THICKFRAME = 0x00040000;
const int WS_EX_DLGMODALFRAME = 0x00000001;
const int WS_EX_CLIENTEDGE = 0x00000200;
const int WS_EX_STATICEDGE = 0x00020000;
// this replaces MinimizeBox=false and MaximizeBox=false
void HideMinimizeAndMaximizeButtons()
{
// read current style
int style = GetWindowLong(Handle, GWL_STYLE);
Debug.WriteLine("0x{0:X}", style);
// update style - remove flags for MinimizeBox and MaximizeBox
style = style & ~WS_MINIMIZEBOX & ~WS_MAXIMIZEBOX;
Debug.WriteLine("0x{0:X}", style);
SetWindowLong(Handle, GWL_STYLE, style);
}
// part of removing the whole border
void HideTitleBar()
{
// read current style
int style = GetWindowLong(Handle, GWL_STYLE);
Debug.WriteLine("0x{0:X}", style);
// update style - remove flag for caption
style = style & ~WS_CAPTION;
Debug.WriteLine("0x{0:X}", style);
SetWindowLong(Handle, GWL_STYLE, style);
}
// hide the border
void HideBorder()
{
// read current style
int style = GetWindowLong(Handle, GWL_STYLE);
Debug.WriteLine("0x{0:X}", style);
// update style - remove flag for border (could use WS_SIZEBOX which is the very same flag (see MSDN)
style = style & ~WS_THICKFRAME;
Debug.WriteLine("0x{0:X}", style);
SetWindowLong(Handle, GWL_STYLE, style);
// read current extended style
style = GetWindowLong(Handle, GWL_EXSTYLE);
Debug.WriteLine("0x{0:X}", style);
// update style by removing some additional border styles -
// may not be necessary, when current border style is not something exotic,
// i.e. as long as it "normal"
style = style & ~WS_EX_DLGMODALFRAME & ~WS_EX_CLIENTEDGE & ~WS_EX_STATICEDGE;
Debug.WriteLine("0x{0:X}", style);
SetWindowLong(Handle, GWL_EXSTYLE, style);
}
public Form1()
{
InitializeComponent();
// hide those unwanted properties - you can try to leave out one or another to see what it does
HideMinimizeAndMaximizeButtons();
HideTitleBar();
HideBorder();
}
}
This works as intended. Maximizing/minimizing by setting WindowState works as well.
One could analyze in sources what the framework does and what it does "wrong" (or not quite correct).
Edit: I added debug output of the style values. Please try this sequence of commands in Form1 constructor:
MaximizeBox = false;
FormBorderStyle = FormBorderStyle.Sizable;
HideMinimizeAndMaximizeButtons();
FormBorderStyle = FormBorderStyle.None;
MaximizeBox = true;
MaximizeBox = false;
HideMinimizeAndMaximizeButtons();
FormBorderStyle = FormBorderStyle.None;
HideMinimizeAndMaximizeButtons();
You'll see, that setting FormBorderStyle.None enables the WS_MAXIMIZEBOX style. This cannot be "corrected" by another MaximizeBox = false. It seems it's necessary to call API functions.

DefWindowProc call not working on Windows 8

I am having a problem calling DefWindowsProc on Windows 8 from a C# winform. I have this form that I need it to be dragabble from anywhwere inside the form.
Here is my code.
[DllImport("user32.dll")]
static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, UIntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool ReleaseCapture(IntPtr hwnd);
const uint WM_SYSCOMMAND = 0x112;
const uint MOUSE_MOVE = 0xF012;
public void Drag()
{
DefWindowProc(this.Handle, WM_SYSCOMMAND, (UIntPtr)MOUSE_MOVE, IntPtr.Zero);
}
private void OnMainPanelMouseDown(object sender, MouseEventArgs e)
{
Control ctrl = sender as Control;
ReleaseCapture(ctrl.Handle);
this.Drag(); // put the form into drag mode.
}
DefWindowProc always return 0 yet I am unable to drag my window. This call works on XP, Vista and 7 but not on 8. I am guessing it has something to do with the decleration of DefWindowProc that is not working well on Windows 8.
Please note that on Windows 8 I am building my application with the .NET 4.0 framework yet on other platforms I am using the 2.0 version to build the software.
I achieved your functionality with a slightly different approach.
I'm running Windows 8 and Visual Studio 2012.
I tested my solution an all versions of the framework, versions 2, 3, 4, 4.5.
Of course your main form will need to trap the mouse_down() method.
private void InitializeComponent()
{
this.SuspendLayout();
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(282, 253);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mouse_down);
this.ResumeLayout(false);
}
First I defined a class called UnsafeNativeMethods:
[SuppressUnmanagedCodeSecurity]
internal static class UnsafeNativeMethods
{
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr hwnd, uint Msg, int wParam, int lParam);
[DllImport("User32.dll", EntryPoint = "ReleaseCapture")]
public static extern bool ReleaseCapture();
}
Inside the form you wish to move with mouse from anywhere:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void mouse_down(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
UnsafeNativeMethods.ReleaseCapture();
UnsafeNativeMethods.SendMessage(Handle, UnsafeNativeMethods.WM_NCLBUTTONDOWN, UnsafeNativeMethods.HT_CAPTION, 0);
}
}
}
I re-wrote the example to see if the behavior was different with DefWindowProc(). It wasn't. The form will still dock to the edge when dragged beyond.
Nevertheless, here is the code:
[SuppressUnmanagedCodeSecurity]
internal static class UnsafeNativeMethods2
{
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, ref int lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32")]
public static extern int ReleaseCapture(IntPtr hwnd);
public const int WM_SYSCOMMAND = 0x112;
public const int MOUSE_MOVE = 0xF012;
}
Here is the code inside the form.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void mouse_down(object sender, MouseEventArgs e)
{
Control ctrl = sender as Control;
UnsafeNativeMethods2.ReleaseCapture(ctrl.Handle);
this.Drag(); // put the form into drag mode.
}
public void Drag()
{
UnsafeNativeMethods2.DefWindowProc(this.Handle, UnsafeNativeMethods2.WM_SYSCOMMAND, (IntPtr) UnsafeNativeMethods2.MOUSE_MOVE, IntPtr.Zero);
}
}
My DefWindowProc() is declared slightly differently from yours.
Bottom line, both approaches achieve the same result - the ability to drag a window from anywhere in a form.

Remove Close(X) Button from WPF Window [duplicate]

I'm writing a modal dialog in WPF. How do I set a WPF window to not have a close button? I'd still like for its WindowState to have a normal title bar.
I found ResizeMode, WindowState, and WindowStyle, but none of those properties allow me to hide the close button but show the title bar, as in modal dialogs.
WPF doesn't have a built-in property to hide the title bar's Close button, but you can do it with a few lines of P/Invoke.
First, add these declarations to your Window class:
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);
Then put this code in the Window's Loaded event:
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
And there you go: no more Close button. You also won't have a window icon on the left side of the title bar, which means no system menu, even when you right-click the title bar - they all go together.
Important note: all this does is hide the button. The user can still close the window! If the user presses Alt+F4, or closes the app via the taskbar, the window will still close.
If you don't want to allow the window to close before the background thread is done, then you could also override OnClosing and set Cancel to true, as Gabe suggested.
I just got to similar problem and Joe White's solution seems to me simple and clean. I reused it and defined it as an attached property of Window
public class WindowBehavior
{
private static readonly Type OwnerType = typeof (WindowBehavior);
#region HideCloseButton (attached property)
public static readonly DependencyProperty HideCloseButtonProperty =
DependencyProperty.RegisterAttached(
"HideCloseButton",
typeof (bool),
OwnerType,
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(HideCloseButtonChangedCallback)));
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetHideCloseButton(Window obj) {
return (bool)obj.GetValue(HideCloseButtonProperty);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static void SetHideCloseButton(Window obj, bool value) {
obj.SetValue(HideCloseButtonProperty, value);
}
private static void HideCloseButtonChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window == null) return;
var hideCloseButton = (bool)e.NewValue;
if (hideCloseButton && !GetIsHiddenCloseButton(window)) {
if (!window.IsLoaded) {
window.Loaded += HideWhenLoadedDelegate;
}
else {
HideCloseButton(window);
}
SetIsHiddenCloseButton(window, true);
}
else if (!hideCloseButton && GetIsHiddenCloseButton(window)) {
if (!window.IsLoaded) {
window.Loaded -= ShowWhenLoadedDelegate;
}
else {
ShowCloseButton(window);
}
SetIsHiddenCloseButton(window, false);
}
}
#region Win32 imports
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
private static readonly RoutedEventHandler HideWhenLoadedDelegate = (sender, args) => {
if (sender is Window == false) return;
var w = (Window)sender;
HideCloseButton(w);
w.Loaded -= HideWhenLoadedDelegate;
};
private static readonly RoutedEventHandler ShowWhenLoadedDelegate = (sender, args) => {
if (sender is Window == false) return;
var w = (Window)sender;
ShowCloseButton(w);
w.Loaded -= ShowWhenLoadedDelegate;
};
private static void HideCloseButton(Window w) {
var hwnd = new WindowInteropHelper(w).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
private static void ShowCloseButton(Window w) {
var hwnd = new WindowInteropHelper(w).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_SYSMENU);
}
#endregion
#region IsHiddenCloseButton (readonly attached property)
private static readonly DependencyPropertyKey IsHiddenCloseButtonKey =
DependencyProperty.RegisterAttachedReadOnly(
"IsHiddenCloseButton",
typeof (bool),
OwnerType,
new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsHiddenCloseButtonProperty =
IsHiddenCloseButtonKey.DependencyProperty;
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetIsHiddenCloseButton(Window obj) {
return (bool)obj.GetValue(IsHiddenCloseButtonProperty);
}
private static void SetIsHiddenCloseButton(Window obj, bool value) {
obj.SetValue(IsHiddenCloseButtonKey, value);
}
#endregion
}
Then in XAML you just set it like this:
<Window
x:Class="WafClient.Presentation.Views.SampleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:WafClient.Presentation.Behaviors"
ResizeMode="NoResize"
u:WindowBehavior.HideCloseButton="True">
...
</Window>
Set WindowStyle property to None which will hide the control box along with the title bar. No need to kernal calls.
This won't get rid of the close button, but it will stop someone closing the window.
Put this in your code behind file:
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
e.Cancel = true;
}
To disable close button you should add the following code to your Window class (the code was taken from here, edited and reformatted a bit):
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
if (hwndSource != null)
{
hwndSource.AddHook(HwndSourceHook);
}
}
private bool allowClosing = false;
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
private static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
private const uint MF_BYCOMMAND = 0x00000000;
private const uint MF_GRAYED = 0x00000001;
private const uint SC_CLOSE = 0xF060;
private const int WM_SHOWWINDOW = 0x00000018;
private const int WM_CLOSE = 0x10;
private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WM_SHOWWINDOW:
{
IntPtr hMenu = GetSystemMenu(hwnd, false);
if (hMenu != IntPtr.Zero)
{
EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
}
}
break;
case WM_CLOSE:
if (!allowClosing)
{
handled = true;
}
break;
}
return IntPtr.Zero;
}
This code also disables close item in System menu and disallows closing the dialog using Alt+F4.
You will probably want to close the window programmatically. Just calling Close() will not work. Do something like this:
allowClosing = true;
Close();
I was trying Viachaslau's answer since I like the idea of not removing the button but disabling it, but for some reason it did not always work: the close button was still enabled but no errors whatsoever.
This on the other hand always worked (error checking omitted):
[DllImport( "user32.dll" )]
private static extern IntPtr GetSystemMenu( IntPtr hWnd, bool bRevert );
[DllImport( "user32.dll" )]
private static extern bool EnableMenuItem( IntPtr hMenu, uint uIDEnableItem, uint uEnable );
private const uint MF_BYCOMMAND = 0x00000000;
private const uint MF_GRAYED = 0x00000001;
private const uint SC_CLOSE = 0xF060;
private const int WM_SHOWWINDOW = 0x00000018;
protected override void OnSourceInitialized( EventArgs e )
{
base.OnSourceInitialized( e );
var hWnd = new WindowInteropHelper( this );
var sysMenu = GetSystemMenu( hWnd.Handle, false );
EnableMenuItem( sysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED );
}
The property to set is => WindowStyle="None"
<Window x:Class="mdaframework.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Start" Height="350" Width="525" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" WindowStyle="None">
I just add my implementation of Joe White's answer using Interactivity Behavior (you need to reference System.Windows.Interactivity).
code:
public class HideCloseButtonOnWindow : 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 WindowInteropHelper(AssociatedObject).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
}
usage:
<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>
Let the user "close" the window but really just hide it.
In the window's OnClosing event, hide the window if already visible:
If Me.Visibility = Windows.Visibility.Visible Then
Me.Visibility = Windows.Visibility.Hidden
e.Cancel = True
End If
Each time the Background thread is to be executed, re-show background UI window:
w.Visibility = Windows.Visibility.Visible
w.Show()
When terminating execution of program, make sure all windows are/can-be closed:
Private Sub CloseAll()
If w IsNot Nothing Then
w.Visibility = Windows.Visibility.Collapsed ' Tell OnClosing to really close
w.Close()
End If
End Sub
The following is about disabling the close and Maximize/Minimize buttons, it does not actually remove the buttons (but it does remove the menu items!). The buttons on the title bar are drawn in a disabled/grayed state. (I'm not quite ready to take over all the functionality myself ^^)
This is slightly different than Virgoss solution in that it removes the menu items (and the trailing separator, if needed) instead of just disabling them. It differs from Joe Whites solution as it does not disable the entire system menu and so, in my case, I can keep around the Minimize button and icon.
The follow code also supports disabling the Maximize/Minimize buttons as, unlike the Close button, removing the entries from the menu does not cause the system to render the buttons "disabled" even though removing the menu entries does disable the functionality of the buttons.
It works for me. YMMV.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Window = System.Windows.Window;
using WindowInteropHelper = System.Windows.Interop.WindowInteropHelper;
using Win32Exception = System.ComponentModel.Win32Exception;
namespace Channelmatter.Guppy
{
public class WindowUtil
{
const int MF_BYCOMMAND = 0x0000;
const int MF_BYPOSITION = 0x0400;
const uint MFT_SEPARATOR = 0x0800;
const uint MIIM_FTYPE = 0x0100;
[DllImport("user32", SetLastError=true)]
private static extern uint RemoveMenu(IntPtr hMenu, uint nPosition, uint wFlags);
[DllImport("user32", SetLastError=true)]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32", SetLastError=true)]
private static extern int GetMenuItemCount(IntPtr hWnd);
[StructLayout(LayoutKind.Sequential)]
public struct MenuItemInfo {
public uint cbSize;
public uint fMask;
public uint fType;
public uint fState;
public uint wID;
public IntPtr hSubMenu;
public IntPtr hbmpChecked;
public IntPtr hbmpUnchecked;
public IntPtr dwItemData; // ULONG_PTR
public IntPtr dwTypeData;
public uint cch;
public IntPtr hbmpItem;
};
[DllImport("user32", SetLastError=true)]
private static extern int GetMenuItemInfo(
IntPtr hMenu, uint uItem,
bool fByPosition, ref MenuItemInfo itemInfo);
public enum MenuCommand : uint
{
SC_CLOSE = 0xF060,
SC_MAXIMIZE = 0xF030,
}
public static void WithSystemMenu (Window win, Action<IntPtr> action) {
var interop = new WindowInteropHelper(win);
IntPtr hMenu = GetSystemMenu(interop.Handle, false);
if (hMenu == IntPtr.Zero) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get system menu");
} else {
action(hMenu);
}
}
// Removes the menu item for the specific command.
// This will disable and gray the Close button and disable the
// functionality behind the Maximize/Minimuze buttons, but it won't
// gray out the Maximize/Minimize buttons. It will also not stop
// the default Alt+F4 behavior.
public static void RemoveMenuItem (Window win, MenuCommand command) {
WithSystemMenu(win, (hMenu) => {
if (RemoveMenu(hMenu, (uint)command, MF_BYCOMMAND) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to remove menu item");
}
});
}
public static bool RemoveTrailingSeparator (Window win) {
bool result = false; // Func<...> not in .NET3 :-/
WithSystemMenu(win, (hMenu) => {
result = RemoveTrailingSeparator(hMenu);
});
return result;
}
// Removes the final trailing separator of a menu if it exists.
// Returns true if a separator is removed.
public static bool RemoveTrailingSeparator (IntPtr hMenu) {
int menuItemCount = GetMenuItemCount(hMenu);
if (menuItemCount < 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get menu item count");
}
if (menuItemCount == 0) {
return false;
} else {
uint index = (uint)(menuItemCount - 1);
MenuItemInfo itemInfo = new MenuItemInfo {
cbSize = (uint)Marshal.SizeOf(typeof(MenuItemInfo)),
fMask = MIIM_FTYPE,
};
if (GetMenuItemInfo(hMenu, index, true, ref itemInfo) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get menu item info");
}
if (itemInfo.fType == MFT_SEPARATOR) {
if (RemoveMenu(hMenu, index, MF_BYPOSITION) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to remove menu item");
}
return true;
} else {
return false;
}
}
}
private const int GWL_STYLE = -16;
[Flags]
public enum WindowStyle : int
{
WS_MINIMIZEBOX = 0x00020000,
WS_MAXIMIZEBOX = 0x00010000,
}
// Don't use this version for dealing with pointers
[DllImport("user32", SetLastError=true)]
private static extern int SetWindowLong (IntPtr hWnd, int nIndex, int dwNewLong);
// Don't use this version for dealing with pointers
[DllImport("user32", SetLastError=true)]
private static extern int GetWindowLong (IntPtr hWnd, int nIndex);
public static int AlterWindowStyle (Window win,
WindowStyle orFlags, WindowStyle andNotFlags)
{
var interop = new WindowInteropHelper(win);
int prevStyle = GetWindowLong(interop.Handle, GWL_STYLE);
if (prevStyle == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get window style");
}
int newStyle = (prevStyle | (int)orFlags) & ~((int)andNotFlags);
if (SetWindowLong(interop.Handle, GWL_STYLE, newStyle) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to set window style");
}
return prevStyle;
}
public static int DisableMaximizeButton (Window win) {
return AlterWindowStyle(win, 0, WindowStyle.WS_MAXIMIZEBOX);
}
}
}
Usage: This must be done AFTER the source is initialized. A good place is to use the SourceInitialized event of the Window:
Window win = ...; /* the Window :-) */
WindowUtil.DisableMaximizeButton(win);
WindowUtil.RemoveMenuItem(win, WindowUtil.MenuCommand.SC_MAXIMIZE);
WindowUtil.RemoveMenuItem(win, WindowUtil.MenuCommand.SC_CLOSE);
while (WindowUtil.RemoveTrailingSeparator(win))
{
//do it here
}
To disable the Alt+F4 functionality the easy method is just to wire up the Canceling event and use set a flag for when you really do want to close the window.
Here is how I achieved the similar goal using custom styles without DllImports and P/Invoke calls. This removes the existing title bar using WindowStyle="none" and shows a 'TextBlock' with similar background color to indicate as title bar.
XAML Code
<Window x:Class="AddBook"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="http://wpftoolkit.my-libraries.com/v5"
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize"
Style="{DynamicResource WindowStyleX}"
ShowInTaskbar="False"
ShowActivated="True"
SizeToContent="Height"
Title="Add New Book"
Width="450">
..............
</Window>
XAML
<Style x:Key="WindowStyleX" TargetType="{x:Type Window}">
<Setter Property="WindowStyle" Value="None" />
<Setter Property="AllowsTransparency" Value="False" />
<Setter Property="ResizeMode" Value="NoResize" />
<Setter Property="Background" Value="White" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border BorderBrush="{DynamicResource BlackColor}" BorderThickness="1">
<Grid Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border
Grid.Row="0"
Grid.ColumnSpan="2"
Background="{DynamicResource BlackColor}">
<Grid>
<TextBlock
Grid.Column="1"
Margin="10,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="16"
Foreground="{DynamicResource WhiteTextForeground}"
Text="{TemplateBinding Title}" />
</Grid>
</Border>
<ContentPresenter Grid.Row="1" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
So, pretty much here is your problem. The close button on the upper right of a window frame is not part of the WPF window, but it belongs to the part of the window frame that is controled by your OS. This means you will have to use Win32 interop to do it.
alternativly, you can use the noframe and either provide your own "frame" or have no frame at all.
This doesn't hide the button but will prevent the user from moving forward by shutting down the window.
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
if (e.Cancel == false)
{
Application.Current.Shutdown();
}
}
XAML Code
<Button Command="Open" Content="_Open">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
should work
Edit- for your instant this Thread shows how that can be done but I don't think Window has a property to get what you want without losing the normal title bar.
Edit 2
This Thread shows a way for it to be done, but you must apply your own style to the system menu and it shows a way how you can do that.
As stated in other answers, you can use WindowStyle="None" to remove the Title Bar altogether.
And, as stated in the comments to those other answers, this prevents the window from being draggable so it is hard to move it from its initial position.
However, you can overcome this by adding a single line of code to the Constructor in the Window's Code Behind file:
MouseDown += delegate { DragMove(); };
Or, if you prefer Lambda Syntax:
MouseDown += (sender, args) => DragMove();
This makes the entire Window draggable. Any interactive controls present in the Window, such as Buttons, will still work as normal and won't act as drag-handles for the Window.
Try adding a Closing event to the window. Add this code to the event handler.
e.Cancel = true;
This will prevent the window from closing. This has the same effect as hiding the close button.
Use this, modified from https://stephenhaunts.com/2014/09/25/remove-the-close-button-from-a-wpf-window :
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
namespace Whatever
{
public partial class MainMenu : Window
{
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x00080000;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLongPtr(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
public MainMenu()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window_Loaded);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
}
}
I very much like this answer which uses attached properties to mediate the behavior. However, I found that answer's implementation overly complicated, and it also doesn't address the secondary goal of preventing the window from being closed even with Alt+F4. So I offer this alternative:
enum CloseButtonVisibility
{
Visible,
Hidden,
CloseDisabled,
}
static class WindowEx
{
private static readonly CancelEventHandler _cancelCloseHandler = (sender, e) => e.Cancel = true;
public static readonly DependencyProperty CloseButtonVisibilityProperty =
DependencyProperty.RegisterAttached(
"CloseButtonVisibility",
typeof(CloseButtonVisibility),
typeof(WindowEx),
new FrameworkPropertyMetadata(CloseButtonVisibility.Visible, new PropertyChangedCallback(_OnCloseButtonChanged)));
[AttachedPropertyBrowsableForType(typeof(Window))]
public static CloseButtonVisibility GetCloseButtonVisibility(Window obj)
{
return (CloseButtonVisibility)obj.GetValue(CloseButtonVisibilityProperty);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static void SetCloseButtonVisibility(Window obj, CloseButtonVisibility value)
{
obj.SetValue(CloseButtonVisibilityProperty, value);
}
private static void _OnCloseButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is Window window))
{
return;
}
if (e.OldValue is CloseButtonVisibility oldVisibility)
{
if (oldVisibility == CloseButtonVisibility.CloseDisabled)
{
window.Closing -= _cancelCloseHandler;
}
}
if (e.NewValue is CloseButtonVisibility newVisibility)
{
if (newVisibility == CloseButtonVisibility.CloseDisabled)
{
window.Closing += _cancelCloseHandler;
}
if (!window.IsLoaded)
{
// NOTE: if the property is set multiple times before the window is loaded,
// the window will wind up with multiple event handlers. But they will all
// set the same value, so this is fine from a functionality point of view.
//
// The handler is never unsubscribed, so there is some nominal overhead there.
// But it would be incredibly unusual for this to be set more than once
// before the window is loaded, and even a handful of delegate instances
// being around that are no longer needed is not really a big deal.
window.Loaded += _ApplyCloseButtonVisibility;
}
else
{
_SetVisibility(window, newVisibility);
}
}
}
#region Win32 imports
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
private static void _ApplyCloseButtonVisibility(object sender, RoutedEventArgs e)
{
Window window = (Window)sender;
CloseButtonVisibility visibility = GetCloseButtonVisibility(window);
_SetVisibility(window, visibility);
}
private static void _SetVisibility(Window window, CloseButtonVisibility visibility)
{
var hwnd = new WindowInteropHelper(window).Handle;
if (visibility == CloseButtonVisibility.Visible)
{
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_SYSMENU);
}
else
{
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
}
}
This provides three states to choose from:
Visible
Hidden, but user can still close using Alt+F4
Hidden, and closing is disabled completely
Note that by default, a window that is never closed will prevent a WPF program's process from terminating. So if you choose to use the CloseButtonVisibility.CloseDisabled value, you will need to either customize the Application.Run() behavior, or re-enable closing of the window before exiting. E.g. in your main window, you might have something like this:
protected override void OnClosed(EventArgs e)
{
WindowEx.SetCloseButtonVisibility(this.toolWindow.Value, CloseButtonVisibility.Hidden);
this.toolWindow.Value.Close();
base.OnClosed(e);
}
where toolWindow is the Window reference for the window with the close button disabled.
The above assumes that the window is normally just hidden and shown as needed during the normal UI activity. Of course, you can also choose to close the window explicitly at any time, but the same technique — setting the option to not disable closing, and then explicitly closing the window — would still apply.
You may run into the need to toggle the window state depending on what page or user control is currently being displayed. (Yes, this is an unusual scenario, but it can happen. In our case we have a user control in our application that is displayed on an external touch screen for customers to interact with. We don't want our customers to have touch access to close the screen. But the rest of the application uses standard windows frames.) To do this from the code behind of the page or user control. Note: You must run it from the Loaded event not the constructor because the control hasn't been populated in the constructor and it will throw an exception.
// To toggle it off
Window.GetWindow(this).WindowStyle = WindowStyle.None;
// To turn it back on toggle it off
Window.GetWindow(this).WindowStyle = WindowStyle.SingleBorderWindow;
goto window properties set
window style = none;
u wont get close buttons...
After much searching for the answer to this, I worked out this simple solution that I will share here in hopes it helps others.
I set WindowStyle=0x10000000.
This sets the WS_VISIBLE (0x10000000) and WS_OVERLAPPED (0x0) values for Window Style. "Overlapped" is the necessary value to show the title bar and window border. By removing the WS_MINIMIZEBOX (0x20000), WS_MAXIMIZEBOX (0x10000), and WS_SYSMENU (0x80000) values from my style value, all the buttons from the title bar were removed, including the Close button.
If the need is only to prohibit the user from closing the window, this is a simple solution.
XAML code:
IsCloseButtonEnabled="False"
It's block the button.
Use WindowStyle="SingleBorderWindow" , this will hide max and min button from WPF Window.

How to hide close button in WPF window?

I'm writing a modal dialog in WPF. How do I set a WPF window to not have a close button? I'd still like for its WindowState to have a normal title bar.
I found ResizeMode, WindowState, and WindowStyle, but none of those properties allow me to hide the close button but show the title bar, as in modal dialogs.
WPF doesn't have a built-in property to hide the title bar's Close button, but you can do it with a few lines of P/Invoke.
First, add these declarations to your Window class:
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);
Then put this code in the Window's Loaded event:
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
And there you go: no more Close button. You also won't have a window icon on the left side of the title bar, which means no system menu, even when you right-click the title bar - they all go together.
Important note: all this does is hide the button. The user can still close the window! If the user presses Alt+F4, or closes the app via the taskbar, the window will still close.
If you don't want to allow the window to close before the background thread is done, then you could also override OnClosing and set Cancel to true, as Gabe suggested.
I just got to similar problem and Joe White's solution seems to me simple and clean. I reused it and defined it as an attached property of Window
public class WindowBehavior
{
private static readonly Type OwnerType = typeof (WindowBehavior);
#region HideCloseButton (attached property)
public static readonly DependencyProperty HideCloseButtonProperty =
DependencyProperty.RegisterAttached(
"HideCloseButton",
typeof (bool),
OwnerType,
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(HideCloseButtonChangedCallback)));
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetHideCloseButton(Window obj) {
return (bool)obj.GetValue(HideCloseButtonProperty);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static void SetHideCloseButton(Window obj, bool value) {
obj.SetValue(HideCloseButtonProperty, value);
}
private static void HideCloseButtonChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window == null) return;
var hideCloseButton = (bool)e.NewValue;
if (hideCloseButton && !GetIsHiddenCloseButton(window)) {
if (!window.IsLoaded) {
window.Loaded += HideWhenLoadedDelegate;
}
else {
HideCloseButton(window);
}
SetIsHiddenCloseButton(window, true);
}
else if (!hideCloseButton && GetIsHiddenCloseButton(window)) {
if (!window.IsLoaded) {
window.Loaded -= ShowWhenLoadedDelegate;
}
else {
ShowCloseButton(window);
}
SetIsHiddenCloseButton(window, false);
}
}
#region Win32 imports
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
private static readonly RoutedEventHandler HideWhenLoadedDelegate = (sender, args) => {
if (sender is Window == false) return;
var w = (Window)sender;
HideCloseButton(w);
w.Loaded -= HideWhenLoadedDelegate;
};
private static readonly RoutedEventHandler ShowWhenLoadedDelegate = (sender, args) => {
if (sender is Window == false) return;
var w = (Window)sender;
ShowCloseButton(w);
w.Loaded -= ShowWhenLoadedDelegate;
};
private static void HideCloseButton(Window w) {
var hwnd = new WindowInteropHelper(w).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
private static void ShowCloseButton(Window w) {
var hwnd = new WindowInteropHelper(w).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_SYSMENU);
}
#endregion
#region IsHiddenCloseButton (readonly attached property)
private static readonly DependencyPropertyKey IsHiddenCloseButtonKey =
DependencyProperty.RegisterAttachedReadOnly(
"IsHiddenCloseButton",
typeof (bool),
OwnerType,
new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsHiddenCloseButtonProperty =
IsHiddenCloseButtonKey.DependencyProperty;
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetIsHiddenCloseButton(Window obj) {
return (bool)obj.GetValue(IsHiddenCloseButtonProperty);
}
private static void SetIsHiddenCloseButton(Window obj, bool value) {
obj.SetValue(IsHiddenCloseButtonKey, value);
}
#endregion
}
Then in XAML you just set it like this:
<Window
x:Class="WafClient.Presentation.Views.SampleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:WafClient.Presentation.Behaviors"
ResizeMode="NoResize"
u:WindowBehavior.HideCloseButton="True">
...
</Window>
Set WindowStyle property to None which will hide the control box along with the title bar. No need to kernal calls.
This won't get rid of the close button, but it will stop someone closing the window.
Put this in your code behind file:
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
e.Cancel = true;
}
To disable close button you should add the following code to your Window class (the code was taken from here, edited and reformatted a bit):
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
if (hwndSource != null)
{
hwndSource.AddHook(HwndSourceHook);
}
}
private bool allowClosing = false;
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
private static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
private const uint MF_BYCOMMAND = 0x00000000;
private const uint MF_GRAYED = 0x00000001;
private const uint SC_CLOSE = 0xF060;
private const int WM_SHOWWINDOW = 0x00000018;
private const int WM_CLOSE = 0x10;
private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WM_SHOWWINDOW:
{
IntPtr hMenu = GetSystemMenu(hwnd, false);
if (hMenu != IntPtr.Zero)
{
EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
}
}
break;
case WM_CLOSE:
if (!allowClosing)
{
handled = true;
}
break;
}
return IntPtr.Zero;
}
This code also disables close item in System menu and disallows closing the dialog using Alt+F4.
You will probably want to close the window programmatically. Just calling Close() will not work. Do something like this:
allowClosing = true;
Close();
I was trying Viachaslau's answer since I like the idea of not removing the button but disabling it, but for some reason it did not always work: the close button was still enabled but no errors whatsoever.
This on the other hand always worked (error checking omitted):
[DllImport( "user32.dll" )]
private static extern IntPtr GetSystemMenu( IntPtr hWnd, bool bRevert );
[DllImport( "user32.dll" )]
private static extern bool EnableMenuItem( IntPtr hMenu, uint uIDEnableItem, uint uEnable );
private const uint MF_BYCOMMAND = 0x00000000;
private const uint MF_GRAYED = 0x00000001;
private const uint SC_CLOSE = 0xF060;
private const int WM_SHOWWINDOW = 0x00000018;
protected override void OnSourceInitialized( EventArgs e )
{
base.OnSourceInitialized( e );
var hWnd = new WindowInteropHelper( this );
var sysMenu = GetSystemMenu( hWnd.Handle, false );
EnableMenuItem( sysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED );
}
The property to set is => WindowStyle="None"
<Window x:Class="mdaframework.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Start" Height="350" Width="525" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" WindowStyle="None">
I just add my implementation of Joe White's answer using Interactivity Behavior (you need to reference System.Windows.Interactivity).
code:
public class HideCloseButtonOnWindow : 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 WindowInteropHelper(AssociatedObject).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
}
usage:
<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>
Let the user "close" the window but really just hide it.
In the window's OnClosing event, hide the window if already visible:
If Me.Visibility = Windows.Visibility.Visible Then
Me.Visibility = Windows.Visibility.Hidden
e.Cancel = True
End If
Each time the Background thread is to be executed, re-show background UI window:
w.Visibility = Windows.Visibility.Visible
w.Show()
When terminating execution of program, make sure all windows are/can-be closed:
Private Sub CloseAll()
If w IsNot Nothing Then
w.Visibility = Windows.Visibility.Collapsed ' Tell OnClosing to really close
w.Close()
End If
End Sub
The following is about disabling the close and Maximize/Minimize buttons, it does not actually remove the buttons (but it does remove the menu items!). The buttons on the title bar are drawn in a disabled/grayed state. (I'm not quite ready to take over all the functionality myself ^^)
This is slightly different than Virgoss solution in that it removes the menu items (and the trailing separator, if needed) instead of just disabling them. It differs from Joe Whites solution as it does not disable the entire system menu and so, in my case, I can keep around the Minimize button and icon.
The follow code also supports disabling the Maximize/Minimize buttons as, unlike the Close button, removing the entries from the menu does not cause the system to render the buttons "disabled" even though removing the menu entries does disable the functionality of the buttons.
It works for me. YMMV.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Window = System.Windows.Window;
using WindowInteropHelper = System.Windows.Interop.WindowInteropHelper;
using Win32Exception = System.ComponentModel.Win32Exception;
namespace Channelmatter.Guppy
{
public class WindowUtil
{
const int MF_BYCOMMAND = 0x0000;
const int MF_BYPOSITION = 0x0400;
const uint MFT_SEPARATOR = 0x0800;
const uint MIIM_FTYPE = 0x0100;
[DllImport("user32", SetLastError=true)]
private static extern uint RemoveMenu(IntPtr hMenu, uint nPosition, uint wFlags);
[DllImport("user32", SetLastError=true)]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32", SetLastError=true)]
private static extern int GetMenuItemCount(IntPtr hWnd);
[StructLayout(LayoutKind.Sequential)]
public struct MenuItemInfo {
public uint cbSize;
public uint fMask;
public uint fType;
public uint fState;
public uint wID;
public IntPtr hSubMenu;
public IntPtr hbmpChecked;
public IntPtr hbmpUnchecked;
public IntPtr dwItemData; // ULONG_PTR
public IntPtr dwTypeData;
public uint cch;
public IntPtr hbmpItem;
};
[DllImport("user32", SetLastError=true)]
private static extern int GetMenuItemInfo(
IntPtr hMenu, uint uItem,
bool fByPosition, ref MenuItemInfo itemInfo);
public enum MenuCommand : uint
{
SC_CLOSE = 0xF060,
SC_MAXIMIZE = 0xF030,
}
public static void WithSystemMenu (Window win, Action<IntPtr> action) {
var interop = new WindowInteropHelper(win);
IntPtr hMenu = GetSystemMenu(interop.Handle, false);
if (hMenu == IntPtr.Zero) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get system menu");
} else {
action(hMenu);
}
}
// Removes the menu item for the specific command.
// This will disable and gray the Close button and disable the
// functionality behind the Maximize/Minimuze buttons, but it won't
// gray out the Maximize/Minimize buttons. It will also not stop
// the default Alt+F4 behavior.
public static void RemoveMenuItem (Window win, MenuCommand command) {
WithSystemMenu(win, (hMenu) => {
if (RemoveMenu(hMenu, (uint)command, MF_BYCOMMAND) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to remove menu item");
}
});
}
public static bool RemoveTrailingSeparator (Window win) {
bool result = false; // Func<...> not in .NET3 :-/
WithSystemMenu(win, (hMenu) => {
result = RemoveTrailingSeparator(hMenu);
});
return result;
}
// Removes the final trailing separator of a menu if it exists.
// Returns true if a separator is removed.
public static bool RemoveTrailingSeparator (IntPtr hMenu) {
int menuItemCount = GetMenuItemCount(hMenu);
if (menuItemCount < 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get menu item count");
}
if (menuItemCount == 0) {
return false;
} else {
uint index = (uint)(menuItemCount - 1);
MenuItemInfo itemInfo = new MenuItemInfo {
cbSize = (uint)Marshal.SizeOf(typeof(MenuItemInfo)),
fMask = MIIM_FTYPE,
};
if (GetMenuItemInfo(hMenu, index, true, ref itemInfo) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get menu item info");
}
if (itemInfo.fType == MFT_SEPARATOR) {
if (RemoveMenu(hMenu, index, MF_BYPOSITION) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to remove menu item");
}
return true;
} else {
return false;
}
}
}
private const int GWL_STYLE = -16;
[Flags]
public enum WindowStyle : int
{
WS_MINIMIZEBOX = 0x00020000,
WS_MAXIMIZEBOX = 0x00010000,
}
// Don't use this version for dealing with pointers
[DllImport("user32", SetLastError=true)]
private static extern int SetWindowLong (IntPtr hWnd, int nIndex, int dwNewLong);
// Don't use this version for dealing with pointers
[DllImport("user32", SetLastError=true)]
private static extern int GetWindowLong (IntPtr hWnd, int nIndex);
public static int AlterWindowStyle (Window win,
WindowStyle orFlags, WindowStyle andNotFlags)
{
var interop = new WindowInteropHelper(win);
int prevStyle = GetWindowLong(interop.Handle, GWL_STYLE);
if (prevStyle == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get window style");
}
int newStyle = (prevStyle | (int)orFlags) & ~((int)andNotFlags);
if (SetWindowLong(interop.Handle, GWL_STYLE, newStyle) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to set window style");
}
return prevStyle;
}
public static int DisableMaximizeButton (Window win) {
return AlterWindowStyle(win, 0, WindowStyle.WS_MAXIMIZEBOX);
}
}
}
Usage: This must be done AFTER the source is initialized. A good place is to use the SourceInitialized event of the Window:
Window win = ...; /* the Window :-) */
WindowUtil.DisableMaximizeButton(win);
WindowUtil.RemoveMenuItem(win, WindowUtil.MenuCommand.SC_MAXIMIZE);
WindowUtil.RemoveMenuItem(win, WindowUtil.MenuCommand.SC_CLOSE);
while (WindowUtil.RemoveTrailingSeparator(win))
{
//do it here
}
To disable the Alt+F4 functionality the easy method is just to wire up the Canceling event and use set a flag for when you really do want to close the window.
Here is how I achieved the similar goal using custom styles without DllImports and P/Invoke calls. This removes the existing title bar using WindowStyle="none" and shows a 'TextBlock' with similar background color to indicate as title bar.
XAML Code
<Window x:Class="AddBook"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="http://wpftoolkit.my-libraries.com/v5"
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize"
Style="{DynamicResource WindowStyleX}"
ShowInTaskbar="False"
ShowActivated="True"
SizeToContent="Height"
Title="Add New Book"
Width="450">
..............
</Window>
XAML
<Style x:Key="WindowStyleX" TargetType="{x:Type Window}">
<Setter Property="WindowStyle" Value="None" />
<Setter Property="AllowsTransparency" Value="False" />
<Setter Property="ResizeMode" Value="NoResize" />
<Setter Property="Background" Value="White" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border BorderBrush="{DynamicResource BlackColor}" BorderThickness="1">
<Grid Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border
Grid.Row="0"
Grid.ColumnSpan="2"
Background="{DynamicResource BlackColor}">
<Grid>
<TextBlock
Grid.Column="1"
Margin="10,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="16"
Foreground="{DynamicResource WhiteTextForeground}"
Text="{TemplateBinding Title}" />
</Grid>
</Border>
<ContentPresenter Grid.Row="1" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
So, pretty much here is your problem. The close button on the upper right of a window frame is not part of the WPF window, but it belongs to the part of the window frame that is controled by your OS. This means you will have to use Win32 interop to do it.
alternativly, you can use the noframe and either provide your own "frame" or have no frame at all.
This doesn't hide the button but will prevent the user from moving forward by shutting down the window.
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
if (e.Cancel == false)
{
Application.Current.Shutdown();
}
}
XAML Code
<Button Command="Open" Content="_Open">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
should work
Edit- for your instant this Thread shows how that can be done but I don't think Window has a property to get what you want without losing the normal title bar.
Edit 2
This Thread shows a way for it to be done, but you must apply your own style to the system menu and it shows a way how you can do that.
As stated in other answers, you can use WindowStyle="None" to remove the Title Bar altogether.
And, as stated in the comments to those other answers, this prevents the window from being draggable so it is hard to move it from its initial position.
However, you can overcome this by adding a single line of code to the Constructor in the Window's Code Behind file:
MouseDown += delegate { DragMove(); };
Or, if you prefer Lambda Syntax:
MouseDown += (sender, args) => DragMove();
This makes the entire Window draggable. Any interactive controls present in the Window, such as Buttons, will still work as normal and won't act as drag-handles for the Window.
Try adding a Closing event to the window. Add this code to the event handler.
e.Cancel = true;
This will prevent the window from closing. This has the same effect as hiding the close button.
Use this, modified from https://stephenhaunts.com/2014/09/25/remove-the-close-button-from-a-wpf-window :
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
namespace Whatever
{
public partial class MainMenu : Window
{
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x00080000;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLongPtr(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
public MainMenu()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window_Loaded);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
}
}
I very much like this answer which uses attached properties to mediate the behavior. However, I found that answer's implementation overly complicated, and it also doesn't address the secondary goal of preventing the window from being closed even with Alt+F4. So I offer this alternative:
enum CloseButtonVisibility
{
Visible,
Hidden,
CloseDisabled,
}
static class WindowEx
{
private static readonly CancelEventHandler _cancelCloseHandler = (sender, e) => e.Cancel = true;
public static readonly DependencyProperty CloseButtonVisibilityProperty =
DependencyProperty.RegisterAttached(
"CloseButtonVisibility",
typeof(CloseButtonVisibility),
typeof(WindowEx),
new FrameworkPropertyMetadata(CloseButtonVisibility.Visible, new PropertyChangedCallback(_OnCloseButtonChanged)));
[AttachedPropertyBrowsableForType(typeof(Window))]
public static CloseButtonVisibility GetCloseButtonVisibility(Window obj)
{
return (CloseButtonVisibility)obj.GetValue(CloseButtonVisibilityProperty);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static void SetCloseButtonVisibility(Window obj, CloseButtonVisibility value)
{
obj.SetValue(CloseButtonVisibilityProperty, value);
}
private static void _OnCloseButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is Window window))
{
return;
}
if (e.OldValue is CloseButtonVisibility oldVisibility)
{
if (oldVisibility == CloseButtonVisibility.CloseDisabled)
{
window.Closing -= _cancelCloseHandler;
}
}
if (e.NewValue is CloseButtonVisibility newVisibility)
{
if (newVisibility == CloseButtonVisibility.CloseDisabled)
{
window.Closing += _cancelCloseHandler;
}
if (!window.IsLoaded)
{
// NOTE: if the property is set multiple times before the window is loaded,
// the window will wind up with multiple event handlers. But they will all
// set the same value, so this is fine from a functionality point of view.
//
// The handler is never unsubscribed, so there is some nominal overhead there.
// But it would be incredibly unusual for this to be set more than once
// before the window is loaded, and even a handful of delegate instances
// being around that are no longer needed is not really a big deal.
window.Loaded += _ApplyCloseButtonVisibility;
}
else
{
_SetVisibility(window, newVisibility);
}
}
}
#region Win32 imports
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
private static void _ApplyCloseButtonVisibility(object sender, RoutedEventArgs e)
{
Window window = (Window)sender;
CloseButtonVisibility visibility = GetCloseButtonVisibility(window);
_SetVisibility(window, visibility);
}
private static void _SetVisibility(Window window, CloseButtonVisibility visibility)
{
var hwnd = new WindowInteropHelper(window).Handle;
if (visibility == CloseButtonVisibility.Visible)
{
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_SYSMENU);
}
else
{
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
}
}
This provides three states to choose from:
Visible
Hidden, but user can still close using Alt+F4
Hidden, and closing is disabled completely
Note that by default, a window that is never closed will prevent a WPF program's process from terminating. So if you choose to use the CloseButtonVisibility.CloseDisabled value, you will need to either customize the Application.Run() behavior, or re-enable closing of the window before exiting. E.g. in your main window, you might have something like this:
protected override void OnClosed(EventArgs e)
{
WindowEx.SetCloseButtonVisibility(this.toolWindow.Value, CloseButtonVisibility.Hidden);
this.toolWindow.Value.Close();
base.OnClosed(e);
}
where toolWindow is the Window reference for the window with the close button disabled.
The above assumes that the window is normally just hidden and shown as needed during the normal UI activity. Of course, you can also choose to close the window explicitly at any time, but the same technique — setting the option to not disable closing, and then explicitly closing the window — would still apply.
You may run into the need to toggle the window state depending on what page or user control is currently being displayed. (Yes, this is an unusual scenario, but it can happen. In our case we have a user control in our application that is displayed on an external touch screen for customers to interact with. We don't want our customers to have touch access to close the screen. But the rest of the application uses standard windows frames.) To do this from the code behind of the page or user control. Note: You must run it from the Loaded event not the constructor because the control hasn't been populated in the constructor and it will throw an exception.
// To toggle it off
Window.GetWindow(this).WindowStyle = WindowStyle.None;
// To turn it back on toggle it off
Window.GetWindow(this).WindowStyle = WindowStyle.SingleBorderWindow;
goto window properties set
window style = none;
u wont get close buttons...
After much searching for the answer to this, I worked out this simple solution that I will share here in hopes it helps others.
I set WindowStyle=0x10000000.
This sets the WS_VISIBLE (0x10000000) and WS_OVERLAPPED (0x0) values for Window Style. "Overlapped" is the necessary value to show the title bar and window border. By removing the WS_MINIMIZEBOX (0x20000), WS_MAXIMIZEBOX (0x10000), and WS_SYSMENU (0x80000) values from my style value, all the buttons from the title bar were removed, including the Close button.
If the need is only to prohibit the user from closing the window, this is a simple solution.
XAML code:
IsCloseButtonEnabled="False"
It's block the button.
Use WindowStyle="SingleBorderWindow" , this will hide max and min button from WPF Window.

Categories