¿Cómo ocultar el botón de cerrar en la ventana de WPF?

Estoy escribiendo un diálogo modal en WPF. ¿Cómo configuro una ventana de WPF para que no tenga un botón de cerrar? Todavía me gustaría que WindowState tenga una barra de título normal.

Encontré ResizeMode, WindowState y WindowStyle, pero ninguna de esas propiedades me permite ocultar el botón Cerrar, pero muestra la barra de título, como en los cuadros de diálogo modales.

WPF no tiene una propiedad incorporada para ocultar el botón Cerrar de la barra de título, pero puede hacerlo con unas pocas líneas de P / Invoke.

Primero, agregue estas declaraciones a su clase de ventana:

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

A continuación, coloque este código en el evento de Windows cargado:

 var hwnd = new WindowInteropHelper(this).Handle; SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU); 

Y ahí tienes: no más botón Cerrar. Tampoco tendrá un ícono de ventana en el lado izquierdo de la barra de título, lo que significa que no hay menú del sistema, incluso cuando hace clic con el botón derecho en la barra de título; todos van juntos.

Tenga en cuenta que Alt + F4 aún cerrará la ventana. Si no desea permitir que la ventana se cierre antes de que finalice el hilo de fondo, entonces también puede anular el cierre y establecer Cancelar en verdadero, como sugirió Gabe.

Acabo de llegar a un problema similar y la solución de Joe White me parece simple y limpia. Lo reutilicé y lo definí como una propiedad adjunta de 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 } 

Entonces, en XAML, simplemente lo configuró así:

  ...  

Establezca la propiedad WindowStyle en Ninguno que ocultará el cuadro de control junto con la barra de título. No es necesario hacer llamadas kernal.

Esto no eliminará el botón de cerrar, pero evitará que alguien cierre la ventana.

Pon esto en tu código detrás del archivo:

 protected override void OnClosing(CancelEventArgs e) { base.OnClosing(e); e.Cancel = true; } 

Para deshabilitar el botón Cerrar, debe agregar el siguiente código a su clase Window (el código fue tomado desde aquí , editado y reformateado un poco):

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

Este código también desactiva el elemento cercano en el menú Sistema y no permite cerrar el cuadro de diálogo usando Alt + F4.

Es probable que desee cerrar la ventana mediante progtwigción. Simplemente llamando a Close() no funcionará. Haz algo como esto:

 allowClosing = true; Close(); 

Estaba probando la respuesta de Viachaslau porque me gusta la idea de no quitar el botón pero deshabilitarlo, pero por alguna razón no siempre funcionó: el botón de cerrar todavía estaba habilitado pero no había errores en absoluto.

Esto, por otro lado, siempre funcionó (se omite la comprobación de errores):

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

Acabo de agregar mi implementación de la respuesta de Joe White usando el Comportamiento de Interactividad (necesita hacer referencia a System.Windows.Interactivity).

código:

 public class HideCloseButtonOnWindow : Behavior { #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); } } 

uso:

      

La propiedad para establecer es => WindowStyle="None"

  

Entonces, aquí está tu problema. El botón de cerrar en la esquina superior derecha de un marco de ventana no es parte de la ventana de WPF, pero pertenece a la parte del marco de la ventana que controla su sistema operativo. Esto significa que tendrá que usar la interoperabilidad de Win32 para hacerlo.

alternativamente, puede usar el noframe y proporcionar su propio “marco” o no tener ningún marco.

Lo siguiente se trata de deshabilitar los botones Cerrar y Maximizar / Minimizar, en realidad no elimina los botones (¡pero elimina los elementos del menú!). Los botones en la barra de título están dibujados en un estado deshabilitado / atenuado. (No estoy listo para asumir toda la funcionalidad yo mismo ^^)

Esto es ligeramente diferente de la solución de Virgoss en que elimina los elementos del menú (y el separador posterior, si es necesario) en lugar de simplemente deshabilitarlos. Difiere de la solución de Joe Whites ya que no desactiva todo el menú del sistema y, en mi caso, puedo mantener el ícono y el botón Minimizar.

El código siguiente también es compatible con la desactivación de los botones Maximizar / Minimizar ya que, a diferencia del botón Cerrar, eliminar las entradas del menú no hace que el sistema “deshabilite” los botones aunque eliminar las entradas del menú desactiva la funcionalidad de los botones.

Esto funciona para mi. 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 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); } } } 

Uso: Esto debe hacerse DESPUÉS de que se inicialice la fuente. Un buen lugar es usar el evento SourceInitialized de la ventana:

 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 } 

Para desactivar la funcionalidad Alt + F4, el método fácil es simplemente conectar el evento de Cancelación y usar establecer un indicador para cuando realmente desea cerrar la ventana.

Código XAML

   

Deberia trabajar

Editar – para este instante, este hilo muestra cómo se puede hacer eso, pero no creo que Windows tenga una propiedad para obtener lo que quiere sin perder la barra de título normal.

Editar 2 Este hilo muestra una forma de hacerlo, pero debe aplicar su propio estilo al menú del sistema y muestra una forma de cómo hacerlo.

Permita que el usuario “cierre” la ventana, pero realmente solo la oculte.

En el evento OnClosing de la ventana, oculta la ventana si ya está visible:

  If Me.Visibility = Windows.Visibility.Visible Then Me.Visibility = Windows.Visibility.Hidden e.Cancel = True End If 

Cada vez que se ejecuta el hilo de fondo, vuelve a mostrar la ventana de UI de fondo:

  w.Visibility = Windows.Visibility.Visible w.Show() 

Al finalizar la ejecución del progtwig, asegúrese de que todas las ventanas estén / puedan cerrarse:

 Private Sub CloseAll() If w IsNot Nothing Then w.Visibility = Windows.Visibility.Collapsed ' Tell OnClosing to really close w.Close() End If End Sub 

Propiedades de la ventana goto

 window style = none; 

no obtendrás botones cercanos …

Intente agregar un evento de Cierre a la ventana. Agregue este código al controlador de eventos.

 e.Cancel = true; 

Esto evitará que la ventana se cierre. Esto tiene el mismo efecto que ocultar el botón de cerrar.

Use esto, modificado desde 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); } } } 

Después de mucho buscar la respuesta a esto, resolví esta solución simple que compartiré aquí con la esperanza de que ayude a otros.

Configuro WindowStyle=0x10000000 .

Esto establece los WS_VISIBLE (0x10000000) y WS_OVERLAPPED (0x0) para Window Style. “Superpuesto” es el valor necesario para mostrar la barra de título y el borde de la ventana. Al eliminar los WS_MINIMIZEBOX (0x20000) , WS_MAXIMIZEBOX (0x10000) y WS_SYSMENU (0x80000) de mi valor de estilo, se eliminaron todos los botones de la barra de título, incluido el botón Cerrar.

Como se indica en otras respuestas, puede usar WindowStyle="None" para eliminar la barra de título por completo.

Y, como se afirma en los comentarios a esas otras respuestas, esto impide que la ventana sea arrastrable por lo que es difícil moverla desde su posición inicial.

Sin embargo, puede solucionar esto agregando una sola línea de código al Constructor en el archivo Code’s Back de la ventana:

 MouseDown += delegate { DragMove(); }; 

O, si prefiere la syntax Lambda:

 MouseDown += (sender, args) => DragMove(); 

Esto hace que toda la ventana sea arrastrable. Todos los controles interactivos presentes en la Ventana, como Botones, seguirán funcionando normalmente y no actuarán como controladores de arrastre para la Ventana.

Use WindowStyle="SingleBorderWindow" , esto ocultará los botones max y min de la ventana de WPF.

Si la necesidad es solo prohibir al usuario cerrar la ventana, esta es una solución simple.

Código XAML: IsCloseButtonEnabled="False"