DEV Community

William Amaya
William Amaya

Posted on

WinUI 3: Bloquear doble clic en el Title Bar

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.

objetivo

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);
    }
}
Enter fullscreen mode Exit fullscreen mode
<?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>
Enter fullscreen mode Exit fullscreen mode

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étodo WndProc, usado como interceptador.
  • _oldWndProc: puntero al procedimiento original de la ventana, para reenviar mensajes.

2. Constantes

  • GWLP_WNDPROC = -4: índice que indica a SetWindowLongPtr que 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);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode
  • 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);
Enter fullscreen mode Exit fullscreen mode
  • 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;
}
Enter fullscreen mode Exit fullscreen mode
  • Intercepta el mensaje de doble clic en la barra de título.
  • Devuelve IntPtr.Zero para 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é SetWindowLongPtrW y no SetWindowLongPtr? → La versión W es Unicode, compatible con C#.
  • ¿Por qué CallWindowProc no necesita EntryPoint? → Porque el nombre coincide exactamente con la función exportada en la DLL.

Conclusión

conclusion

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)