我正在WPF中写一个模态对话框。我如何设置一个WPF窗口没有关闭按钮?我仍然希望它的WindowState有一个正常的标题栏。
我找到了ResizeMode、WindowState和WindowStyle,但这些属性都不允许我隐藏关闭按钮,而是显示标题栏,就像在模态对话框中一样。
我正在WPF中写一个模态对话框。我如何设置一个WPF窗口没有关闭按钮?我仍然希望它的WindowState有一个正常的标题栏。
我找到了ResizeMode、WindowState和WindowStyle,但这些属性都不允许我隐藏关闭按钮,而是显示标题栏,就像在模态对话框中一样。
当前回答
在寻找了很多答案之后,我想出了这个简单的解决方法,我将在这里分享,希望它能帮助到其他人。
我设置WindowStyle=0x10000000。
这设置了窗口样式的WS_VISIBLE (0x10000000)和WS_OVERLAPPED (0x0)值。"Overlapped"是显示标题栏和窗口边框的必要值。通过从我的样式值中删除WS_MINIMIZEBOX (0x20000)、WS_MAXIMIZEBOX (0x10000)和WS_SYSMENU (0x80000)值,标题栏中的所有按钮都被删除了,包括关闭按钮。
其他回答
我非常喜欢这个答案,它使用附加属性来调节行为。然而,我发现答案的实现过于复杂,它也没有解决防止窗口关闭的次要目标,即使使用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);
}
}
}
这提供了三种状态可供选择:
可见 隐藏,但用户仍然可以使用Alt+F4关闭 隐藏,关闭是完全禁用的
请注意,默认情况下,永不关闭的窗口将防止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是关闭按钮被禁用的窗口的窗口引用。
上面假设在正常的UI活动期间,窗口通常只是隐藏并根据需要显示。当然,您也可以选择在任何时候显式地关闭窗口,但是同样的技术—将选项设置为不禁用关闭,然后显式地关闭窗口—仍然适用。
尝试在窗口中添加关闭事件。将此代码添加到事件处理程序中。
e.Cancel = true;
这样可以防止窗口关闭。这与隐藏关闭按钮具有相同的效果。
使用这个,从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);
}
}
}
如果需要的只是禁止用户关闭窗口,这是一个简单的解决方案。
XAML守则: IsCloseButtonEnabled =“虚假”
它挡住了按钮。
下面是我如何实现类似的目标使用自定义样式没有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>