如何隐藏WPF窗口中的closuresbutton?
我在WPF中编写一个modal dialog。 如何设置一个WPF窗口没有closuresbutton? 我仍然喜欢它的WindowState有一个正常的标题栏。
我发现ResizeMode,WindowState和WindowStyle,但是这些属性都不允许我隐藏closuresbutton,但显示标题栏,如在模式对话框中。
WPF没有内置属性来隐藏标题栏的closuresbutton,但是你可以用几行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's Loaded事件中:
var hwnd = new WindowInteropHelper(this).Handle; SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
你去了:没有更多的closuresbutton。 标题栏左侧也没有窗口图标,即使您右键单击标题栏,也不会显示“系统”菜单 – 它们都会一起显示。
请注意,Alt + F4将仍然closures窗口。 如果您不想在后台线程完成之前closures窗口,那么您也可以重写OnClosing并将Cancel设置为true,如Gabe所示。
我刚刚遇到类似的问题,乔·怀特的解决scheme似乎对我来说简单而干净。 我重用它并将其定义为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>
这不会消除closuresbutton,但会阻止某人closures窗口。
把它放在你的代码后面:
protected override void OnClosing(CancelEventArgs e) { base.OnClosing(e); e.Cancel = true; }
将WindowStyle
属性设置为None,这将隐藏控制框和标题栏。 不需要内核调用。
要禁用closuresbutton,您应该将以下代码添加到您的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; }
此代码还禁用系统菜单中的closures项目,并禁止使用Alt + F4closures对话框。
您可能想要以编程方式closures窗口。 只要调用Close()
将不起作用。 做这样的事情:
allowClosing = true; Close();
我试图Viachaslau的答案,因为我喜欢不删除button,但禁用它的想法,但由于某种原因,它并不总是工作:closuresbutton仍然启用,但没有任何错误。
这另一方面总是工作(错误检查省略):
[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 ); }
我只是使用Interactivity Behavior添加我的Joe White答案的实现(您需要引用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>
要设置的属性是=> 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">
所以,这里几乎是你的问题。 窗口右上angular的closuresbutton不是WPF窗口的一部分,但它属于由您的操作系统控制的窗口框架的一部分。 这意味着你将不得不使用Win32互操作。
另外,您可以使用noframe,并提供您自己的“框架”或根本没有框架。
以下是closuresclosures和最大化/最小化button,它实际上并没有删除button(但它确实删除了菜单项!)。 标题栏上的button以禁用/变灰的状态绘制。 (我还没准备好自己接pipe所有的function^^)
这与Virgoss解决scheme略有不同,因为它删除了菜单项(如果需要的话,删除尾随分隔符),而不是仅仅禁用它们。 它不同于Joe Whites解决scheme,因为它不会禁用整个系统菜单,所以在我的情况下,我可以保留最小化button和图标。
以下代码还支持禁用“最大化/最小化”button,因为与“closures”button不同,从菜单中删除条目不会导致系统呈现“禁用”button,即使删除菜单条目也会禁用button的function。
它适用于我。 因人而异。
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 }
要禁用Alt + F4function,简单的方法就是连接取消事件,并在您确实要closures窗口时使用设置标志。
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>
应该工作
编辑 – 为你的即时这个线程显示如何做到这一点,但我不认为窗户有一个属性得到你想要的,而不会失去正常的标题栏。
编辑2这个线程显示了一个完成它的方法,但是你必须把你自己的样式应用到系统菜单,并且显示了你如何做到这一点。
让用户“closures”窗口,但真的只是隐藏它。
在窗口的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()
终止执行程序时,确保所有的窗口都是/可以closures的:
Private Sub CloseAll() If w IsNot Nothing Then w.Visibility = Windows.Visibility.Collapsed ' Tell OnClosing to really close w.Close() End If End Sub
转到窗口属性设置
window style = none;
你不会closuresbutton…
尝试添加closures事件到窗口。 将此代码添加到事件处理程序。
e.Cancel = true;
这将防止窗户closures。 这与隐藏closuresbutton具有相同的效果。
使用这个,从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); } } }
正如其他答案中所述,您可以使用WindowStyle="None"
来完全删除标题栏。
而且,正如在对其他答案的评论中指出的那样,这可以防止窗口被拖动,因此很难将其从初始位置移开。
但是,您可以通过将一行代码添加到窗口的代码隐藏文件中的构造函数中来解决此问题:
MouseDown += delegate { DragMove(); };
或者,如果您更喜欢Lambda语法:
MouseDown += (sender, args) => DragMove();
这使整个窗口可拖动。 窗口中存在的任何交互式控件(如button)仍然可以正常工作,并且不会用作窗口的拖动控件。
如果需要只是禁止用户closures窗口,这是一个简单的解决scheme。
XAML代码: IsCloseButtonEnabled="False"
使用WindowStyle="SingleBorderWindow"
,这将从WPF窗口隐藏最大值和最小值button。