UWP AppWindow Get correct monitor/Display region

559 Views Asked by At

Idea is very simple open one or more windows in the app, save their size, position and monitor placement when closing the app, and then when they are opened again, every window should open on the same position, size and monitor they were closed on, I was able to do the size and position succesfully but monitor/DisplayRegion is giving me incorrect monitor, even when my secondary window is on 2nd monitor, It returns the first monitor (display region), I just need to figure out at the time of saving the placement data, how can I figure out that my specific secondary AppWindow is on which monitor/DisplayRegion?

Following code runs when app is closing

internal static void UpdateAppWindowsPlacements()
    {
        foreach (var item in AppWindowViewModels)
        {
            ApplicationData.Current.LocalSettings.Values[$"AppWindow_SecondaryView_Show_{item.Key}"] = item.AppWindow != null;
            if (item.AppWindow != null)
            {
                var placement = item.AppWindow.GetPlacement();
                var regions = new List<DisplayRegion>(); 
                foreach (var dr in item.AppWindow.WindowingEnvironment.GetDisplayRegions())
                {
                    regions.Add(dr);// this list is just for testing, it gives me boh monitors/DisplayRegions, but no way to find out where this window resides.
                }
                //Size is full screen size and can be bigger bcz it also includes taskbar etc.
                //Display region excludes taskbar etc
                var displayRegion = placement.DisplayRegion;
                var displayRegionWidth = displayRegion.WorkAreaSize.Width;
                var displayRegionHeight = displayRegion.WorkAreaSize.Height;

                var sizeWidth = placement.Size.Width;
                var sizeHeight = placement.Size.Height;

                ApplicationData.Current.LocalSettings.Values[$"AppWindow_SecondaryView_Width_{item.Key}"] = sizeWidth > displayRegionWidth ? displayRegionWidth : sizeWidth;
                ApplicationData.Current.LocalSettings.Values[$"AppWindow_SecondaryView_Height_{item.Key}"] = sizeHeight > displayRegionHeight ? displayRegionHeight : sizeHeight;

                ApplicationData.Current.LocalSettings.Values[$"AppWindow_SecondaryView_X_{item.Key}"] = placement.Offset.X;
                ApplicationData.Current.LocalSettings.Values[$"AppWindow_SecondaryView_Y_{item.Key}"] = placement.Offset.Y;
            }
        }
    }

Opening secondary windows and positioning them as per saved position

internal static async Task OpenSecondaryWindows(int total)
    {
        for (int i = 0; i < total; i++)
        {
            var appWindowViewModel = new AppWindowViewModel(i.ToString());
            AppWindowViewModels.Add(appWindowViewModel);
            var open = ApplicationData.Current.LocalSettings.Values[$"AppWindow_SecondaryView_Show_{i}"];
            if (open == null)
            {
                ApplicationData.Current.LocalSettings.Values[$"AppWindow_SecondaryView_Show_{i}"] = true;
                open = true;
            }
            if ((bool)open)
            {
                await View(appWindowViewModel);
            }
        }
    }
    private static async Task View(AppWindowViewModel appWindowViewModel)
    {
        if (appWindowViewModel.AppWindow is null)
        {
            appWindowViewModel.AppWindow = await AppWindow.TryCreateAsync();
            var frame = new Frame();
            frame.Navigate(typeof(SecondaryPage), appWindowViewModel.Key);
            ElementCompositionPreview.SetAppWindowContent(appWindowViewModel.AppWindow, frame);

            appWindowViewModel.AppWindow.Closed += delegate
            {
                frame.Content = null;
                appWindowViewModel.AppWindow = null;
            };
        }

        var shown = await appWindowViewModel.AppWindow.TryShowAsync();

        var windowWidth = ApplicationData.Current.LocalSettings.Values[$"AppWindow_SecondaryView_Width_{appWindowViewModel.Key}"];
        var windowHeight = ApplicationData.Current.LocalSettings.Values[$"AppWindow_SecondaryView_Height_{appWindowViewModel.Key}"];
        if (windowWidth is double wWidth && windowHeight is double wHeight)
        {
            appWindowViewModel.AppWindow.RequestSize(new Size(wWidth, wHeight));
        }

        var xposition = ApplicationData.Current.LocalSettings.Values[$"AppWindow_SecondaryView_X_{appWindowViewModel.Key}"];
        var yposition = ApplicationData.Current.LocalSettings.Values[$"AppWindow_SecondaryView_Y_{appWindowViewModel.Key}"];
        if (xposition is double xpos && yposition is double ypos)
        {
            var placement = appWindowViewModel.AppWindow.GetPlacement();
            appWindowViewModel.AppWindow.RequestMoveRelativeToDisplayRegion(placement.DisplayRegion, new Point(xpos, ypos));
        }
        else
        {
            appWindowViewModel.AppWindow.RequestMoveAdjacentToCurrentView();
        }
    }

I have a sample uwp app : https://github.com/touseefbsb/AppWindowRemember

you can clone it and run the MultiAppWindowSample2 project, enter "1" in the text box and press the button Open Secondary Windows. it will open 1 secondary window alongside the main window as expected, now move the 2nd window to your 2nd monitor and then close your main window it will ask whether you want to save the placement, press Yes.

Now run the app again, and enter "1" in textbox and press button again, notice the secondary window opens on your first monitor/display. While the aim is to open it on the 2nd monitor as it was closed the last time.

3

There are 3 best solutions below

5
On

UWP AppWindow Get correct monitor/Display region

The problem is the DisplayRegion of current AppWindow is always the first monitor even if you have moved the AppWindow into the second monitor. I will report this problem. and currently there is work around is show AppWindow in the second monitor at first and record the second monitor's id into local setting.

During the testing ApplicationView.GetForCurrentView().GetDisplayRegions()[0] could return correct DisplayRegion for current main app window (does not return correct value when move current into other monitor). you can use it to direct which is second monitor.

private DisplayRegion GetOtherDisplayRegion(DisplayRegion currentAppDisplayRegion)
{
    // Get the list of all DisplayRegions defined for the WindowingEnvironment that our application is currently in
    IReadOnlyList<DisplayRegion> displayRegions = ApplicationView.GetForCurrentView().WindowingEnvironment.GetDisplayRegions();
    foreach (DisplayRegion displayRegion in displayRegions)
    {
        if (displayRegion != currentAppDisplayRegion && displayRegion.IsVisible)
        {
            return displayRegion;
        }
    }

    return null;
}

For more please refer to official code sample .

0
On

First, you must save the DisplayRegion.DisplayMonitorDeviceId (or something to recover on reload). I successfully used the ID.

Then you can get a list of DisplayRegions availible through either the AppWindow.WindowingEnvironment.GetDisplayRegions() or Windows.UI.WindowManagement.WindowingEnvironment.GetDisplayRegions(). This list is not debug viewable, but does work with foreach.

Walk the list and find the matching device, then RequestMoveRelativeToDisplayRegion. The ids in the struct require recasting to "string" to compare correctly.

The coordinates passed are relative to the DisplayRegion [monitor] and will be offset from 0,0 even if the monitor is positioned in negative space from the main monitor.

I just found this sample yesterday and figured out the solution. I need to offer an update to the original article and github sample, but I didn't bring the link home.

1
On

The bug only appeared on Windows 11. The device id is wrong but the offset is right, so we can use the right window offset and device offset to calculate the right device.