WinUI 3: How to Set Minimum Window Size (Desktop)


Setting a minimum window size in WinUI 3 desktop applications isn't as straightforward as you might expect. Unlike WPF or Windows Forms, there's no simple MinWidth/MinHeight property on the Window class. However, we can achieve this by intercepting Windows Messages using window subclassing.
Setup
First, you'll need to add the CsWin32 NuGet package to your project:
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.49-beta" />
Then create a file called NativeMethods.txt
in your project root and add these required Win32 APIs:
SetWindowSubclass
DefSubclassProc
GetDpiForWindow
MINMAXINFO
After creating NativeMethods.txt
, ensure its build action is set to "C# analyzer additional file":
- Right-click the file > Properties > Build Action > Select "C# analyzer additional file".
This file tells the CsWin32 source generator which Win32 APIs to generate code for.
The Solution
Here is a helper class that will handle the window size constraints:
public class WindowsSubclass
{
private const int WM_GETMINMAXINFO = 0x0024;
private readonly Windows.Win32.UI.Shell.SUBCLASSPROC _subclassProc;
private readonly Windows.Win32.Foundation.HWND _hwnd;
private readonly uint _id;
/// <summary>
/// Initializes a new instance of the <see cref="WindowMinSizeSubclass"/> class.
/// </summary>
/// <param name="hwnd">The handle to the window.</param>
/// <param name="id">The subclass ID, could be any number.</param>
/// <param name="minSize">The minimum size of the window.</param>
/// <exception cref="InvalidOperationException">Thrown when setting the window subclass fails.</exception>
public WindowsSubclass(IntPtr hwnd, uint id, Size minSize)
{
_hwnd = (Windows.Win32.Foundation.HWND)hwnd;
_id = id;
MinSize = minSize;
_subclassProc = new Windows.Win32.UI.Shell.SUBCLASSPROC(WindowSubclassProc);
var result = Windows.Win32.PInvoke.SetWindowSubclass(_hwnd, _subclassProc, _id, 0);
if (result.Value == 0)
{
throw new InvalidOperationException("Failed to set window subclass");
}
}
public Size MinSize { get; }
private Windows.Win32.Foundation.LRESULT WindowSubclassProc(Windows.Win32.Foundation.HWND hWnd, uint uMsg,
Windows.Win32.Foundation.WPARAM wParam, Windows.Win32.Foundation.LPARAM lParam,
nuint uIdSubclass, nuint dwRefData)
{
switch (uMsg)
{
case WM_GETMINMAXINFO:
// Windows sends this message to query size constraints.
// ptMinTrackSize defines the smallest draggable window size.
// We adjust it based on DPI scaling.
var dpi = Windows.Win32.PInvoke.GetDpiForWindow(hWnd);
float scalingFactor = (float)dpi / 96;
var minMaxInfo = Marshal.PtrToStructure<Windows.Win32.UI.WindowsAndMessaging.MINMAXINFO>(lParam);
minMaxInfo.ptMinTrackSize.X = (int)(MinSize.Width * scalingFactor);
minMaxInfo.ptMinTrackSize.Y = (int)(MinSize.Height * scalingFactor);
Marshal.StructureToPtr(minMaxInfo, lParam, true);
return new Windows.Win32.Foundation.LRESULT(0);
}
return Windows.Win32.PInvoke.DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
}
How to Use It
To use this in your WinUI 3 window, add this code to your window's constructor:
public MainWindow()
{
this.InitializeComponent();
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
_ = new WindowsSubclass(hwnd, 69, new Size(500, 300)); // Sets minimum size to 500x300
}
How It Works
The solution works by intercepting the WM_GETMINMAXINFO Windows Message, which Windows sends to a window when it needs to know its size constraints. We use window subclassing to add our own procedure that handles this message.
Key points about the implementation:
CsWin32 generates strongly-typed wrappers for the Win32 APIs we specified in NativeMethods.txt
The class keeps a reference to the subclass procedure delegate to prevent it from being garbage collected
It properly handles DPI scaling to ensure consistent sizing across different display scales
All other window messages are passed through to the default handler
The subclass is automatically cleaned up when the window is destroyed
Important Notes
The minimum size is specified in logical pixels and will be properly scaled based on the display's DPI
The window subclassing is automatically cleaned up when the window is destroyed
This solution works only for desktop applications, not for UWP or other platforms
Make sure NativeMethods.txt is included in your project and the file properties are set to "C# analyzer additional file"
Troubleshooting
Some problems you could encounter and how to fix them:
Subclass ID Conflicts: Ensure unique IDs if subclassing multiple windows.
DPI Scaling Issues: Verify
GetDpiForWindow
returns valid values (e.g., not zero).Garbage Collection: The
SUBCLASSPROC
delegate must remain referenced (already handled in the helper class).
Now you can set minimum window sizes in your WinUI 3 desktop applications while maintaining proper Windows integration and DPI awareness.
Subscribe to my newsletter
Read articles from Pavel Osadchuk directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Pavel Osadchuk
Pavel Osadchuk
I'm a .NET Developer working on MS Stack for more than 12 years already. I worked with most dotnet-based technologies, from WinForms to Azure Functions. In my time, I won a dozen hackathons, launched a couple of startups, failed them, and am now working as a lead .NET developer in an enterprise company.