Skip to content
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

Add minimum window sizes #1404

Merged
merged 3 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions WinUIGallery/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
using Windows.ApplicationModel.Activation;
using WinUIGallery.DesktopWap.DataModel;
using WASDK = Microsoft.WindowsAppSDK;

using Microsoft.Windows.AppLifecycle;
using System.IO;
using WinUIGallery.Helper;

namespace AppUIBasics
{
/// <summary>
Expand All @@ -30,23 +33,24 @@ namespace AppUIBasics
sealed partial class App : Application
{
private static Window startupWindow;
private static Win32WindowHelper win32WindowHelper;

public static string WinAppSdkDetails
{
// TODO: restore patch number and version tag when WinAppSDK supports them both
get => string.Format("Windows App SDK {0}.{1}",
WASDK.Release.Major, WASDK.Release.Minor);
}
}

public static string WinAppSdkRuntimeDetails
{
get
{
{
// Retrieve Windows App Runtime version info dynamically
var windowsAppRuntimeVersion =
from module in Process.GetCurrentProcess().Modules.OfType<ProcessModule>()
where module.FileName.EndsWith("Microsoft.WindowsAppRuntime.Insights.Resource.dll")
select FileVersionInfo.GetVersionInfo(module.FileName);
var windowsAppRuntimeVersion =
from module in Process.GetCurrentProcess().Modules.OfType<ProcessModule>()
where module.FileName.EndsWith("Microsoft.WindowsAppRuntime.Insights.Resource.dll")
select FileVersionInfo.GetVersionInfo(module.FileName);
return WinAppSdkDetails + ", Windows App Runtime " + windowsAppRuntimeVersion.First().FileVersion;
}
}
Expand Down Expand Up @@ -107,6 +111,10 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar

startupWindow = WindowHelper.CreateWindow();
startupWindow.ExtendsContentIntoTitleBar = true;

win32WindowHelper = new Win32WindowHelper(startupWindow);
win32WindowHelper.SetWindowMinMaxSize(new Win32WindowHelper.POINT() { x = 500, y = 500 });

#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
Expand Down
25 changes: 25 additions & 0 deletions WinUIGallery/Common/Win32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,37 @@ internal static class Win32
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(IntPtr moduleName);

[DllImport("User32.dll")]
internal static extern int GetDpiForWindow(IntPtr hwnd);

[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
internal static extern int SetWindowLong32(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc);

[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
internal static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc);

[DllImport("user32.dll")]
internal static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, WindowMessage Msg, IntPtr wParam, IntPtr lParam);

public const int WM_ACTIVATE = 0x0006;
public const int WA_ACTIVE = 0x01;
public const int WA_INACTIVE = 0x00;

public const int WM_SETICON = 0x0080;
public const int ICON_SMALL = 0;
public const int ICON_BIG = 1;

internal delegate IntPtr WinProc(IntPtr hWnd, WindowMessage Msg, IntPtr wParam, IntPtr lParam);

[Flags]
internal enum WindowLongIndexFlags : int
{
GWL_WNDPROC = -4,
}

internal enum WindowMessage : int
{
WM_GETMINMAXINFO = 0x0024,
}
}
}
10 changes: 6 additions & 4 deletions WinUIGallery/ControlPages/TabViewPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,14 @@ private void TabCloseButtonOverlayModeComboBox_SelectionChanged(object sender, S

private void TabViewWindowingButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
var tabViewSample = new TabViewWindowingSamplePage();

var newWindow = WindowHelper.CreateWindow();
newWindow.ExtendsContentIntoTitleBar = true;
newWindow.Content = tabViewSample;
tabViewSample.LoadDemoData();
tabViewSample.SetupWindowMinSize(newWindow);

Frame frame = new Frame();
frame.RequestedTheme = ThemeHelper.RootTheme;
frame.Navigate(typeof(TabViewWindowingSamplePage), null);
newWindow.Content = frame;
newWindow.Activate();
}
}
Expand Down
88 changes: 88 additions & 0 deletions WinUIGallery/Helper/Win32WindowHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.UI.Xaml;
using static AppUIBasics.Win32;

namespace WinUIGallery.Helper
{
internal class Win32WindowHelper
{
private static WinProc newWndProc = null;
private static nint oldWndProc = nint.Zero;

private POINT? minWindowSize = null;
private POINT? maxWindowSize = null;

private readonly Window window;

public Win32WindowHelper(Window window)
{
this.window = window;
}

public void SetWindowMinMaxSize(POINT? minWindowSize = null, POINT? maxWindowSize = null)
{
this.minWindowSize = minWindowSize;
this.maxWindowSize = maxWindowSize;

var hwnd = GetWindowHandleForCurrentWindow(window);

newWndProc = new WinProc(WndProc);
oldWndProc = SetWindowLongPtr(hwnd, WindowLongIndexFlags.GWL_WNDPROC, newWndProc);
}

private static nint GetWindowHandleForCurrentWindow(object target) =>
WinRT.Interop.WindowNative.GetWindowHandle(target);

private nint WndProc(nint hWnd, WindowMessage Msg, nint wParam, nint lParam)
{
switch (Msg)
{
case WindowMessage.WM_GETMINMAXINFO:
var dpi = GetDpiForWindow(hWnd);
var scalingFactor = (float)dpi / 96;

var minMaxInfo = Marshal.PtrToStructure<MINMAXINFO>(lParam);
if (minWindowSize != null)
{
minMaxInfo.ptMinTrackSize.x = (int)(minWindowSize.Value.x * scalingFactor);
minMaxInfo.ptMinTrackSize.y = (int)(minWindowSize.Value.y * scalingFactor);
}
if (maxWindowSize != null)
{
minMaxInfo.ptMaxTrackSize.x = (int)(maxWindowSize.Value.x * scalingFactor);
minMaxInfo.ptMaxTrackSize.y = (int)(minWindowSize.Value.y * scalingFactor);
}

Marshal.StructureToPtr(minMaxInfo, lParam, true);
break;

}
return CallWindowProc(oldWndProc, hWnd, Msg, wParam, lParam);
}

private nint SetWindowLongPtr(nint hWnd, WindowLongIndexFlags nIndex, WinProc newProc)
{
if (nint.Size == 8)
return SetWindowLongPtr64(hWnd, nIndex, newProc);
else
return new nint(SetWindowLong32(hWnd, nIndex, newProc));
}

internal struct POINT
{
public int x;
public int y;
}

[StructLayout(LayoutKind.Sequential)]
private struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
}
}
}
19 changes: 10 additions & 9 deletions WinUIGallery/TabViewPages/TabViewWindowingSamplePage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
using System.Threading.Tasks;
using Windows.System;
using DispatcherQueueHandler = Microsoft.UI.Dispatching.DispatcherQueueHandler;
using WinUIGallery.Helper;

namespace AppUIBasics.TabViewPages
{
public sealed partial class TabViewWindowingSamplePage : Page
{
private const string DataIdentifier = "MyTabItem";
private Win32WindowHelper win32WindowHelper;
public TabViewWindowingSamplePage()
{
this.InitializeComponent();
Expand All @@ -29,6 +31,12 @@ public TabViewWindowingSamplePage()
Loaded += TabViewWindowingSamplePage_Loaded;
}

public void SetupWindowMinSize(Window window)
{
win32WindowHelper = new Win32WindowHelper(window);
win32WindowHelper.SetWindowMinMaxSize(new Win32WindowHelper.POINT() { x = 500, y = 300 });
}

private void TabViewWindowingSamplePage_Loaded(object sender, RoutedEventArgs e)
{
var currentWindow = WindowHelper.GetWindowForElement(this);
Expand Down Expand Up @@ -57,16 +65,8 @@ private void Tabs_TabItemsChanged(TabView sender, Windows.Foundation.Collections
}
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);

SetupWindow();
}

void SetupWindow()
public void LoadDemoData()
{

// Main Window -- add some default items
for (int i = 0; i < 3; i++)
{
Expand All @@ -92,6 +92,7 @@ private void Tabs_TabDroppedOutside(TabView sender, TabViewTabDroppedOutsideEven
var newWindow = WindowHelper.CreateWindow();
newWindow.ExtendsContentIntoTitleBar = true;
newWindow.Content = newPage;
newPage.SetupWindowMinSize(newWindow);

newWindow.Activate();
}
Expand Down