How to get screen bounds for WinUI 3 Desktop application?
Asked Answered
D

4

11

I need to achieve below requirements in WinUI 3 Desktop applications.

  1. How to get screen bounds?
  2. How to change windows cursor type in runtime?

I already did this in WinUI UWP applications.

For Screen bounds,

var visibleBounds = Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().VisibleBounds;
var scaleFactor = Windows.Graphics.Display.DisplayInformation.GetForCurrentView().RawPixelsPerViewPixel;
Size screenSize = new Size((visibleBounds.Width * scaleFactor), (visibleBounds.Height * scaleFactor));

For Cursor:

Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNorthwestSoutheast, 0);

Anyone please suggest how to achieve same requirement in WinUI Desktop applications?

Doublepark answered 9/2, 2021 at 16:11 Comment(0)
Q
2

To get information about displays connected using WinUI 3, the simplest I can think of is to use
Nugget package: WindowsDisplayApi
Info here

It's a older package, however it works really well.

Once installed, you can do for example:

using System.Text.RegularExpressions;
using CommunityToolkit.Mvvm.ComponentModel;
using WindowsDisplayAPI;

namespace EasyDisplay.ViewModels;

public partial class DemoViewModel : ObservableObject
{

    [ObservableProperty]
    private string displayName = string.Empty;

    [ObservableProperty]
    private string friendly = string.Empty;

    [ObservableProperty]
    private string resolution = string.Empty;

    [ObservableProperty]
    private string position = string.Empty;

    public DemoViewModel()
    {
        foreach (Display display in Display.GetDisplays())
        {
            DisplayName = display.DisplayName;
            Friendly = GetFriendly(display.DisplayName);
            Resolution = GetResolution(display);
            Position = GetPosition(display);
        }
    }

    private static string GetFriendly(string value)
    {
        return Regex.Replace(value, @"[^A-Za+Z0-9 ]", "");
    }

    private static string GetResolution(Display display)
    {
        return display.CurrentSetting.Resolution.Width.ToString() + " x " + display.CurrentSetting.Resolution.Height.ToString();
    }

    private static string GetPosition(Display display)
    {
        return display.CurrentSetting.Position.X.ToString() + " x " + display.CurrentSetting.Position.Y.ToString();
    }
}

and more as per below:

enter image description here



A second option using the Win32 APIs is available here, and may give you more functionalities.

enter image description here

Option 3 - Using P/Invoke
Step 1: Define the Supporting Structures and P/Invoke Signatures First, define the necessary structs and P/Invoke signatures for EnumDisplayMonitors and GetMonitorInfo.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

// Define the RECT structure
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

// Define the MONITORINFOEX structure
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class MONITORINFOEX
{
    public int Size = Marshal.SizeOf(typeof(MONITORINFOEX));
    public RECT Monitor;
    public RECT Work;
    public uint Flags;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string DeviceName = string.Empty;
}

// Define the MonitorDetails class
public class MonitorDetails
{
    public string DeviceName;
    public bool IsPrimary;
    public int Height;
    public int Width;
    public RECT MonitorArea;
    public RECT WorkArea;
}

// Define the delegate for EnumDisplayMonitors callback
public delegate bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);

// P/Invoke signatures
public class NativeMethods
{
    [DllImport("user32.dll")]
    public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumProc lpfnEnum, IntPtr dwData);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool GetMonitorInfo(IntPtr hMonitor, [In, Out] MONITORINFOEX lpmi);
}


Step 2: Implement GetAllMonitors Method The GetAllMonitors method you've already defined calls the native EnumDisplayMonitors function, iterating through each monitor and collecting details into a list.
public static List<MonitorDetails> GetAllMonitors()
{
    var monitors = new List<MonitorDetails>();
    
    MonitorEnumProc callback = (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) =>
    {
        MONITORINFOEX mi = new MONITORINFOEX();
        
        if (NativeMethods.GetMonitorInfo(hMonitor, mi))
        {
            var monitorInfo = new MonitorDetails
            {
                DeviceName = mi.DeviceName,
                IsPrimary = (mi.Flags & 1) != 0, // MONITORINFOF_PRIMARY
                Height = mi.Monitor.Bottom - mi.Monitor.Top,
                Width = mi.Monitor.Right - mi.Monitor.Left,
                MonitorArea = mi.Monitor,
                WorkArea = mi.Work,
            };
            monitors.Add(monitorInfo);
        }
        return true;
    };

    NativeMethods.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, IntPtr.Zero);
    return monitors;
}
Quartus answered 25/4, 2023 at 15:1 Comment(0)
A
2

Try DisplayArea Class:

DisplayArea.Primary.WorkArea.Width

Gets the rectangle of the work area of the DisplayArea, where the X,Y offset is within the display area's bounds.

Amontillado answered 20/5, 2024 at 13:7 Comment(0)
W
0

I've added the following to App.xaml.cs:

private static MainWindow m_window;

public static MainWindow MainWindow { get { return m_window; } }

protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs e)
{  
   m_window = new MainWindow();        
      
   m_window.Activate();
}      

And when I need bounds anywhere, I can use MainWindow.Bounds.

Regarding cursor, you need ProtectedCursor

this.ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.Arrow);

Only thing it is protected, so you need to use it from right class.

Willenewillet answered 15/12, 2021 at 12:14 Comment(1)
Regarding Cursor: You need to subclass the UI Element for this to work source. Or you use reflection... but that's not a great way to do things.Psychodiagnosis
M
0

I wanted to use WindowsDisplayApi as per the other answer, but its license was overly prohibitive. So I used direct Pinvokes instead, here's how I did it.

I started with the https://github.com/microsoft/CsWin32 nuget package to get easy pinvokes. If you haven't used it before, you can put a NativeMethods.txt file in your project root, and add any methods/consts you want there and it'll automatically generate the functions for you on pre-build. Here's my NativeMethods.txt:

EnumDisplayDevices
EnumDisplaySettings
DISPLAY_DEVICE_ACTIVE

Then I created these helpers to get the screen dimensions:

public record struct Rect(int x, int y, uint width, uint height);

internal class PInvokes
{
    public static IList<Rect> GetScreenRects()
    {
        var screenRects = new List<Rect>();
        for (uint i = 0; ; i++)
        {
            string? deviceName = GetDisplayDeviceName(i);
            if (deviceName == null)
            {
                break;
            }

            var result = GetDisplayDeviceProperties(deviceName);
            screenRects.Add(new Rect(result.Anonymous1.Anonymous2.dmPosition.x,
                result.Anonymous1.Anonymous2.dmPosition.y,
                result.dmPelsWidth,
                result.dmPelsHeight));
        }

        return screenRects;
    }

    private static DEVMODEW GetDisplayDeviceProperties(string deviceName)
    {
        DEVMODEW devMode = default;
        devMode.dmSize = (ushort)Marshal.SizeOf(devMode);
        Windows.Win32.PInvoke.EnumDisplaySettings(deviceName, ENUM_DISPLAY_SETTINGS_MODE.ENUM_CURRENT_SETTINGS, ref devMode);
        return devMode;
    }

    private static string? GetDisplayDeviceName(uint iDevNum)
    {
        DISPLAY_DEVICEW lpDisplayDevice = default;
        lpDisplayDevice.cb = (ushort)Marshal.SizeOf(lpDisplayDevice);
        var result = Windows.Win32.PInvoke.EnumDisplayDevices(null, iDevNum, ref lpDisplayDevice, 0);

        bool isActive = result && (lpDisplayDevice.StateFlags & Windows.Win32.PInvoke.DISPLAY_DEVICE_ACTIVE) != 0;
        return isActive ? lpDisplayDevice.DeviceName.ToString() : null;
    }
}
Mob answered 16/2, 2024 at 20:49 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.