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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/743906/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 23:28:26  来源:igfitidea点击:

How to hide close button in WPF window?

c#wpfxamlbuttondialog

提问by Michael Hedgpeth

I'm writing a modal dialog in WPF. How do I set a WPF window to not have a close button? I'd still like for its WindowStateto have a normal title bar.

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

I found ResizeMode, WindowState, and WindowStyle, but none of those properties allow me to hide the close button but show the title bar, as in modal dialogs.

我发现ResizeMode, WindowState, 和WindowStyle, 但这些属性都不允许我隐藏关闭按钮但显示标题栏,就像在模式对话框中一样。

采纳答案by Joe White

WPF doesn't have a built-in property to hide the title bar's Close button, but you can do it with a few lines of P/Invoke.

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

First, add these declarations to your Window 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);

Then put this code in the Window's Loadedevent:

然后把这段代码放在Window的Loaded事件中:

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

And there you go: no more Close button. You also won't have a window icon on the left side of the title bar, which means no system menu, even when you right-click the title bar - they all go together.

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

Note that Alt+F4will still close the Window. If you don't want to allow the window to close before the background thread is done, then you could also override OnClosingand set Cancelto true, as Gabe suggested.

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

回答by TStamper

XAML 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>

should work

应该管用

Edit- for your instant this Threadshows how that can be done but I don't think Window has a property to get what you want without losing the normal title bar.

编辑- 此线程立即显示如何完成此操作,但我认为 Window 不具有在不丢失正常标题栏的情况下获得所需内容的属性。

Edit 2This Threadshows a way for it to be done, but you must apply your own style to the system menu and it shows a way how you can do that.

编辑 2线程显示了一种完成它的方法,但您必须将自己的样式应用到系统菜单中,并且它显示了一种如何做到这一点的方法。

回答by Muad'Dib

So, pretty much here is your problem. The close button on the upper right of a window frame is not part of the WPF window, but it belongs to the part of the window frame that is controled by your OS. This means you will have to use Win32 interop to do it.

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

alternativly, you can use the noframe and either provide your own "frame" or have no frame at all.

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

回答by flurbius

This won't get rid of the close button, but it will stop someone closing the window.

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

Put this in your code behind file:

把它放在你的代码隐藏文件中:

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

回答by Viachaslau Tysianchuk

To disable close button you should add the following code to your Window class (the code was taken from here, edited and reformatted a bit):

要禁用关闭按钮,您应该将以下代码添加到您的 Window 类(代码取自此处,对其进行了一些编辑和重新格式化):

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

This code also disables close item in System menu and disallows closing the dialog using Alt+F4.

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

You will probably want to close the window programmatically. Just calling Close()will not work. Do something like this:

您可能希望以编程方式关闭窗口。只是打电话是Close()行不通的。做这样的事情:

allowClosing = true;
Close();

回答by Viachaslau Tysianchuk

The following is about disabling the close and Maximize/Minimize buttons, it does notactually remove the buttons (but it does remove the menu items!). The buttons on the title bar are drawn in a disabled/grayed state. (I'm not quite ready to take over all the functionality myself ^^)

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

This is slightly different than Virgoss solution in that it removes the menu items (and the trailing separator, if needed) instead of just disabling them. It differs from Joe Whites solution as it does not disable the entire system menu and so, in my case, I can keep around the Minimize button and icon.

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

The follow code also supports disabling the Maximize/Minimize buttons as, unlike the Close button, removing the entries from the menu does not cause the system to render the buttons "disabled" even though removing the menu entries doesdisable the functionality of the buttons.

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

It works for me. 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);
            }
        }
    }

Usage: This must be done AFTER the source is initialized. A good place is to use the SourceInitialized event of the Window:

用法:这必须在源初始化之后完成。一个好地方是使用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
}

To disable the Alt+F4 functionality the easy method is just to wire up the Canceling event and use set a flag for when you really do want to close the window.

要禁用 Alt+F4 功能,最简单的方法是连接取消事件并在您确实想要关闭窗口时使用设置标志。

回答by SpudCZ

I just got to similar problem and Joe White's solutionseems to me simple and clean. I reused it and defined it as an attached property of Window

我刚刚遇到了类似的问题,乔怀特的解决方案在我看来简单而干净。我重用它并将其定义为 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

}

Then in XAML you just set it like this:

然后在 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>

回答by BSalita

Let the user "close" the window but really just hide it.

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

In the window's OnClosing event, hide the window if already visible:

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

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

Each time the Background thread is to be executed, re-show background UI window:

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

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

When terminating execution of program, make sure all windows are/can-be closed:

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

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

回答by Vishal Nayan

Use WindowStyle="SingleBorderWindow", this will hide max and min button from WPF Window.

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

回答by Sriwantha Attanayake

Set WindowStyleproperty to None which will hide the control box along with the title bar. No need to kernal calls.

WindowStyle属性设置为 None 将隐藏控制框和标题栏。无需内核调用。