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);
}
}
}
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);
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);
}
}
}
19 回答
所以,这里几乎是你的问题 . 窗口框架右上角的关闭按钮不是WPF窗口的一部分,但它属于操作系统控制的窗口框架部分 . 这意味着您必须使用Win32互操作才能执行此操作 .
或者,您可以使用noframe并提供自己的“框架”或根本没有框架 .
以下是关于禁用关闭和最大化/最小化按钮,它实际上不删除按钮(但它确实删除菜单项!) . Headers 栏上的按钮以禁用/灰色状态绘制 . (我还没准备好接管所有的功能^^)
这与Virgoss解决方案略有不同,因为它删除了菜单项(以及尾随分隔符,如果需要),而不是仅仅禁用它们 . 它与Joe Whites解决方案不同,因为它不会禁用整个系统菜单,因此,在我的情况下,我可以保持最小化按钮和图标 .
以下代码还支持禁用“最大化/最小化”按钮,因为与“关闭”按钮不同,从菜单中删除条目不会导致系统呈现按钮"disabled",即使删除菜单项也会禁用按钮的功能 .
这个对我有用 . 因人而异 .
用法:必须在初始化源之后完成 . 一个好的地方是使用Window的SourceInitialized事件:
要禁用Alt F4功能,easy方法只是连接Canceling事件,并在您确实要关闭窗口时使用set flag .
WPF没有内置属性来隐藏 Headers 栏的“关闭”按钮,但您可以使用几行P / Invoke来完成 .
首先,将这些声明添加到Window类:
然后将此代码放入Window的Loaded事件中:
你去了:没有更多关闭按钮 . 你也不会在 Headers 栏的左侧有一个窗口图标,这意味着没有系统菜单,即使你右键单击 Headers 栏 - 它们都在一起 .
请注意,Alt F4仍将关闭窗口 . 如果您不想在后台线程完成之前允许窗口关闭,那么您也可以覆盖OnClosing并将Cancel设置为true,如Gabe建议的那样 .
我只是遇到了类似的问题,Joe White的解决方案在我看来简单而干净 . 我重用它并将其定义为Window的附加属性
然后在XAML中你只需这样设置:
将
WindowStyle
属性设置为None,这将隐藏控件框和 Headers 栏 . 不需要kernal电话 .这不会摆脱关闭按钮,但它会阻止某人关闭窗口 .
把它放在你的代码后面的文件中:
要禁用关闭按钮,您应该将以下代码添加到Window类中(代码取自here,编辑并重新格式化了一下):
此代码还禁用“系统”菜单中的“关闭”项,并禁止使用Alt F4关闭对话框 .
您可能希望以编程方式关闭窗口 . 只是调用
Close()
将无效 . 做这样的事情:我正在尝试Viachaslau的答案,因为我喜欢不删除按钮但禁用它的想法,但由于某种原因它并不总是有效:关闭按钮仍然启用但没有任何错误 .
另一方面,这总是起作用(省略错误检查):
我只是使用Interactivity Behavior添加我的Joe White's answer实现(您需要引用System.Windows.Interactivity) .
码:
用法:
要设置的属性是=>
WindowStyle="None"
让用户“关闭”窗口,但实际上只是隐藏它 .
在窗口的OnClosing事件中,如果已经可见则隐藏窗口:
每次执行后台线程时,重新显示后台UI窗口:
终止程序执行时,请确保所有窗口都可以关闭:
XAML代码
应该管用
Edit - 你的瞬间这个Thread显示了它是如何做到的可以完成,但我不认为Window有一个属性来获得你想要的东西,而不会失去正常的 Headers 栏 .
Edit 2 这Thread显示了一种完成它的方法,但您必须将自己的样式应用于系统菜单,它会显示如何执行此操作的方式 .
转到窗口属性集
你不会得到关闭按钮......
尝试将Closing事件添加到窗口 . 将此代码添加到事件处理程序 .
这将阻止窗口关闭 . 这与隐藏关闭按钮具有相同的效果 .
使用此,从https://stephenhaunts.com/2014/09/25/remove-the-close-button-from-a-wpf-window修改:
经过多次寻找答案后,我找到了这个简单的解决方案,我将在这里分享,希望能帮助别人 .
我设置
WindowStyle=0x10000000
.这将为Window Style设置
WS_VISIBLE (0x10000000)
和WS_OVERLAPPED (0x0)
值 . "Overlapped"是显示 Headers 栏和窗口边框的必要值 . 通过从样式值中删除WS_MINIMIZEBOX (0x20000)
,WS_MAXIMIZEBOX (0x10000)
和WS_SYSMENU (0x80000)
值, Headers 栏中的所有按钮都被删除,包括“关闭”按钮 .如其他答案中所述,您可以使用
WindowStyle="None"
完全删除 Headers 栏 .并且,正如对其他答案的评论中所述,这可以防止窗口被拖动,因此很难将窗口从其初始位置移开 .
但是,您可以通过在Window的Code Behind文件中向构造函数添加一行代码来克服这个问题:
或者,如果您更喜欢Lambda语法:
这使得整个Window可以拖动 . 窗口中存在的任何交互式控件(如按钮)仍将正常工作,不会充当窗口的拖动句柄 .
使用
WindowStyle="SingleBorderWindow"
,这将隐藏WPF窗口中的最大和最小按钮 .如果只需要禁止用户关闭窗口,这是一个简单的解决方案 .
XAML代码:
IsCloseButtonEnabled="False"