web-dev-qa-db-fra.com

Comment masquer le bouton de fermeture dans la fenêtre WPF?

J'écris un dialogue modal dans WPF. Comment définir une fenêtre WPF pour ne pas avoir de bouton de fermeture? Je voudrais toujours pour son WindowState avoir 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 de fermeture mais d'afficher la barre de titre, comme dans les boîtes de dialogue modales.

187
Michael Hedgpeth

WPF n'a pas de propriété intégrée pour masquer le bouton Fermer 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, placez ce code dans l'événement Loaded 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 Fermer. 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 ne sera créé, même lorsque vous cliquerez avec le bouton droit sur la barre de titre.

Notez que Alt + F4 fermera toujours la fenêtre. Si vous ne souhaitez pas autoriser la fermeture de la fenêtre avant que le thread d'arrière-plan ne soit terminé, vous pouvez également remplacer OnClosing et attribuer la valeur true à Cancel, comme l'a suggéré Gabe.

254
Joe White

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 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 le définissez comme suit:

<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>
85
SpudCZ

Définissez la propriété WindowStyle sur Aucune, ce qui masquera la zone de contrôle avec la barre de titre. Pas besoin d'appels kernaux. 

56

Cela ne supprimera pas le bouton de fermeture, mais 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;
}
48
flurbius

Pour désactiver le bouton de fermeture, vous devez ajouter le code suivant à votre classe Window (le code provient de ici , légèrement modifié et reformaté):

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 de fermeture du menu Système et interdit de fermer la boîte de dialogue à l'aide de Alt + F4.

Vous voudrez probablement fermer la fenêtre par programme. Appeler simplement Close() ne fonctionnera pas. Faites quelque chose comme ça:

allowClosing = true;
Close();
15

J'essayais la réponse de Viachaslau car j'aime bien 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. 

Cela a par contre toujours fonctionné (vérification d'erreur 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 );
}
9
stijn

Je viens d'ajouter mon implémentation de la réponse de Joe White using interactivity Behavior (vous devez référencer 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>
8
tom.maruska

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">
7
daniele3004

Laissez l'utilisateur "fermer" la fenêtre mais ne la cachez pas.

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é, réaffiche la fenêtre d'interface utilisateur en 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
2
BSalita

Ce qui suit concerne la désactivation des boutons Fermer et Agrandir/Réduire. En fait, not supprime réellement les boutons (mais les éléments du menu sont également supprimés!). 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 diffère légèrement de la solution Virgoss en ce sens qu’elle supprime les éléments de menu (et le séparateur final, si nécessaire) au lieu de simplement les désactiver. Elle diffère de la solution Joe Whites car elle ne désactive pas tout le menu système. Par conséquent, dans mon cas, je peux conserver le bouton et l'icône Réduire.

Le code suivant prend également en charge la désactivation des boutons Agrandir/Réduire car, contrairement au bouton Fermer, la suppression des entrées du menu ne permet pas au système de rendre les boutons "désactivés", bien que la suppression des entrées du 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 la plus simple consiste à câbler l'événement Canceling et à définir un indicateur lorsque vous souhaitez réellement fermer la fenêtre.

1
user166390

Donc, voici votre problème. Le bouton de fermeture situé en haut à droite d'un cadre de fenêtre ne fait pas partie de la fenêtre WPF, mais appartient à la partie du cadre de fenêtre 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 ne pas en avoir du tout.

1
Muad'Dib

Utilisez ceci, modifié depuis 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);
        }  

    }
}
0
vapcguy

Essayez d'ajouter un événement de clôture à la fenêtre. Ajoutez ce code 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 cacher le bouton de fermeture.

0
Dennis R

propriétés de la fenêtre goto 

window style = none;

vous n'obtiendrez pas de boutons rapprochés ...

0
Pavan R Bhupalam

Cela ne masque pas le bouton mais empêchera l'utilisateur d'avancer en fermant la fenêtre. 

protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{            
    if (e.Cancel == false)
    {
        Application.Current.Shutdown();
    }
}
0
A. Lartey

Après avoir longuement cherché la réponse à cette question, j’ai élaboré cette solution simple que je vais partager ici dans l’espoir d’aider les autres.

Je mets WindowStyle=0x10000000.

Ceci définit les valeurs WS_VISIBLE (0x10000000) et WS_OVERLAPPED (0x0) pour le style de fenêtre. "Chevauchement" est la valeur nécessaire pour afficher la barre de titre et la bordure de la fenêtre. En supprimant les valeurs WS_MINIMIZEBOX (0x20000), WS_MAXIMIZEBOX (0x10000) et WS_SYSMENU (0x80000) de mon style, tous les boutons de la barre de titre ont été supprimés, y compris le bouton Fermer.

0
Joe Horr

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 marcher

Edit - pour l'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. 

Edit 2 This Thread _ montre comment procéder, mais vous devez appliquer votre propre style au menu système et sur la manière de procéder.

0
TStamper