Skip to content

Child window exception fix #2007

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

AndrewKeepCoding
Copy link
Contributor

@AndrewKeepCoding AndrewKeepCoding commented Aug 1, 2025

Description

This PR replaces Win32WindowHelper with OverlappedPresenter to set window max size.

Motivation and Context

The Win32WindowHelper relied on static fields, which led to app crashes when handling multiple windows. The OverlappedPresenter, as demonstrated in the AppWindow sample page, is the recommended approach to setting window size constraints.

Note: This PR supersedes #2005, which was closed due to an incomplete approach.
Fixes: #2003

How Has This Been Tested?

Ran the app and verified the changes.

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

@niels9001 niels9001 requested review from niels9001 and Copilot August 1, 2025 06:19
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a crash issue when handling multiple windows by replacing the Win32WindowHelper with OverlappedPresenter for setting window minimum size constraints. The change eliminates the use of static fields that caused the crashes and adopts the recommended WinUI approach.

Key Changes

  • Replaced Win32WindowHelper with OverlappedPresenter for window size constraints
  • Removed the entire Win32WindowHelper class and its Win32 API dependencies
  • Updated window minimum size setting in both MainWindow and TabViewWindowingSamplePage

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
TabViewWindowingSamplePage.xaml.cs Updated to use OverlappedPresenter instead of Win32WindowHelper for setting minimum window size
MainWindow.xaml.cs Replaced Win32WindowHelper usage with OverlappedPresenter for window constraints
Win32WindowHelper.cs Completely removed the helper class that was causing crashes with multiple windows

Comment on lines +27 to +28
(window.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumWidth = 640;
(window.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumHeight = 500;
Copy link
Preview

Copilot AI Aug 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cast to OverlappedPresenter could fail if the presenter is not of that type, potentially causing a NullReferenceException. Consider adding a null check or type check before setting the properties.

Suggested change
(window.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumWidth = 640;
(window.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumHeight = 500;
if (window.AppWindow.Presenter is OverlappedPresenter overlappedPresenter)
{
overlappedPresenter.PreferredMinimumWidth = 640;
overlappedPresenter.PreferredMinimumHeight = 500;
}
else
{
// Handle the case where the presenter is not an OverlappedPresenter
System.Diagnostics.Debug.WriteLine("The presenter is not of type OverlappedPresenter.");
}

Copilot uses AI. Check for mistakes.

Comment on lines +27 to +28
(window.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumWidth = 640;
(window.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumHeight = 500;
Copy link
Preview

Copilot AI Aug 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cast to OverlappedPresenter could fail if the presenter is not of that type, potentially causing a NullReferenceException. Consider adding a null check or type check before setting the properties.

Suggested change
(window.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumWidth = 640;
(window.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumHeight = 500;
var presenter = window.AppWindow.Presenter as OverlappedPresenter;
if (presenter != null)
{
presenter.PreferredMinimumWidth = 640;
presenter.PreferredMinimumHeight = 500;
}

Copilot uses AI. Check for mistakes.

Comment on lines +64 to +65
(this.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumWidth = 640;
(this.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumHeight = 500;
Copy link
Preview

Copilot AI Aug 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cast to OverlappedPresenter could fail if the presenter is not of that type, potentially causing a NullReferenceException. Consider adding a null check or type check before setting the properties.

Suggested change
(this.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumWidth = 640;
(this.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumHeight = 500;
var overlappedPresenter = this.AppWindow.Presenter as OverlappedPresenter;
if (overlappedPresenter != null)
{
overlappedPresenter.PreferredMinimumWidth = 640;
overlappedPresenter.PreferredMinimumHeight = 500;
}

Copilot uses AI. Check for mistakes.

Comment on lines +64 to +65
(this.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumWidth = 640;
(this.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumHeight = 500;
Copy link
Preview

Copilot AI Aug 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cast to OverlappedPresenter could fail if the presenter is not of that type, potentially causing a NullReferenceException. Consider adding a null check or type check before setting the properties.

Suggested change
(this.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumWidth = 640;
(this.AppWindow.Presenter as OverlappedPresenter).PreferredMinimumHeight = 500;
var presenter = this.AppWindow.Presenter as OverlappedPresenter;
if (presenter != null)
{
presenter.PreferredMinimumWidth = 640;
presenter.PreferredMinimumHeight = 500;
}

Copilot uses AI. Check for mistakes.

@niels9001
Copy link
Contributor

@AndrewKeepCoding I believe these APIs do not take into account DPI by default?

@marcelwgn
Copy link
Contributor

In addition to @niels9001 concern, wouldnt it be easier to have the functions in WindowHelper be and resolve the hwnd by passing in a Window? It doesnt look like we need any of the state the way the functions work right now and keep that little state inside the functions.

@AndrewKeepCoding
Copy link
Contributor Author

@niels9001 @marcelwgn Thanks for the feedback!

Would this work better? We should also consider integrate it to the other helper class WindowHelper.

internal class Win32WindowHelper
{
    private const double DefaultDpi = 96.0f;

    public static void SetWindowMinMaxSize(Window window, POINT? minWindowSize = null, POINT? maxWindowSize = null)
    {
        if (window.AppWindow.Presenter is not OverlappedPresenter presenter)
        {
            System.Diagnostics.Debug.WriteLine("SetWindowMinMaxSize: AppWindow.Presenter is not OverlappedPresenter");
            return;
        }

        var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window);
        var dpi = Win32.GetDpiForWindow(hWnd);

        if (minWindowSize is not null)
        {
            presenter.PreferredMinimumWidth = (int)(minWindowSize.Value.x * dpi / DefaultDpi);
            presenter.PreferredMinimumHeight = (int)(minWindowSize.Value.y * dpi / DefaultDpi);
        }

        if (maxWindowSize is not null)
        {
            presenter.PreferredMaximumWidth = (int)(maxWindowSize.Value.x * dpi / DefaultDpi);
            presenter.PreferredMaximumHeight = (int)(maxWindowSize.Value.y * dpi / DefaultDpi);
        }
    }

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

Copy link
Contributor

@Zakariathr22 Zakariathr22 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @AndrewKeepCoding ,

I previously worked on DPI awareness using PreferredMinimumWidth and PreferredMinimumHeight, and I believe I have a solid workaround. I didn’t implement it earlier because it seemed too complex at the time, and the current implementation appeared sufficient. However, since it’s now breaking the app, I suggest we consider this workaround.

You can find the related code here: DPI-Aware

From my findings, we should focus on two key areas:

  • Making window size constraints DPI-aware using GetDpiForWindow and applying scaleFactor = dpi / 96.0 (see this PR: Zakariathr22/DPI-Aware#1)
  • Handling dynamic DPI changes by intercepting the WM_DPICHANGED message (see this PR: Zakariathr22/DPI-Aware#2)

I'd also like to know what @niels9001 and @marcelwgn think about this.

@AndrewKeepCoding
Copy link
Contributor Author

I'm starting to think that we should also consider simplifying this "child window sample" and just use a plain Window instead of the MainWindow. And if needed, we can add a separate sample to demonstrate dpi-aware windows.
Also, we can also implement dpi-aware with XamlRoot.Changed. IIRC, it gets fired when the scale factor changes.

@marcelwgn
Copy link
Contributor

I think your proposed approaches are fine (grouping them since they are virtually doing the same) @AndrewKeepCoding and @Zakariathr22. Regarding the DPI changes and grabbing the DPI factor, I think we should use the XAMLRoot.RasterizationScale property so we dont have to rely on calling into Win32 APIs. Since the XAMLRoot also comes with a changed events arg, we would also be able to deal with the DPI changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Creating a child window on Windowing page throws System.ExecutionEngineException
4 participants