Skip to content

Commit 9134fbe

Browse files
marcelwgnniels9001
andauthored
Add minimum window sizes (#1404)
## Description Adding a minimum size for the apps window and also the TabView tearout sample for better user experience. ## Motivation and Context Closes #1181 Co-authored-by: Niels Laute <[email protected]>
1 parent 9127f51 commit 9134fbe

File tree

5 files changed

+145
-21
lines changed

5 files changed

+145
-21
lines changed

WinUIGallery/App.xaml.cs

+16-8
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
using Windows.ApplicationModel.Activation;
2222
using WinUIGallery.DesktopWap.DataModel;
2323
using WASDK = Microsoft.WindowsAppSDK;
24-
24+
using Microsoft.Windows.AppLifecycle;
25+
using System.IO;
26+
using WinUIGallery.Helper;
27+
2528
namespace AppUIBasics
2629
{
2730
/// <summary>
@@ -30,23 +33,24 @@ namespace AppUIBasics
3033
sealed partial class App : Application
3134
{
3235
private static Window startupWindow;
36+
private static Win32WindowHelper win32WindowHelper;
3337

3438
public static string WinAppSdkDetails
3539
{
3640
// TODO: restore patch number and version tag when WinAppSDK supports them both
3741
get => string.Format("Windows App SDK {0}.{1}",
3842
WASDK.Release.Major, WASDK.Release.Minor);
39-
}
40-
43+
}
44+
4145
public static string WinAppSdkRuntimeDetails
4246
{
4347
get
44-
{
48+
{
4549
// Retrieve Windows App Runtime version info dynamically
46-
var windowsAppRuntimeVersion =
47-
from module in Process.GetCurrentProcess().Modules.OfType<ProcessModule>()
48-
where module.FileName.EndsWith("Microsoft.WindowsAppRuntime.Insights.Resource.dll")
49-
select FileVersionInfo.GetVersionInfo(module.FileName);
50+
var windowsAppRuntimeVersion =
51+
from module in Process.GetCurrentProcess().Modules.OfType<ProcessModule>()
52+
where module.FileName.EndsWith("Microsoft.WindowsAppRuntime.Insights.Resource.dll")
53+
select FileVersionInfo.GetVersionInfo(module.FileName);
5054
return WinAppSdkDetails + ", Windows App Runtime " + windowsAppRuntimeVersion.First().FileVersion;
5155
}
5256
}
@@ -107,6 +111,10 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar
107111

108112
startupWindow = WindowHelper.CreateWindow();
109113
startupWindow.ExtendsContentIntoTitleBar = true;
114+
115+
win32WindowHelper = new Win32WindowHelper(startupWindow);
116+
win32WindowHelper.SetWindowMinMaxSize(new Win32WindowHelper.POINT() { x = 500, y = 500 });
117+
110118
#if DEBUG
111119
if (System.Diagnostics.Debugger.IsAttached)
112120
{

WinUIGallery/Common/Win32.cs

+25
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,37 @@ internal static class Win32
1717
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
1818
public static extern IntPtr GetModuleHandle(IntPtr moduleName);
1919

20+
[DllImport("User32.dll")]
21+
internal static extern int GetDpiForWindow(IntPtr hwnd);
22+
23+
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
24+
internal static extern int SetWindowLong32(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc);
25+
26+
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
27+
internal static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc);
28+
29+
[DllImport("user32.dll")]
30+
internal static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, WindowMessage Msg, IntPtr wParam, IntPtr lParam);
31+
2032
public const int WM_ACTIVATE = 0x0006;
2133
public const int WA_ACTIVE = 0x01;
2234
public const int WA_INACTIVE = 0x00;
2335

2436
public const int WM_SETICON = 0x0080;
2537
public const int ICON_SMALL = 0;
2638
public const int ICON_BIG = 1;
39+
40+
internal delegate IntPtr WinProc(IntPtr hWnd, WindowMessage Msg, IntPtr wParam, IntPtr lParam);
41+
42+
[Flags]
43+
internal enum WindowLongIndexFlags : int
44+
{
45+
GWL_WNDPROC = -4,
46+
}
47+
48+
internal enum WindowMessage : int
49+
{
50+
WM_GETMINMAXINFO = 0x0024,
51+
}
2752
}
2853
}

WinUIGallery/ControlPages/TabViewPage.xaml.cs

+6-4
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,14 @@ private void TabCloseButtonOverlayModeComboBox_SelectionChanged(object sender, S
245245

246246
private void TabViewWindowingButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
247247
{
248+
var tabViewSample = new TabViewWindowingSamplePage();
249+
248250
var newWindow = WindowHelper.CreateWindow();
251+
newWindow.ExtendsContentIntoTitleBar = true;
252+
newWindow.Content = tabViewSample;
253+
tabViewSample.LoadDemoData();
254+
tabViewSample.SetupWindowMinSize(newWindow);
249255

250-
Frame frame = new Frame();
251-
frame.RequestedTheme = ThemeHelper.RootTheme;
252-
frame.Navigate(typeof(TabViewWindowingSamplePage), null);
253-
newWindow.Content = frame;
254256
newWindow.Activate();
255257
}
256258
}
+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System;
2+
using System.Runtime.InteropServices;
3+
using Microsoft.UI.Xaml;
4+
using static AppUIBasics.Win32;
5+
6+
namespace WinUIGallery.Helper
7+
{
8+
internal class Win32WindowHelper
9+
{
10+
private static WinProc newWndProc = null;
11+
private static nint oldWndProc = nint.Zero;
12+
13+
private POINT? minWindowSize = null;
14+
private POINT? maxWindowSize = null;
15+
16+
private readonly Window window;
17+
18+
public Win32WindowHelper(Window window)
19+
{
20+
this.window = window;
21+
}
22+
23+
public void SetWindowMinMaxSize(POINT? minWindowSize = null, POINT? maxWindowSize = null)
24+
{
25+
this.minWindowSize = minWindowSize;
26+
this.maxWindowSize = maxWindowSize;
27+
28+
var hwnd = GetWindowHandleForCurrentWindow(window);
29+
30+
newWndProc = new WinProc(WndProc);
31+
oldWndProc = SetWindowLongPtr(hwnd, WindowLongIndexFlags.GWL_WNDPROC, newWndProc);
32+
}
33+
34+
private static nint GetWindowHandleForCurrentWindow(object target) =>
35+
WinRT.Interop.WindowNative.GetWindowHandle(target);
36+
37+
private nint WndProc(nint hWnd, WindowMessage Msg, nint wParam, nint lParam)
38+
{
39+
switch (Msg)
40+
{
41+
case WindowMessage.WM_GETMINMAXINFO:
42+
var dpi = GetDpiForWindow(hWnd);
43+
var scalingFactor = (float)dpi / 96;
44+
45+
var minMaxInfo = Marshal.PtrToStructure<MINMAXINFO>(lParam);
46+
if (minWindowSize != null)
47+
{
48+
minMaxInfo.ptMinTrackSize.x = (int)(minWindowSize.Value.x * scalingFactor);
49+
minMaxInfo.ptMinTrackSize.y = (int)(minWindowSize.Value.y * scalingFactor);
50+
}
51+
if (maxWindowSize != null)
52+
{
53+
minMaxInfo.ptMaxTrackSize.x = (int)(maxWindowSize.Value.x * scalingFactor);
54+
minMaxInfo.ptMaxTrackSize.y = (int)(minWindowSize.Value.y * scalingFactor);
55+
}
56+
57+
Marshal.StructureToPtr(minMaxInfo, lParam, true);
58+
break;
59+
60+
}
61+
return CallWindowProc(oldWndProc, hWnd, Msg, wParam, lParam);
62+
}
63+
64+
private nint SetWindowLongPtr(nint hWnd, WindowLongIndexFlags nIndex, WinProc newProc)
65+
{
66+
if (nint.Size == 8)
67+
return SetWindowLongPtr64(hWnd, nIndex, newProc);
68+
else
69+
return new nint(SetWindowLong32(hWnd, nIndex, newProc));
70+
}
71+
72+
internal struct POINT
73+
{
74+
public int x;
75+
public int y;
76+
}
77+
78+
[StructLayout(LayoutKind.Sequential)]
79+
private struct MINMAXINFO
80+
{
81+
public POINT ptReserved;
82+
public POINT ptMaxSize;
83+
public POINT ptMaxPosition;
84+
public POINT ptMinTrackSize;
85+
public POINT ptMaxTrackSize;
86+
}
87+
}
88+
}

WinUIGallery/TabViewPages/TabViewWindowingSamplePage.xaml.cs

+10-9
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
using System.Threading.Tasks;
1515
using Windows.System;
1616
using DispatcherQueueHandler = Microsoft.UI.Dispatching.DispatcherQueueHandler;
17+
using WinUIGallery.Helper;
1718

1819
namespace AppUIBasics.TabViewPages
1920
{
2021
public sealed partial class TabViewWindowingSamplePage : Page
2122
{
2223
private const string DataIdentifier = "MyTabItem";
24+
private Win32WindowHelper win32WindowHelper;
2325
public TabViewWindowingSamplePage()
2426
{
2527
this.InitializeComponent();
@@ -29,6 +31,12 @@ public TabViewWindowingSamplePage()
2931
Loaded += TabViewWindowingSamplePage_Loaded;
3032
}
3133

34+
public void SetupWindowMinSize(Window window)
35+
{
36+
win32WindowHelper = new Win32WindowHelper(window);
37+
win32WindowHelper.SetWindowMinMaxSize(new Win32WindowHelper.POINT() { x = 500, y = 300 });
38+
}
39+
3240
private void TabViewWindowingSamplePage_Loaded(object sender, RoutedEventArgs e)
3341
{
3442
var currentWindow = WindowHelper.GetWindowForElement(this);
@@ -57,16 +65,8 @@ private void Tabs_TabItemsChanged(TabView sender, Windows.Foundation.Collections
5765
}
5866
}
5967

60-
protected override void OnNavigatedTo(NavigationEventArgs e)
61-
{
62-
base.OnNavigatedTo(e);
63-
64-
SetupWindow();
65-
}
66-
67-
void SetupWindow()
68+
public void LoadDemoData()
6869
{
69-
7070
// Main Window -- add some default items
7171
for (int i = 0; i < 3; i++)
7272
{
@@ -92,6 +92,7 @@ private void Tabs_TabDroppedOutside(TabView sender, TabViewTabDroppedOutsideEven
9292
var newWindow = WindowHelper.CreateWindow();
9393
newWindow.ExtendsContentIntoTitleBar = true;
9494
newWindow.Content = newPage;
95+
newPage.SetupWindowMinSize(newWindow);
9596

9697
newWindow.Activate();
9798
}

0 commit comments

Comments
 (0)