我正在WPF中写一个模态对话框。我如何设置一个WPF窗口没有关闭按钮?我仍然希望它的WindowState有一个正常的标题栏。
我找到了ResizeMode、WindowState和WindowStyle,但这些属性都不允许我隐藏关闭按钮,而是显示标题栏,就像在模态对话框中一样。
我正在WPF中写一个模态对话框。我如何设置一个WPF窗口没有关闭按钮?我仍然希望它的WindowState有一个正常的标题栏。
我找到了ResizeMode、WindowState和WindowStyle,但这些属性都不允许我隐藏关闭按钮,而是显示标题栏,就像在模态对话框中一样。
当前回答
下面是我如何实现类似的目标使用自定义样式没有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>
其他回答
下面是关于禁用关闭和最大化/最小化按钮,它实际上并没有删除按钮(但它确实删除了菜单项!)标题栏上的按钮以禁用/灰色状态绘制。(我还没准备好自己接管所有的功能^^)
这与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);
}
}
}
用法:这必须在初始化源代码后执行。一个好地方是使用窗口的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功能,简单的方法是连接取消事件,并使用设置一个标志,当你真的想关闭窗口。
这不会隐藏按钮,但会阻止用户通过关闭窗口向前移动。
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
if (e.Cancel == false)
{
Application.Current.Shutdown();
}
}
我尝试了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 );
}
使用WindowStyle="SingleBorderWindow",这将隐藏WPF窗口的最大和最小按钮。
尝试在窗口中添加关闭事件。将此代码添加到事件处理程序中。
e.Cancel = true;
这样可以防止窗口关闭。这与隐藏关闭按钮具有相同的效果。