我正在WPF中编写模式对话框。如何将WPF窗口设置为没有关闭按钮?我仍然希望它WindowState
有一个普通的标题栏。
我发现了ResizeMode
,WindowState
和WindowStyle
,但是这些属性都不能让我隐藏关闭按钮,而是显示标题栏,如模式对话框中一样。
我正在WPF中编写模式对话框。如何将WPF窗口设置为没有关闭按钮?我仍然希望它WindowState
有一个普通的标题栏。
我发现了ResizeMode
,WindowState
和WindowStyle
,但是这些属性都不能让我隐藏关闭按钮,而是显示标题栏,如模式对话框中一样。
Answers:
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
改用。
我只是遇到了类似的问题,在我看来,乔·怀特的解决方案简单明了。我重用了它并将其定义为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>
将WindowStyle
属性设置为“无”将隐藏控件框以及标题栏。无需进行核心通话。
this.DragMove();
窗口MouseDown
事件来使窗口可移动
这不会摆脱关闭按钮,但会阻止某人关闭窗口。
将其放在文件后面的代码中:
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
e.Cancel = true;
}
Window
设置为模式对话框的窗口中执行此操作会干扰Window
其DialogResult
属性的设置,并可能使其无法使用。stackoverflow.com/questions/898708/cant-set-dialogresult-in-wpf
要禁用关闭按钮,您应该在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;
}
此代码还禁用“系统”菜单中的关闭项目,并不允许使用Alt + F4关闭对话框。
您可能需要以编程方式关闭窗口。只是打电话是Close()
行不通的。做这样的事情:
allowClosing = true;
Close();
我尝试了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
在我的项目中添加为扩展方法。
要设置的属性是=> 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">
我只是使用“交互行为” 添加了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>
让用户“关闭”窗口,但实际上只是将其隐藏。
在窗口的OnClosing事件中,如果已经可见,则将其隐藏:
If Me.Visibility = Windows.Visibility.Visible Then
Me.Visibility = Windows.Visibility.Hidden
e.Cancel = True
End If
每次执行后台线程时,请重新显示后台用户界面窗口:
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
以下是有关禁用关闭和最大化/最小化按钮的信息,它实际上并未删除按钮(但确实删除了菜单项!)。标题栏上的按钮以禁用/灰色状态绘制。(我还没有准备好亲自接管所有功能^^)
这与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事件,并在确实要关闭窗口时使用set标志。
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>
应该管用
编辑 -此线程立即显示如何完成此操作,但我不认为Window具有在不失去常规标题栏的情况下获取所需内容的属性。
编辑2 此线程显示了完成此操作的方法,但是您必须将自己的样式应用于系统菜单,并且显示如何执行此操作。
使用从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"
完全删除标题栏。
并且,如对其他答案的注释中所述,这防止了窗口被拖动,因此很难将其从其初始位置移开。
但是,您可以通过在Window的Code Behind文件中向构造函数添加一行代码来克服此问题:
MouseDown += delegate { DragMove(); };
或者,如果您更喜欢Lambda语法:
MouseDown += (sender, args) => DragMove();
这使得整个窗口可拖动。窗口中存在的任何交互式控件(如按钮)仍将正常工作,并且不会充当窗口的拖动柄。
在寻找答案之后,我制定了这个简单的解决方案,我将在这里分享,希望对其他人有所帮助。
我定了WindowStyle=0x10000000
。
这将设置“ 窗口样式” 的WS_VISIBLE (0x10000000)
和WS_OVERLAPPED (0x0)
值。“重叠”是显示标题栏和窗口边框的必要值。通过移除WS_MINIMIZEBOX (0x20000)
,WS_MAXIMIZEBOX (0x10000)
以及WS_SYSMENU (0x80000)
从我的风格值值,所有从标题栏的按钮被拆除,其中包括关闭按钮。
WindowStyle
是一个枚举,其值与Windows API常数不匹配;将值强制为WindowStyle
枚举将不起作用。可以肯定的是,我已经检查了ILSpy中的.NET源代码。枚举值将转换为私有函数中的Windows API CreateWindowStyle
,如果该函数遇到未知WindowStyle
值,则仅应用WindowStyle.None
。(唯一的方法是使用内部属性_Style
并_StyleEx
使用反射,强烈反对。)
如果只需要禁止用户关闭窗口,这是一个简单的解决方案。
XAML代码:
IsCloseButtonEnabled="False"