En este artículo comparto cómo interceptar mensajes de ventana en WinUI 3 usando interop con Win32, para bloquear la acción de maximizar cuando el usuario hace doble clic en el title bar.
Objetivo
Evitar que la ventana principal se maximice al hacer doble clic en la barra de título, manteniendo el resto de comportamientos intactos.
Código completo
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using System;
using System.Runtime.InteropServices;
using Windows.Graphics;
using WinRT.Interop;
namespace TestApp.WinUI3
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
private readonly IntPtr _hwnd;
private readonly IntPtr _oldWndProc;
private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
private readonly WndProcDelegate _newWndProc;
private const uint WM_NCLBUTTONDBLCLK = 0x00A3;
private const int GWLP_WNDPROC = -4;
public MainWindow()
{
InitializeComponent();
AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
ExtendsContentIntoTitleBar = true;
SetTitleBar(titleBar);
OverlappedPresenter? presenter = OverlappedPresenter.Create();
presenter.IsResizable = false;
presenter.IsMaximizable = false;
presenter.SetBorderAndTitleBar(true, true);
AppWindow.Resize(new SizeInt32(425, 600));
AppWindow.SetPresenter(presenter);
_hwnd = WindowNative.GetWindowHandle(this);
_newWndProc = new WndProcDelegate(WndProc);
_oldWndProc = SetWindowLongPtr(_hwnd, GWLP_WNDPROC, _newWndProc);
}
private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
if (msg == WM_NCLBUTTONDBLCLK)
{
return IntPtr.Zero;
}
return CallWindowProc(_oldWndProc, hWnd, msg, wParam, lParam);
}
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtrW")]
private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, WndProcDelegate newProc);
[DllImport("user32.dll", EntryPoint = "CallWindowProc")]
private static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
}
}
<?xml version="1.0" encoding="utf-8"?>
<Window x:Class="TestApp.WinUI3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestApp.WinUI3"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="TestApp">
<Window.SystemBackdrop>
<MicaBackdrop />
</Window.SystemBackdrop>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<!-- TitleBar -->
<RowDefinition Height="*" />
<!-- NavigationView -->
</Grid.RowDefinitions>
<TitleBar x:Name="titleBar"
Title="TestApp"
IsBackButtonVisible="False"
IsPaneToggleButtonVisible="false">
<TitleBar.IconSource>
<ImageIconSource ImageSource="/Assets/logo.ico" />
</TitleBar.IconSource>
</TitleBar>
</Grid>
</Window>
Explicación paso a paso
1. Variables principales
-
_hwnd: guarda el handle de la ventana (HWND), identificador único en Win32. -
_newWndProc: referencia a tu métodoWndProc, usado como interceptador. -
_oldWndProc: puntero al procedimiento original de la ventana, para reenviar mensajes.
2. Constantes
-
GWLP_WNDPROC = -4: índice que indica aSetWindowLongPtrque quieres reemplazar el procedimiento de ventana. -
WM_NCLBUTTONDBLCLK = 0x00A3: mensaje que Windows envía al hacer doble clic en la barra de título.
3. Delegado
private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
Define la firma del método que interceptará mensajes. Es el equivalente a un puntero a función seguro en C#.
4. Reemplazo del WndProc
_oldWndProc = SetWindowLongPtr(_hwnd, GWLP_WNDPROC, _newWndProc);
- Cambia el procedimiento de ventana por tu método.
- Guarda el original en
_oldWndProc.
5. Reenvío de mensajes
return CallWindowProc(_oldWndProc, hWnd, msg, wParam, lParam);
- Si no es el mensaje que quieres bloquear, lo reenvías al WndProc original.
- Así mantienes el comportamiento estándar de la ventana.
6. Bloqueo del doble clic
if (msg == WM_NCLBUTTONDBLCLK)
{
return IntPtr.Zero;
}
- Intercepta el mensaje de doble clic en la barra de título.
- Devuelve
IntPtr.Zeropara bloquear la acción de maximizar.
Dudas frecuentes aclaradas
-
¿Qué es
_hwnd? → El identificador nativo de la ventana (HWND). -
¿Qué es un delegado? → Un tipo seguro que representa un puntero a función en C#. Aquí define la firma de
WndProc. -
¿Por qué usar
_oldWndProc? → Para reenviar mensajes al procedimiento original y no romper la ventana. -
¿Qué significa
GWLP_WNDPROC = -4? → Es el índice que indica a Windows que quieres reemplazar el procedimiento de ventana. -
¿Por qué
SetWindowLongPtrWy noSetWindowLongPtr? → La versiónWes Unicode, compatible con C#. -
¿Por qué
CallWindowProcno necesita EntryPoint? → Porque el nombre coincide exactamente con la función exportada en la DLL.
Conclusión
Con este patrón puedes interceptar mensajes nativos en WinUI 3 y modificar comportamientos específicos de la ventana, como bloquear la maximización por doble clic en el title bar.


Top comments (0)