Search code examples
c#wpfxamlmultiple-monitors

Select monitor to show WPF application


I'm working on a WPF application, and I want to select the monitor where to display the window. My .NET version is 8.0, SO is Windows and Version is 7.0.

I'm trying to apply the [Mostlytech][2] steps, but simply it's just not working for me, the window is always displayed on the same monitor independent of the selected AllScreens parameter.:

My C#:

  public MainWindow () {

      this.WindowStartupLocation = WindowStartupLocation.Manual;
      Debug.Assert(System.Windows.Forms.SystemInformation.MonitorCount > 1);
      System.Drawing.Rectangle workingArea = System.Windows.Forms.Screen.AllScreens[0].WorkingArea;

      this.Left = workingArea.Left;
      this.Top = workingArea.Top;
      this.Width = workingArea.Width;
      this.Height = workingArea.Height;
      this.WindowState = WindowState.Maximized;
      this.WindowStyle = WindowStyle.None;
      this.Topmost = true;
      this.Show();

      InitializeComponent();
      SetLists();
      // Rest of the Program.

  }

My XAML header:

<Window x:Class="_4913_TV.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:_4913_TV"
        mc:Ignorable="d"
        WindowState="Maximized"
        WindowStyle="None"
        Title="MainWindow"
        Cursor="None"
        Width="4096"
        Height="2160"
        KeyDown="KeyedKey">

The System.Drawing.Rectangle workingArea = System.Windows.Forms.Screen.AllScreens[0].WorkingArea;it seems to correctly contain the information from my monitors.

[![enter image description here][3]][3]

Thanks very much for your support.

EDIT

Finally, the app has been implemented but we have found a problem with the resolution of the application that did not happen with the screens available in the office...

In this picture, "2" is how the app should be seen, and "1" is how it ends up looking.

Note: this mistake only occurs when starts the application from the executable generated by Visual Studio, but if it starts from "F5" in Visual Studio the application is displayed correctly, as "2".

enter image description here


Solution

  • The code snippet you linked won't make the window maximized correctly when the monitors' DPI are not default (100%).

    Instead, I would like to suggest a different approach. The code below will manipulate the position and size of maximized window so that it will fit the working area of a monitor specified with index number given by command-line argument.

    using System;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Windows;
    using System.Windows.Interop;
    
    namespace WpfApp2;
    
    public partial class MainWindow : Window
    {
        public int Index { get; private set; }
    
        public MainWindow()
        {
            InitializeComponent();
    
            // Get index number from command-line argument.
            Index = int.TryParse(Environment.GetCommandLineArgs().LastOrDefault(), out int i) && (i > 0) ? i : 0;
    
            nint handle = new WindowInteropHelper(this).EnsureHandle();
            var source = HwndSource.FromHwnd(handle);
            source.AddHook(new HwndSourceHook(WndProc));
        }
    
        private const int WM_GETMINMAXINFO = 0x0024;
    
        [StructLayout(LayoutKind.Sequential)]
        private 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;
        }
    
        private nint WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled)
        {
            switch (msg)
            {
                case WM_GETMINMAXINFO:
                    var screens = System.Windows.Forms.Screen.AllScreens;
                    if (Index < screens.Length)
                    {
                        var workingArea = screens[Index].WorkingArea;
    
                        var info = Marshal.PtrToStructure<MINMAXINFO>(lParam);
    
                        info.ptMaxPosition.x = workingArea.X;
                        info.ptMaxPosition.y = workingArea.Y;
                        info.ptMaxSize.x = workingArea.Width;
                        info.ptMaxSize.y = workingArea.Height;
    
                        Marshal.StructureToPtr(info, lParam, true);
                    }
                    break;
            }
            return nint.Zero;
        }
    }
    
    <Window x:Class="WpfApp2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow"
            Width="400" Height="300"
            WindowStartupLocation="Manual"
            WindowState="Maximized"
            Topmost="True">
        <Grid>
    
        </Grid>
    </Window>
    

    It requires the following sections to be included in the application manifest.

    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
      <application>
        <!-- Windows 10 -->
        <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
      </application>
    </compatibility>
    
    <application xmlns="urn:schemas-microsoft-com:asm.v3">
      <windowsSettings>
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
      </windowsSettings>
    </application>