Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Support Windows 11 Snap Layout in WPF app

Tags:

c#

wpf

wndproc

I want to enable SnapLayout for WPF, because I use Customized Window, according to the documentation, I have to do it myself.

For Win32 apps, make sure you are responding appropriately to WM_NCHITTEST (with a return value of HTMAXBUTTON for the maximize/restore button).

I used the following code

private const int HTMAXBUTTON = 9;
private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wparam,
    IntPtr lparam, ref bool handled)
{
    switch (msg)
    {
        case InteropValues.WM_NCHITTEST:
        try
        {
            int x = lparam.ToInt32() & 0xffff;
            int y = lparam.ToInt32() >> 16;
            var rect = new Rect(_ButtonMax.PointToScreen(
                new Point()),
                new Size(_ButtonMax.Width, _ButtonMax.Height));
            if (rect.Contains(new Point(x, y)))
            {
                handled = true;
            }
            return new IntPtr(HTMAXBUTTON);
        }
        catch (OverflowException)
        {
            handled = true;
        }
        break;
    }
    return IntPtr.Zero;
}

SnapLayout is displayed well But the maximize button does not work and if I click on it, a button will be created next to it. How can I solve this problem?

Image


1 Answers

Try this way:

Write it in code-behind ([].xaml.cs)

Write it in constructor

   this.Loaded += SnapLayoutButton_Loaded;

Next after or before write it:

float scaleFactor;
HwndSource? hwndSource;
private void SnapLayoutButton_Loaded(object sender, RoutedEventArgs e)
{
    PresentationSource source = PresentationSource.FromVisual(this);

    var dpi = 96.0 * source.CompositionTarget.TransformToDevice.M11;

    scaleFactor = (float)(dpi / 96.0);



    hwndSource = PresentationSource.FromVisual(this) as HwndSource;
    if (hwndSource != null)
    {
        hwndSource.AddHook(HwndSourceHook);
    }
}
  


 private const int WM_NCHITTEST = 0x0084;
 private const int WM_NCLBUTTONDOWN = 161;
 private const int HTMAXBUTTON = 9;

 


 private bool IsMouseOverFromHook(IntPtr lparam, FrameworkElement button)
 {
     // Extract mouse coordinates from lparam
     int mouseX = (short)(lparam.ToInt32() & 0xFFFF);
     int mouseY = (short)((lparam.ToInt32() >> 16) & 0xFFFF);

     // Get button's actual dimensions and position
     var buttonPosition = button.PointToScreen(new System.Windows.Point(0, 0));

     // Check if mouse coordinates are within the button bounds using a single return statement
     return mouseX >= buttonPosition.X && mouseX <= buttonPosition.X + button.ActualWidth * scaleFactor && mouseY >= buttonPosition.Y && mouseY <= buttonPosition.Y + button.ActualHeight * scaleFactor;
 }


 static DependencyPropertyKey s_uiElementIsMouseOverPropertyKey =
    (DependencyPropertyKey)typeof(UIElement).GetField("IsMouseOverPropertyKey", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)!.GetValue(null)!;

 static DependencyPropertyKey s_buttonIsPressedPropertyKey =
     (DependencyPropertyKey)typeof(ButtonBase).GetField("IsPressedPropertyKey", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)!.GetValue(null)!;


 private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
  
    if(msg == WM_NCLBUTTONDOWN)
    {
        if (IsMouseOverFromHook(lparam, maximizeButton))
        {
            maximizeButton.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
        }
    }

    if (msg == WM_NCHITTEST)
    {
        if (IsMouseOverFromHook(lparam, maximizeButton))
        {

            maximizeButton.SetValue(s_uiElementIsMouseOverPropertyKey, true);

            handled = true;

            return new IntPtr(HTMAXBUTTON);
        }
        else
        {
            maximizeButton.SetValue(s_uiElementIsMouseOverPropertyKey, false);

            if (maximizeButton is ButtonBase button)
            {
                button.SetValue(s_buttonIsPressedPropertyKey, false);
            }
        }
    }

    return IntPtr.Zero;
}
like image 164
Maksym Shymchenko Avatar answered Sep 08 '25 21:09

Maksym Shymchenko