首页 文章

如何在WPF窗口中隐藏关闭按钮?

提问于
浏览
186

我正在WPF中编写一个模态对话框 . 如何将WPF窗口设置为没有关闭按钮?我仍然希望它的WindowState有一个普通的 Headers 栏 .

我找到了ResizeMode,WindowState和WindowStyle,但这些属性都不允许我隐藏关闭按钮但显示 Headers 栏,就像在模态对话框中一样 .

19 回答

  • 2

    所以,这里几乎是你的问题 . 窗口框架右上角的关闭按钮不是WPF窗口的一部分,但它属于操作系统控制的窗口框架部分 . 这意味着您必须使用Win32互操作才能执行此操作 .

    或者,您可以使用noframe并提供自己的“框架”或根本没有框架 .

  • 1

    以下是关于禁用关闭和最大化/最小化按钮,它实际上不删除按钮(但它确实删除菜单项!) . Headers 栏上的按钮以禁用/灰色状态绘制 . (我还没准备好接管所有的功能^^)

    这与Virgoss解决方案略有不同,因为它删除了菜单项(以及尾随分隔符,如果需要),而不是仅仅禁用它们 . 它与Joe Whites解决方案不同,因为它不会禁用整个系统菜单,因此,在我的情况下,我可以保持最小化按钮和图标 .

    以下代码还支持禁用“最大化/最小化”按钮,因为与“关闭”按钮不同,从菜单中删除条目不会导致系统呈现按钮"disabled",即使删除菜单项也会禁用按钮的功能 .

    这个对我有用 . 因人而异 .

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

    用法:必须在初始化源之后完成 . 一个好的地方是使用Window的SourceInitialized事件:

    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
    }
    

    要禁用Alt F4功能,easy方法只是连接Canceling事件,并在您确实要关闭窗口时使用set flag .

  • 8

    WPF没有内置属性来隐藏 Headers 栏的“关闭”按钮,但您可以使用几行P / Invoke来完成 .

    首先,将这些声明添加到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);
    

    然后将此代码放入Window的Loaded事件中:

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

    你去了:没有更多关闭按钮 . 你也不会在 Headers 栏的左侧有一个窗口图标,这意味着没有系统菜单,即使你右键单击 Headers 栏 - 它们都在一起 .

    请注意,Alt F4仍将关闭窗口 . 如果您不想在后台线程完成之前允许窗口关闭,那么您也可以覆盖OnClosing并将Cancel设置为true,如Gabe建议的那样 .

  • 0

    我只是遇到了类似的问题,Joe White的解决方案在我看来简单而干净 . 我重用它并将其定义为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
    
    }
    

    然后在XAML中你只需这样设置:

    <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>
    
  • -2

    WindowStyle 属性设置为None,这将隐藏控件框和 Headers 栏 . 不需要kernal电话 .

  • 8

    这不会摆脱关闭按钮,但它会阻止某人关闭窗口 .

    把它放在你的代码后面的文件中:

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

    要禁用关闭按钮,您应该将以下代码添加到Window类中(代码取自here,编辑并重新格式化了一下):

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

    此代码还禁用“系统”菜单中的“关闭”项,并禁止使用Alt F4关闭对话框 .

    您可能希望以编程方式关闭窗口 . 只是调用 Close() 将无效 . 做这样的事情:

    allowClosing = true;
    Close();
    
  • 55

    我正在尝试Viachaslau的答案,因为我喜欢不删除按钮但禁用它的想法,但由于某种原因它并不总是有效:关闭按钮仍然启用但没有任何错误 .

    另一方面,这总是起作用(省略错误检查):

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

    我只是使用Interactivity Behavior添加我的Joe White's answer实现(您需要引用System.Windows.Interactivity) .

    码:

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

    用法:

    <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>
    
  • 0

    要设置的属性是=> 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">
    
  • 0

    让用户“关闭”窗口,但实际上只是隐藏它 .

    在窗口的OnClosing事件中,如果已经可见则隐藏窗口:

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

    每次执行后台线程时,重新显示后台UI窗口:

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

    终止程序执行时,请确保所有窗口都可以关闭:

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

    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>
    

    应该管用

    Edit - 你的瞬间这个Thread显示了它是如何做到的可以完成,但我不认为Window有一个属性来获得你想要的东西,而不会失去正常的 Headers 栏 .

    Edit 2Thread显示了一种完成它的方法,但您必须将自己的样式应用于系统菜单,它会显示如何执行此操作的方式 .

  • 1

    转到窗口属性集

    window style = none;
    

    你不会得到关闭按钮......

  • 84

    尝试将Closing事件添加到窗口 . 将此代码添加到事件处理程序 .

    e.Cancel = true;
    

    这将阻止窗口关闭 . 这与隐藏关闭按钮具有相同的效果 .

  • 0

    使用此,从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);
            }  
    
        }
    }
    
  • 48

    经过多次寻找答案后,我找到了这个简单的解决方案,我将在这里分享,希望能帮助别人 .

    我设置 WindowStyle=0x10000000 .

    这将为Window Style设置 WS_VISIBLE (0x10000000)WS_OVERLAPPED (0x0) 值 . "Overlapped"是显示 Headers 栏和窗口边框的必要值 . 通过从样式值中删除 WS_MINIMIZEBOX (0x20000)WS_MAXIMIZEBOX (0x10000)WS_SYSMENU (0x80000) 值, Headers 栏中的所有按钮都被删除,包括“关闭”按钮 .

  • 15

    如其他答案中所述,您可以使用 WindowStyle="None" 完全删除 Headers 栏 .

    并且,正如对其他答案的评论中所述,这可以防止窗口被拖动,因此很难将窗口从其初始位置移开 .

    但是,您可以通过在Window的Code Behind文件中向构造函数添加一行代码来克服这个问题:

    MouseDown += delegate { DragMove(); };
    

    或者,如果您更喜欢Lambda语法:

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

    这使得整个Window可以拖动 . 窗口中存在的任何交互式控件(如按钮)仍将正常工作,不会充当窗口的拖动句柄 .

  • -2

    使用 WindowStyle="SingleBorderWindow" ,这将隐藏WPF窗口中的最大和最小按钮 .

  • 253

    如果只需要禁止用户关闭窗口,这是一个简单的解决方案 .

    XAML代码: IsCloseButtonEnabled="False"

相关问题