【问题标题】:How to hide close button in WPF window?如何在 WPF 窗口中隐藏关闭按钮?
【发布时间】:2010-10-19 03:18:48
【问题描述】:

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

我找到了 ResizeModeWindowStateWindowStyle,但这些属性都不允许我隐藏关闭按钮但显示标题栏,就像在模式对话框中一样。

【问题讨论】:

  • 这是一个运行后台线程的进度对话框,不支持取消;我想我只是想成功,所以我不必支持取消(还)。不过,您可能是对的。
  • 我也讨厌试图删除窗口镶边的应用程序。如果我创建一个进度对话框,我总是让窗口关闭按钮执行与单击实际取消​​按钮相同的逻辑。
  • 致 Chris:假设您的软件用于视频监控。夜间安全特工(这是他的工作)保持窗户打开......但有时他们的工作很无聊,他们想上网冲浪或出于任何原因关闭视频矩阵窗口,删除窗口按钮是正确的方法去做。
  • @ChrisUpchurch, “你为什么要这样做?它让我觉得 UI 设计真的很糟糕。” - 真正的“糟糕的 UI 设计”是指程序呈现一个带有OK的对话框; 取消关闭按钮。对于用户来说,Close 的作用可能并不明显。它是取消还是提交Consensus is not to include close buttons in dialogs 就是这样
  • @Jean-Marie 但是隐藏关闭按钮并不能阻止这种情况的发生,它只会愚弄不知情和懒惰的人(对谷歌)。隐藏关闭按钮只会阻止单击该按钮。 Win 键和 alt 键组合仍将正常工作“正确”的方法是为工作人员创建一个用户帐户,并使用组策略阻止他们打开/安装未经批准的任何软件。然后有一个管理员帐户,主管可以访问,以处理任何维护。

标签: c# wpf xaml button dialog


【解决方案1】:

以下是我使用自定义样式实现类似目标的方法,而无需 DllImports 和 P/Invoke 调用。这将使用WindowStyle="none" 删除现有的标题栏,并显示一个具有相似背景颜色的“TextBlock”以指示为标题栏。

XAML 代码

<Window x:Class="AddBook"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="http://wpftoolkit.my-libraries.com/v5" 
    WindowStartupLocation="CenterOwner"        
    ResizeMode="NoResize" 
    Style="{DynamicResource WindowStyleX}"
    ShowInTaskbar="False"
    ShowActivated="True"
    SizeToContent="Height"
    Title="Add New Book" 
    Width="450">
..............

</Window>

XAML

<Style x:Key="WindowStyleX" TargetType="{x:Type Window}">
<Setter Property="WindowStyle" Value="None" />
<Setter Property="AllowsTransparency" Value="False" />
<Setter Property="ResizeMode" Value="NoResize" />
<Setter Property="Background" Value="White" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="{x:Type Window}">
            <Border BorderBrush="{DynamicResource BlackColor}" BorderThickness="1">
                <Grid Background="{TemplateBinding Background}">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="30" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <Border
                        Grid.Row="0"
                        Grid.ColumnSpan="2"
                        Background="{DynamicResource BlackColor}">
                        <Grid>
                            <TextBlock
                                Grid.Column="1"
                                Margin="10,0,0,0"
                                HorizontalAlignment="Left"
                                VerticalAlignment="Center"
                                FontSize="16"
                                Foreground="{DynamicResource WhiteTextForeground}"
                                Text="{TemplateBinding Title}" />
                        </Grid>
                    </Border>
                    <ContentPresenter Grid.Row="1" />
                </Grid>
            </Border>
        </ControlTemplate>
    </Setter.Value>
</Setter>

【讨论】:

    【解决方案2】:

    WPF 没有隐藏标题栏的关闭按钮的内置属性,但您可以通过几行 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);
    

    然后就可以了:不再有关闭按钮。您也不会在标题栏的左侧有一个窗口图标,这意味着没有系统菜单,即使您右键单击标题栏 - 它们都在一起。

    重要提示:所有这些都是隐藏按钮。用户仍然可以关闭窗口!如果用户按下Alt+F4,或者通过任务栏关闭应用,窗口仍然会关闭。

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

    【讨论】:

    • 根据文档,我们应该改用SetWindowLongPtr
    • 主要是给自己的注释... DllImport 的命名空间 -> System.Runtime.InteropServices.DllImport。 WindowInteropHelper 的命名空间 -> System.Windows.Interop.WindowInteropHelper
    • 实际上,这种方法隐藏了所有三个按钮(Min、Max 和 Close)。是否可以只隐藏关闭按钮?
    • @miliu,不。你可以disable it,但是你不能隐藏它而不隐藏最小化/最大化。我怀疑 Windows 开发人员认为,如果 Maximize 位于 Close 通常所在的右侧,那会令人困惑。
    • 将 WindowStyle="None" 放在 XAML 文件中的 Window 标记上。
    【解决方案3】:

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

    XAML 代码: IsCloseButtonEnabled="False"

    它阻止了按钮。

    【讨论】:

      【解决方案4】:

      我非常喜欢this answer,它使用附加属性来调解行为。但是,我发现答案的实现过于复杂,而且它也没有解决即使使用 Alt+F4 也无法关闭窗口的次要目标。所以我提供了这个替代方案:

      enum CloseButtonVisibility
      {
          Visible,
          Hidden,
          CloseDisabled,
      }
      
      static class WindowEx
      {
          private static readonly CancelEventHandler _cancelCloseHandler = (sender, e) => e.Cancel = true;
      
          public static readonly DependencyProperty CloseButtonVisibilityProperty =
              DependencyProperty.RegisterAttached(
                  "CloseButtonVisibility",
                  typeof(CloseButtonVisibility),
                  typeof(WindowEx),
                  new FrameworkPropertyMetadata(CloseButtonVisibility.Visible, new PropertyChangedCallback(_OnCloseButtonChanged)));
      
          [AttachedPropertyBrowsableForType(typeof(Window))]
          public static CloseButtonVisibility GetCloseButtonVisibility(Window obj)
          {
              return (CloseButtonVisibility)obj.GetValue(CloseButtonVisibilityProperty);
          }
      
          [AttachedPropertyBrowsableForType(typeof(Window))]
          public static void SetCloseButtonVisibility(Window obj, CloseButtonVisibility value)
          {
              obj.SetValue(CloseButtonVisibilityProperty, value);
          }
      
          private static void _OnCloseButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              if (!(d is Window window))
              {
                  return;
              }
      
              if (e.OldValue is CloseButtonVisibility oldVisibility)
              {
                  if (oldVisibility == CloseButtonVisibility.CloseDisabled)
                  {
                      window.Closing -= _cancelCloseHandler;
                  }
              }
      
              if (e.NewValue is CloseButtonVisibility newVisibility)
              {
                  if (newVisibility == CloseButtonVisibility.CloseDisabled)
                  {
                      window.Closing += _cancelCloseHandler;
                  }
      
                  if (!window.IsLoaded)
                  {
                      // NOTE: if the property is set multiple times before the window is loaded,
                      // the window will wind up with multiple event handlers. But they will all
                      // set the same value, so this is fine from a functionality point of view.
                      //
                      // The handler is never unsubscribed, so there is some nominal overhead there.
                      // But it would be incredibly unusual for this to be set more than once
                      // before the window is loaded, and even a handful of delegate instances
                      // being around that are no longer needed is not really a big deal.
                      window.Loaded += _ApplyCloseButtonVisibility;
                  }
                  else
                  {
                      _SetVisibility(window, newVisibility);
                  }
              }
          }
      
          #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 void _ApplyCloseButtonVisibility(object sender, RoutedEventArgs e)
          {
              Window window = (Window)sender;
              CloseButtonVisibility visibility = GetCloseButtonVisibility(window);
      
              _SetVisibility(window, visibility);
          }
      
          private static void _SetVisibility(Window window, CloseButtonVisibility visibility)
          {
              var hwnd = new WindowInteropHelper(window).Handle;
      
              if (visibility == CloseButtonVisibility.Visible)
              {
                  SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_SYSMENU);
              }
              else
              {
                  SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
              }
          }
      }
      

      这提供了三种状态可供选择:

      1. 可见
      2. 隐藏,但用户仍然可以使用 Alt+F4 关闭
      3. 隐藏,完全禁用关闭

      请注意,默认情况下,从不关闭的窗口将阻止 WPF 程序的进程终止。因此,如果您选择使用CloseButtonVisibility.CloseDisabled 值,您将需要自定义Application.Run() 行为,或者在退出前重新启用关闭窗口。例如。在你的主窗口中,你可能有这样的东西:

      protected override void OnClosed(EventArgs e)
      {
          WindowEx.SetCloseButtonVisibility(this.toolWindow.Value, CloseButtonVisibility.Hidden);
          this.toolWindow.Value.Close();
      
          base.OnClosed(e);
      }
      

      其中toolWindow 是禁用关闭按钮的窗口的Window 引用。

      以上假设窗口通常只是在正常 UI 活动期间隐藏并根据需要显示。当然,您也可以随时选择显式关闭窗口,但同样的技术——将选项设置为不禁用关闭,然后显式关闭窗口——仍然适用。

      【讨论】:

        【解决方案5】:

        我刚遇到类似的问题,Joe White's solution 在我看来简单而干净。我重用了它并将其定义为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>
        

        【讨论】:

        • 应该是 window.Loaded += ShowWhenLoadedDelegate; 而不是 -= 吗?否则我看不到任何地方可以调用 ShowWhenLoadedDelegate。
        【解决方案6】:

        这不会隐藏按钮,但会阻止用户通过关闭窗口继续前进。

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

        【讨论】:

          【解决方案7】:

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

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

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

          它对我有用。 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);
                      }
                  }
              }
          

          用法:这必须在源初始化之后完成。一个好地方是使用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 功能,最简单的方法是连接 Canceling 事件并在您确实想要关闭窗口时使用设置标志。

          【讨论】:

            【解决方案8】:

            在寻找这个问题的答案之后,我制定了这个简单的解决方案,我将在这里分享,希望它可以帮助其他人。

            我设置了WindowStyle=0x10000000

            这会设置窗口样式的WS_VISIBLE (0x10000000)WS_OVERLAPPED (0x0) 值。 “重叠”是显示标题栏和窗口边框的必要值。通过从我的样式值中删除 WS_MINIMIZEBOX (0x20000)WS_MAXIMIZEBOX (0x10000)WS_SYSMENU (0x80000) 值,标题栏中的所有按钮都被删除了,包括关闭按钮。

            【讨论】:

            • 在 WPF 中 WindowStyle 是一个枚举,其值与 Windows API 常量不匹配;将值强制为WindowStyle 枚举将不起作用。可以肯定的是,我已经检查了 ILSpy 中的 .NET 源代码;枚举值在私有函数CreateWindowStyle 中被转换为Windows API,如果函数遇到未知的WindowStyle 值,它只会应用WindowStyle.None。 (唯一的方法是使用内部属性 _Style_StyleEx 使用反射,我强烈建议不要这样做。)
            【解决方案9】:

            使用这个,修改自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);
                    }  
            
                }
            }
            

            【讨论】:

              【解决方案10】:

              尝试向窗口添加关闭事件。将此代码添加到事件处理程序。

              e.Cancel = true;

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

              【讨论】:

              • "这与隐藏关闭按钮的效果相同。"除了按钮仍然可见和可点击,即当你点击它时它是动画的并在视觉上按下它——这违背了POLA
              【解决方案11】:

              我只是使用交互行为添加了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>
              

              【讨论】:

                【解决方案12】:

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

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

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

                【讨论】:

                • 请注意,在设置为模式对话框的Window 中执行此操作会干扰Window 设置其DialogResult 属性并可能使其无法使用。 stackoverflow.com/questions/898708/cant-set-dialogresult-in-wpf
                • 我使用这种方法出现溢出,我取出 base.OnClosing(e) 然后它工作了
                • 作为用户,我会讨厌将其放入应用程序的程序员
                • @UrbanEsc 我倾向于同意这是一件令人讨厌的事情,但是当我这样做时——而且只是一次——这是一项强制性要求,而且是一种必要的邪恶,有一些非常重要的过程正在进行,无法中断,应用程序在完成之前无法继续。还有其他方法可以完成(后台线程,在准备好之前禁用 UI)但老板和客户都喜欢这种方式,因为它强调了过程的重要性。
                【解决方案13】:

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

                【讨论】:

                • 这也隐藏了最大/最小按钮
                • 它删除了整个标题栏,使框变得丑陋且没有描述。霰弹枪方法和重复的答案。投反对票。
                • 这是自助服务终端应用程序的最佳解决方案,这些应用程序始终需要最大化其应用程序并且不应让客户关闭应用程序。所以支持投票
                【解决方案14】:

                如其他答案所述,您可以使用WindowStyle="None" 完全删除标题栏。

                而且,正如其他答案的 cmets 中所述,这可以防止窗口被拖动,因此很难将其从初始位置移动。

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

                MouseDown += delegate { DragMove(); };
                

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

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

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

                【讨论】:

                • 还是个坏主意。它删除了整个标题栏,使其成为一种霰弹枪方法,并使盒子看起来很难看,意味着它没有标题/描述。还有更好的选择。
                • @vapcguy 它删除了整个标题栏。这是一种霰弹枪方法。让盒子难看?你的意见。更好的选择?对你来说,也许。不适合所有人。 :-)
                【解决方案15】:

                转到窗口属性设置

                window style = none;
                

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

                【讨论】:

                • 投反对票。它实际上是WindowStyle = "None" - 注意你的语法。另一方面,当有很多更好的方法来处理这个问题时(正如其他答案所证明的那样),它也是一种猎枪方法,它也删除了标题栏,使盒子变得丑陋并且缺少标题,并且是重复的答案。
                【解决方案16】:

                我正在尝试 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 );
                }
                

                【讨论】:

                • 完美!在我的项目中添加为Window 扩展方法。
                【解决方案17】:

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

                【讨论】:

                • 没有解决隐藏close按钮的问题
                【解决方案18】:

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

                【讨论】:

                • 好吧,这将完全隐藏窗口标题栏。这意味着您没有获得窗口标题,用户将无法移动窗口。
                • 您可以通过在窗口的MouseDown 事件中添加this.DragMove(); 来使窗口可移动
                • 对于一个应该是纯信息性和强制性的模态对话框,例如使用已打开的旧模式升级数据库的进度,此解决方案是完美的。
                • 我想有些人希望有一个边框,虽然
                • 绝对是最好的解决方案。给面板加边框,或者实现移动都没有问题。
                【解决方案19】:

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

                在窗口的 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
                

                【讨论】:

                  【解决方案20】:

                  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>
                  

                  应该有效

                  编辑- 这个Thread 显示了如何做到这一点,但我不认为 Window 具有在不丢失正常标题栏的情况下获得所需内容的属性。

                  编辑 2 这个Thread 展示了一种实现方式,但您必须将自己的样式应用到系统菜单中,它展示了一种实现方式。

                  【讨论】:

                  • 由于某种原因刚刚显示“应该工作”,但现在已经更新
                  • 我说的是窗口状态,它在标题栏中。这看起来像编辑一个简单的按钮。
                  • @TStamper,我该如何使用你的 sn-p?我正在使用全局窗口样式(和模板)。
                  • @Shimmy- 你指的是哪一个?
                  【解决方案21】:

                  要禁用关闭按钮,您应该将以下代码添加到您的 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();
                  

                  【讨论】:

                  • 在 Windows 7 中:以上还禁用(但不删除)下拉系统菜单中的关闭项目。关闭按钮本身是禁用的(看起来是灰色的),但没有被删除。这个技巧不适用于最小化/最大化项目/按钮——我怀疑 WPF 会重新启用它们。
                  • 禁用按钮比仅仅删除它们更好,它保持一致的感觉,同时让用户知道一个重要的操作正在运行。
                  【解决方案22】:

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

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

                  【讨论】:

                    猜你喜欢
                    • 2015-12-01
                    • 2020-11-21
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-09-25
                    • 1970-01-01
                    相关资源
                    最近更新 更多