Lo siguiente es sobre deshabilitar los botones de cerrar y Maximizar / Minimizar, no realidad elimina los botones (¡pero elimina los elementos del menú!). Los botones en la barra de título se dibujan en un estado deshabilitado / atenuado. (No estoy listo para asumir toda la funcionalidad yo mismo ^^)
Esto es ligeramente diferente de la solución de Virgoss en que elimina los elementos del menú (y el separador final, si es necesario) en lugar de simplemente deshabilitarlos. Se diferencia de la solución Joe Whites, ya que no deshabilita todo el menú del sistema y, en mi caso, puedo mantener el botón y el icono Minimizar.
El código siguiente también admite la desactivación de los botones Maximizar / Minimizar ya que, a diferencia del botón Cerrar, eliminar las entradas del menú no hace que el sistema haga que los botones estén "desactivados", aunque eliminar las entradas del menú sí desactiva la funcionalidad de los botones.
Esto funciona para mi. 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);
}
}
}
Uso: Esto debe hacerse DESPUÉS de que se inicialice la fuente. Un buen lugar es usar el evento SourceInitialized de la ventana:
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
}
Para deshabilitar la funcionalidad Alt + F4, el método fácil es simplemente conectar el evento de Cancelación y usar establecer un indicador para cuando realmente desea cerrar la ventana.