Come hide il pulsante Chiudi nella finestra WPF?

Sto scrivendo una finestra di dialogo modale in WPF. Come posso impostare una finestra WPF per non avere un pulsante di chiusura? Mi piacerebbe ancora che WindowState avesse una normale barra del titolo.

Ho trovato ResizeMode, WindowState e WindowStyle, ma nessuna di queste proprietà mi consente di hide il pulsante di chiusura ma mostrare la barra del titolo, come nelle windows di dialogo modali.

WPF non ha una proprietà built-in per hide il pulsante Chiudi della barra del titolo, ma puoi farlo con poche righe di P / Invoke.

Innanzitutto, aggiungi queste dichiarazioni alla tua class Window:

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

Quindi inserisci questo codice nell’evento Caricamento della finestra:

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

Ed ecco fatto: basta chiudere il pulsante. Inoltre, non avrai un’icona a forma di finestra sul lato sinistro della barra del titolo, il che significa che nessun menu di sistema, anche quando fai clic con il pulsante destro del mouse sulla barra del titolo, andranno tutti insieme.

Nota che Alt + F4 chiuderà comunque la finestra. Se non si desidera consentire alla finestra di chiudere prima che il thread in background sia terminato, si potrebbe anche eseguire l’override di OnClosing e impostare Cancel su true, come suggerito da Gabe.

Ho appena avuto un problema simile e la soluzione di Joe White mi sembra semplice e pulita. L’ho riutilizzato e l’ho definito come una proprietà associata di 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 } 

Quindi in XAML l’hai appena impostato in questo modo:

  ...  

Imposta la proprietà WindowStyle su None che nasconde la casella di controllo insieme alla barra del titolo. Non c’è bisogno di chiamate kernal.

Questo non si sbarazzerà del pulsante di chiusura, ma fermerà qualcuno che chiude la finestra.

Metti questo nel tuo codice dietro il file:

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

Per disabilitare il pulsante di chiusura è necessario aggiungere il seguente codice alla class Window (il codice è stato preso da qui , modificato e riformattato un po ‘):

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

Questo codice disabilita anche l’elemento vicino nel menu di sistema e non consente di chiudere la finestra di dialogo usando Alt + F4.

Probabilmente vorrai chiudere la finestra a livello di programmazione. Basta chiamare Close() non funzionerà. Fai qualcosa del genere:

 allowClosing = true; Close(); 

Stavo cercando la risposta di Viachaslau poiché mi piace l’idea di non rimuovere il pulsante ma di disabilitarlo, ma per qualche motivo non ha funzionato sempre: il pulsante di chiusura era ancora abilitato ma nessun errore di sorta.

Questo d’altra parte ha sempre funzionato (errore di controllo omesso):

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

Ho appena aggiunto la mia implementazione della risposta di Joe White utilizzando Interactivity Behaviour (è necessario fare riferimento a System.Windows.Interactivity).

codice:

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

utilizzo:

      

La proprietà da impostare è => WindowStyle="None"

  

Quindi, praticamente qui è il tuo problema. Il pulsante di chiusura in alto a destra di una cornice della finestra non fa parte della finestra WPF, ma appartiene alla parte della cornice della finestra che è controllata dal sistema operativo. Ciò significa che dovrai utilizzare l’interoperabilità di Win32 per farlo.

alternativamente, puoi usare noframe e fornire il tuo “frame” o non avere alcun frame.

Lascia che l’utente “chiuda” la finestra ma in realtà lo nasconda.

Nell’evento OnClosing della finestra, nascondi la finestra se già visibile:

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

Ogni volta che si esegue il thread in background, ri-mostra la finestra dell’interfaccia utente in background:

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

Quando si interrompe l’esecuzione del programma, assicurarsi che tutte le windows siano / possono essere chiuse:

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

Quanto segue riguarda la disabilitazione dei pulsanti di chiusura e Ingrandisci / Riduci a icona, in realtà non rimuove i pulsanti (ma rimuove le voci di menu!). I pulsanti sulla barra del titolo sono disegnati in uno stato disabilitato / grigio. (Non sono ancora pronto a rilevare tutte le funzionalità da solo ^^)

Questo è leggermente diverso rispetto alla soluzione Virgoss in quanto rimuove le voci di menu (e il separatore finale, se necessario) invece di disabilitarle. Si differenzia dalla soluzione di Joe Whites in quanto non disabilita l’intero menu di sistema e quindi, nel mio caso, posso mantenere il pulsante e icona Minimize.

Il codice seguente supporta anche la distriggerszione dei pulsanti Ingrandisci / Riduci a icona poiché, a differenza del pulsante Chiudi, la rimozione delle voci dal menu non comporta il rendering dei pulsanti “disabilitati”, anche se la rimozione delle voci del menu disabilita la funzionalità dei pulsanti.

Per me funziona. 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); } } } 

Utilizzo: Questa operazione deve essere eseguita DOPO che la sorgente è inizializzata. Un buon posto è usare l'evento SourceInitialized della finestra:

 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 } 

Per disabilitare la funzionalità Alt + F4, il metodo semplice è solo quello di colbind l'evento Canceling e usare set a flag per quando si vuole davvero chiudere la finestra.

Codice XAML

  

dovrebbe funzionare

Modifica – per il tuo istante questa discussione mostra come è ansible farlo, ma non credo che Window abbia una proprietà per ottenere quello che vuoi senza perdere la normale barra del titolo.

Modifica 2 Questo thread mostra un modo per farlo, ma devi applicare il tuo stile al menu di sistema e mostra come puoi farlo.

set di proprietà della finestra di goto

 window style = none; 

non ti avvicinerai ai pulsanti …

Prova ad aggiungere un evento di chiusura alla finestra. Aggiungi questo codice al gestore eventi.

 e.Cancel = true; 

Ciò impedirà la chiusura della finestra. Questo ha lo stesso effetto di hide il pulsante di chiusura.

Usa questo, modificato da 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); } } } 

Dopo aver cercato molto la risposta a questo, ho elaborato questa semplice soluzione che condividerò qui nella speranza che aiuti gli altri.

Ho impostato WindowStyle=0x10000000 .

Imposta i WS_VISIBLE (0x10000000) e WS_OVERLAPPED (0x0) per Stile finestra. “Sovrapposto” è il valore necessario per mostrare la barra del titolo e il bordo della finestra. Rimuovendo i WS_MINIMIZEBOX (0x20000) , WS_MAXIMIZEBOX (0x10000) e WS_SYSMENU (0x80000) dal mio valore di stile, tutti i pulsanti della barra del titolo sono stati rimossi, incluso il pulsante Chiudi.

Come indicato in altre risposte, puoi usare WindowStyle="None" per rimuovere del tutto la barra del titolo.

E, come dichiarato nei commenti a quelle altre risposte, questo impedisce alla finestra di essere trascinata in modo che sia difficile spostarla dalla sua posizione iniziale.

Tuttavia, è ansible superare questo problema aggiungendo una singola riga di codice al Costruttore nel file Code Behind di Window:

 MouseDown += delegate { DragMove(); }; 

Oppure, se preferisci la syntax Lambda:

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

Questo rende l’intera finestra trascinabile. Qualsiasi controllo interattivo presente nella finestra, come i pulsanti, funzionerà normalmente e non agirà da maniglia di trascinamento per la finestra.

Usa WindowStyle="SingleBorderWindow" , questo nasconderà il tasto max e min dalla finestra WPF.

Se la necessità è solo di vietare all’utente di chiudere la finestra, questa è una soluzione semplice.

Codice XAML: IsCloseButtonEnabled="False"