Comment masquer le bouton Fermer dans la fenêtre WPF?
J'écris une boîte de dialogue modale dans WPF. Comment puis-je définir une fenêtre WPF pour ne pas avoir de bouton de fermeture? Je voudrais toujours que son WindowState ait une barre de titre normale.
J'ai trouvé ResizeMode, WindowState et WindowStyle, mais aucune de ces propriétés ne me permet de masquer le bouton Fermer mais d'afficher la barre de titre, comme dans les boîtes de dialogue modales.
19 réponses
WPF n'a pas de propriété intégrée pour masquer le bouton de fermeture de la barre de titre, mais vous pouvez le faire avec quelques lignes de P/Invoke.
Tout d'abord, ajoutez ces déclarations à votre classe 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);
Ensuite, mettez ce code dans l'événement chargé de la fenêtre:
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
Et voilà: plus de bouton de fermeture. Vous n'aurez pas non plus d'icône de fenêtre sur le côté gauche de la barre de titre, ce qui signifie qu'aucun menu Système, même lorsque vous cliquez avec le bouton droit sur la barre de titre-ils vont tous ensemble.
Note cet Alt + F4 fermera toujours la fenêtre. Si vous ne voulez pas permettre à la fenêtre de se fermer avant que le thread d'arrière-plan ne soit terminé, vous pouvez également remplacer OnClosing et définir Cancel sur true, comme L'a suggéré Gabe.
Je viens d'avoir un problème similaire et la solution de Joe White me semble simple et propre. Je l'ai réutilisé et l'ai défini comme une propriété attachée 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
}
Ensuite, dans XAML, vous venez de le Définir comme ceci:
<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>
Définissez la propriété WindowStyle
sur None qui masquera la zone de contrôle avec la barre de titre. Pas besoin d'appels kernal.
Cela ne se débarrassera pas du bouton de fermeture, mais cela empêchera quelqu'un de fermer la fenêtre.
Mettez ceci dans votre code derrière le fichier:
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
e.Cancel = true;
}
Pour désactiver le bouton Fermer, vous devez ajouter le code suivant à votre classe de fenêtre (le code a été pris de ici , édité et reformaté un peu):
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;
}
CE code désactive également l'élément fermer dans le menu Système et interdit la fermeture de la boîte de dialogue en utilisant Alt + F4.
Vous voudrez probablement fermer la fenêtre par programme. Juste appeler Close()
ne fonctionnera pas. Faites quelque chose comme ceci:
allowClosing = true;
Close();
J'essayais la réponse de Viachaslau car j'aime l'idée de ne pas supprimer le bouton mais de le désactiver, mais pour une raison quelconque cela ne fonctionnait pas toujours: le bouton de fermeture était toujours activé mais aucune erreur.
D'autre part, cela a toujours fonctionné (erreur de vérification omise):
[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 );
}
J'ajoute juste mon implémentation de la réponse de Joe White en utilisant le comportement D'interactivité (vous devez référencer le système.Windows.Interactivité).
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);
}
}
Utilisation:
<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>
La propriété à définir est => 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">
Laissez l'Utilisateur "fermer" la fenêtre mais vraiment juste la Cacher.
Dans L'événement OnClosing de la fenêtre, masquez la fenêtre si elle est déjà visible:
If Me.Visibility = Windows.Visibility.Visible Then
Me.Visibility = Windows.Visibility.Hidden
e.Cancel = True
End If
Chaque fois que le thread D'arrière-plan doit être exécuté, afficher à nouveau la fenêtre de l'interface utilisateur d'arrière-plan:
w.Visibility = Windows.Visibility.Visible
w.Show()
Lorsque vous terminez l'exécution du programme, assurez-vous que toutes les fenêtres sont / peuvent être fermées:
Private Sub CloseAll()
If w IsNot Nothing Then
w.Visibility = Windows.Visibility.Collapsed ' Tell OnClosing to really close
w.Close()
End If
End Sub
Donc, à peu près voici votre problème. Le bouton Fermer en haut à droite d'un cadre de fenêtre ne fait pas partie de la fenêtre WPF, mais il appartient à la partie du cadre de fenêtre qui est contrôlée par votre système d'exploitation. Cela signifie que vous devrez utiliser Win32 interop pour le faire.
Alternativement, vous pouvez utiliser le noframe et fournir votre propre "cadre" ou n'avoir aucun cadre.
Ce qui suit concerne la désactivation des boutons fermer et maximiser/minimiser, il nePas supprime réellement les boutons (mais il supprime les éléments de menu!). Les boutons de la barre de titre sont dessinés dans un état désactivé / grisé. (Je ne suis pas tout à fait prêt à prendre en charge toutes les fonctionnalités moi-même^^)
Ceci est légèrement différent de la solution Virgoss en ce sens qu'elle supprime les éléments de menu (et le séparateur de fin, si nécessaire) au lieu de simplement les désactiver. Il diffère de Joe Whites solution car il ne désactive pas l'ensemble du menu système et donc, dans mon cas, je peux garder autour du bouton et de L'icône minimiser.
Le code suivant prend également en charge la désactivation des boutons maximiser/minimiser car, contrairement au bouton Fermer, la suppression des entrées du menu ne provoque pas le système à rendre les boutons "désactivés" même si la suppression des entrées de menu fait désactiver la fonctionnalité des boutons.
Ça marche pour moi. 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);
}
}
}
Utilisation: cela doit être fait Après l'initialisation de la source. Un bon endroit est d'utiliser L'événement SourceInitialized de la fenêtre:
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
}
Pour désactiver la fonctionnalité Alt+F4, la méthode simple consiste simplement à câbler l'événement D'Annulation et à utiliser Définir un drapeau pour quand vous voulez vraiment fermer la fenêtre.
Code XAML
<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>
Devrait fonctionner
Edit - pour votre instant ce Thread montre comment cela peut être fait mais je ne pense pas que Window ait une propriété pour obtenir ce que vous voulez sans perdre la barre de titre normale.
Modifier 2 Ce Thread montre un moyen de le faire, mais vous devez appliquer votre propre style au menu système et cela montre comment vous pouvez le faire.
Goto propriétés de la fenêtre définie
window style = none;
Vous n'obtiendrez pas de boutons proches...
Essayez d'ajouter un événement de fermeture à la fenêtre. Ajoutez le code suivant au gestionnaire d'événements.
e.Cancel = true;
Cela empêchera la fenêtre de se fermer. Cela a le même effet que de masquer le bouton de fermeture.
Utilisez ceci, modifié à partir de 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);
}
}
}
Après avoir beaucoup cherché la réponse à cela, j'ai élaboré cette solution simple que je partagerai ici dans l'espoir qu'elle aide les autres.
Je mets WindowStyle=0x10000000
.
Définit les valeurs WS_VISIBLE (0x10000000)
et WS_OVERLAPPED (0x0)
pour le Style de fenêtre. "Chevauché" est la valeur nécessaire pour montrer la barre de titre et la bordure de fenêtre. En enlevant le WS_MINIMIZEBOX (0x20000)
, WS_MAXIMIZEBOX (0x10000)
, et WS_SYSMENU (0x80000)
valeurs de ma valeur de style, tous les boutons de la barre de titre ont été supprimés, y compris le bouton Fermer.
Comme indiqué dans d'autres réponses, vous pouvez utiliser WindowStyle="None"
pour supprimer complètement la barre de titre.
Et, comme indiqué dans les commentaires à ces autres réponses, cela empêche la fenêtre d'être déplaçable, il est donc difficile de la déplacer de sa position initiale.
Cependant, vous pouvez surmonter cela en ajoutant une seule ligne de code au constructeur dans le code de la fenêtre derrière le fichier:
MouseDown += delegate { DragMove(); };
Ou, si vous préférez la syntaxe Lambda:
MouseDown += (sender, args) => DragMove();
Cela rend la fenêtre entière déplaçable. Tout les contrôles interactifs présents dans la fenêtre, tels que les boutons, fonctionneront toujours normalement et n'agiront pas comme des poignées de glisser pour la fenêtre.
Utilisez WindowStyle="SingleBorderWindow"
, cela masquera les boutons max et min de la fenêtre WPF.
Si le besoin n'est d'interdire à l'utilisateur de fermer la fenêtre, c'est une solution simple.
Code XAML:
IsCloseButtonEnabled="False"