-
Notifications
You must be signed in to change notification settings - Fork 666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: How to set minimum window size (Desktop) #2945
Comments
does this do iApplicationView.GetForCurrentView().SetPreferredMinSize()? It seems like the default window size for the desktop skew might be strang @MikeHillberg do you know if we have the ability to set a more reasonable default? |
@marb2000 for the window APIs. |
Desktop WinUI 3 don't have MinSize or MaxSize properties yet on a Window class, and it's not trivial to do this for a developer. The way to do it in Win32 is listening the message WM_GETMINMAXINFO, but you need first to get access to the procedure that get the messages in Windows (the WindowProc). Unfortunately, WinUI 3 Desktop doesn't expose it, so the solution is Subclassing. Subclassing allows to intercept and process messages sent to window before the WindowProc. I wrote this code snippet that could help you to understand what to do: using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using WinRT;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
SubClassing();
}
private delegate IntPtr WinProc(IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam);
private WinProc newWndProc = null;
private IntPtr oldWndProc = IntPtr.Zero;
[DllImport("user32")]
private static extern IntPtr SetWindowLong(IntPtr hWnd, PInvoke.User32.WindowLongIndexFlags nIndex, WinProc newProc);
[DllImport("user32.dll")]
static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam);
private void SubClassing()
{
//Get the Window's HWND
var hwnd = this.As<IWindowNative>().WindowHandle;
newWndProc = new WinProc(NewWindowProc);
oldWndProc = SetWindowLong(hwnd, PInvoke.User32.WindowLongIndexFlags.GWL_WNDPROC, newWndProc);
}
int MinWidth = 800;
int MinHeight = 600;
[StructLayout(LayoutKind.Sequential)]
struct MINMAXINFO
{
public PInvoke.POINT ptReserved;
public PInvoke.POINT ptMaxSize;
public PInvoke.POINT ptMaxPosition;
public PInvoke.POINT ptMinTrackSize;
public PInvoke.POINT ptMaxTrackSize;
}
private IntPtr NewWindowProc(IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam)
{
switch (Msg)
{
case PInvoke.User32.WindowMessage.WM_GETMINMAXINFO:
var dpi = PInvoke.User32.GetDpiForWindow(hWnd);
float scalingFactor = (float)dpi / 96;
MINMAXINFO minMaxInfo = Marshal.PtrToStructure<MINMAXINFO>(lParam);
minMaxInfo.ptMinTrackSize.x = (int)(MinWidth * scalingFactor);
minMaxInfo.ptMinTrackSize.y = (int)(MinHeight * scalingFactor);
Marshal.StructureToPtr(minMaxInfo, lParam, true);
break;
}
return CallWindowProc(oldWndProc, hWnd, Msg, wParam, lParam);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("EECDBF0E-BAE9-4CB6-A68E-9598E1CB57BB")]
internal interface IWindowNative
{
IntPtr WindowHandle { get; }
}
} |
SetWindowLong only accepts 32 bits values which can result in unexpected truncations (therefore a crash due to a bad function pointer), SetWindowLongPtr should be used. Subclassing via SetWindowLongPtr is also not recommended by the Microsoft docs due to various disadvantages, but the alternative subclassing APIs only work with common controls 6: https://docs.microsoft.com/en-us/windows/win32/controls/subclassing-overview#disadvantages-of-the-old-subclassing-approach Why not add a protected overridable function to the Window class which is similar to WinForms' WndProc (with a respective Message struct which mirrors the native MSG struct)? It would be much simpler for everyone involved. |
Hello - I'm using Windows App SDK 1.0. Is there no property that can be set, either at the App or Window level? Thank you. |
Hello @marb2000 - I tried the PInvoke.User32.WindowMessage.WM_GETMINMAXINFO as described above, and NewWindowProc is never receiving any messages. Again, I am running a C# WinUI3 desktop app. Do you have any advice at this point? Thank you. |
@rcohn use WinUIEx and its WindowEx Window class for now. It adds the needed min sizes as well as the window messaging stuff https://dotmorten.github.io/WinUIEx/concepts/WindowEx.html |
Just use SetWindowSubclass |
Hi - Thanks very much to all who replied. It was really helpful. |
This reminds me Turbo Pascal 6.0 |
Is there a plan to add minWidth/minHeight/min window size any time soon? |
@bhuvana-01m this is the approach the WinUIEx helper extensions take. I’d suggest you look at using that which will allow you to set min size in a couple lines of code , until WinUI itself has this implemented |
What does not work ? |
Hi - I used SetWindowSubclass and that works fine (see my code snippet far above). It's unfortunate that we are still having to go through so many hoops with WinUI 3 (and I'm still stumbling through those hoops), but it's fortunate that we have all you experts that read and advise on this forum. Many, many thanks for your help. Best regards. |
@sylveon Thank you so much for pointing out the issue with With this change @marb2000 code works correctly for me. - [DllImport("user32")]
- private static extern IntPtr SetWindowLong(
- IntPtr hWnd,
- PInvoke.User32.WindowLongIndexFlags nIndex,
- WinProc? newProc
- );
+ [DllImport("user32")]
+ private static extern IntPtr SetWindowLongPtr(
+ IntPtr hWnd,
+ PInvoke.User32.WindowLongIndexFlags nIndex,
+ WinProc? newProc
+ ); |
To follow-up my previous comment, the solution of using In the Win32 world, // winuser.h
#ifdef _WIN64
LONG_PTR WINAPI SetWindowLongPtrA(HWND,int,LONG_PTR);
LONG_PTR WINAPI SetWindowLongPtrW(HWND,int,LONG_PTR);
#else
#define SetWindowLongPtrA SetWindowLongA
#define SetWindowLongPtrW SetWindowLongW
#endif But in our C# world, we can't rely on macros, and PInvoke doesn't automatically handle the mapping for us. So, if we call My solution here is a small It basically mimics the behavior of the Here's the code snippet: // Note: this DllImport can now be commented or removed.
//
//[DllImport("user32")]
//private static extern IntPtr SetWindowLongPtr(
// IntPtr hWnd,
// PInvoke.User32.WindowLongIndexFlags nIndex,
// WinProc? newProc
//);
private void SubClassing()
{
var hwnd = this.As<IWindowNative>().WindowHandle;
if (hwnd == IntPtr.Zero)
{
int error = Marshal.GetLastWin32Error();
throw new InvalidOperationException($"Failed to get window handler: error code {error}");
}
newWndProc = new(NewWindowProc);
// Here we use the NativeMethods class 👇
oldWndProc = NativeMethods.SetWindowLong(hwnd, PInvoke.User32.WindowLongIndexFlags.GWL_WNDPROC, newWndProc);
if (oldWndProc == IntPtr.Zero)
{
int error = Marshal.GetLastWin32Error();
throw new InvalidOperationException($"Failed to set GWL_WNDPROC: error code {error}");
}
}
public static class NativeMethods
{
// We have to handle the 32-bit and 64-bit functions separately.
// 'SetWindowLongPtr' is the 64-bit version of 'SetWindowLong', and isn't available in user32.dll for 32-bit processes.
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern IntPtr SetWindowLong32(IntPtr hWnd, PInvoke.User32.WindowLongIndexFlags nIndex, WinProc newProc);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
private static extern IntPtr SetWindowLong64(IntPtr hWnd, PInvoke.User32.WindowLongIndexFlags nIndex, WinProc newProc);
// This does the selection for us, based on the process architecture.
public static IntPtr SetWindowLong(IntPtr hWnd, PInvoke.User32.WindowLongIndexFlags nIndex, WinProc newProc)
{
if (IntPtr.Size == 4) // 32-bit process
{
return SetWindowLong32(hWnd, nIndex, newProc);
}
else // 64-bit process
{
return SetWindowLong64(hWnd, nIndex, newProc);
}
}
}
// ... the rest is similar to what @marb2000 suggested in his solution That should provide support for for 32 and 64-bit systems. |
Hey everyone, I've already submitted a proposal so that a workaround no longer needs to be developed. Issue: #7296 |
WinUI 3 Preview 2 Desktop C#
Sorry, but how do I set the minimum window size? Currently the app can be shrunk almost entirely.
The text was updated successfully, but these errors were encountered: