WPF Windows on Two Screens

WPF Windows on Two Screens

The post shows how to position Wpf window on secondary monitor or show two windows on two monitors. The post contains complete code and we discuss how to address several scenario.

Table of contents:

  1. Features.
  2. Background.
  3. Solution.
  4. Application.
  5. MainWindow.
  6. MainWindow Model.

There are the following disclaimers:

  1. provided code and conclusions base on empiric results, so could be wrong or describe thing not correctly;
  2. the code was tested on computer with two monitors, so three and more monitors could behave differently;
  3. there are many topics concerning how to show or manipulate by the secondary monitor in Wpf application. The post doesn't include these links, as it may be easily googled.
the code was tested on computer with two monitors
code was tested on computer with two monitors

 

example: code was tested on computer with two monitors

Features

Application demonstrates the following features:

  1. output maximized windows on all displays;
  2. window contains list box with sizes of screens from system parameters and Screen class;
  3. using of dependency container.

Background

Solution uses C#6, .Net 4.6.1, Wpf with MVVM pattern, System.Windows.FormsSystem.Drawing, NuGet packages Unity and Ikc5.TypeLibrary.

Solution

Solution contains one Wpf application project. From the Wpf application all screens are considered as one "virtual" screen. Here’s when a pressing question about wpf window position pops up.

In order to position window it is necessary set window coordinates in "current" screen size. SystemParameters provides physical resolution of the primary screen, but according to Wpf architecture position of elements should not rely on physical dimensions of the display. The main issue is that exact value of "current" screen size depends on various factors. For example, they include text size from Windows' settings, how user connects to computer - by remote access or locally, and so on.

The WPF set window position problem can be solved by using the methods included in the System.Windows.Forms library.

Screen class from System.Windows.Forms library provides useful property Screen[] AllScreens. It allows enumerate all screen and reads their working area. But coordinates of non-primary screen are set according to "current" text size of the primary window.

Application

As windows are created manually, StartupUri should be removed from App.xaml. Method OnStartup executes the following code:

  • init Unity container;
  • calculate current text size of the primary screen as ratio between Screen.PrimaryScreen.WorkingArea and SystemParameters.PrimaryScreen;
  • for each screen create window, position at current screen, show and maximize.
public partial class App : Application
{
    private void InitContainer(IUnityContainer container)
    {
        container.RegisterType<ILogger, EmptyLogger>();
        container.RegisterType<IMainWindowModel, MainWindowModel>();
    }

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        IUnityContainer container = new UnityContainer();
        InitContainer(container);

        var logger = container.Resolve<ILogger>();
        logger.Log($"There are {Screen.AllScreens.Length} screens");

        // calculates text size in that main window (i.e. 100%, 125%,...)
        var ratio = Math.Max(Screen.PrimaryScreen.WorkingArea.Width / SystemParameters.PrimaryScreenWidth,
                            Screen.PrimaryScreen.WorkingArea.Height / SystemParameters.PrimaryScreenHeight);

        var pos = 0;
        foreach (var screen in Screen.AllScreens)
        {
            logger.Log(
                $"#{pos + 1} screen, size = ({screen.WorkingArea.Left}, {screen.WorkingArea.Top}, {screen.WorkingArea.Width}, {screen.WorkingArea.Height}), " +
                (screen.Primary ? "primary screen" : "secondary screen"));

            // Show automata at all screen
            var mainViewModel = container.Resolve<IMainWindowModel>(
                new ParameterOverride("backgroundColor", _screenColors[Math.Min(pos++, _screenColors.Length - 1)]),
                new ParameterOverride("primary", screen.Primary),
                new ParameterOverride("displayName", screen.DeviceName));

            var window = new MainWindow(mainViewModel);
            if (screen.Primary)
                Current.MainWindow = window;

            window.Left = screen.WorkingArea.Left / ratio;
            window.Top = screen.WorkingArea.Top / ratio;
            window.Width = screen.WorkingArea.Width / ratio;
            window.Height = screen.WorkingArea.Height / ratio;
            window.Show();
            window.WindowState = WindowState.Maximized;
        }
        Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
    }

    private readonly Color[] _screenColors =
    {
        Colors.LightGray, Colors.DarkGray, Colors.Gray, Colors.SlateGray, Colors.DarkSlateGray
    };
}Code language: HTML, XML (xml)

MainWindow

Main window contains ListView that shows the list of ScreenRectangle models from MainWindowModel view model. In order to show window on expected screen, it should has the following properties:

WindowStartupLocation="Manual"
WindowState="Normal"Code language: JavaScript (javascript)

I.e., The WPF window location can be set to manual mode with this property.

In addition, we remove caption bar and make window non-resizable

WindowStyle="None"
ResizeMode="NoResize"Code language: JavaScript (javascript)

If comment line #45 in App.xaml.cs

window.WindowState = WindowState.Maximized;Code language: JavaScript (javascript)

then window will occupy whole screen except taskbar.

Web Development Services and Solutions Require Trusted Expertise Discover how Svitla Systems empowers your web development initiatives with innovative technologies and proven experience. Learn More

MainWindow Model

MainWindowModel class implements IMainWindowModel interface

public interface IMainWindowModel
{
    /// <summary>
    /// Background color.
    /// </summary>
    Color BackgroundColor { get; }
    /// <summary>
    /// Width of the view.
    /// </summary>
    double ViewWidth { get; set; }
    /// <summary>
    /// Height of the view.
    /// </summary>
    double ViewHeight { get; set; }
    /// <summary>
    /// Set of rectangles.
    /// </summary>
    ObservableCollection<ScreenRectangle> Rectangles { get; }
}Code language: PHP (php)

Background color is used to colorized window and distinct them at different screens. ViewHeight and ViewWidth are bound to attached properties in order to obtain view size in view model (code is taken from Pushing read-only GUI properties back into ViewModel). ScreenRectangle class looks like derived class from Tuple<string, Rectangle> that implements NotifyPropertyChanged interface:

public class ScreenRectangle : BaseNotifyPropertyChanged
{
    protected ScreenRectangle()
    {
        Name = string.Empty;
        Bounds = new RectangleF();
    }

    public ScreenRectangle(string name, RectangleF bounds)
    {
        Name = name;
        Bounds = bounds;
    }

    public ScreenRectangle(string name, double left, double top, double width, double height)
        : this(name, new RectangleF((float)left, (float)top, (float)width, (float)height))
    {
    }

    public ScreenRectangle(string name, double width, double height)
        : this(name, new RectangleF(0, 0, (float)width, (float)height))
    {
    }

    #region Public properties

    private string _name;
    private RectangleF _bounds;

    public string Name
    {
        get { return _name; }
        set { SetProperty(ref _name, value); }
    }

    public RectangleF Bounds
    {
        get { return _bounds; }
        set { SetProperty(ref _bounds, value); }
    }

    #endregion Public properties

    public void SetSize(double width, double height)
    {
        Bounds = new RectangleF(Bounds.Location, new SizeF((float)width, (float)height));
    }

}

If you’d like to learn more about the WFP get current window, please refer to the Microsoft documentation on this page.

Also, if you need to find out more information about the WPF get window position, please refer to this valuable post with a code example.

FAQ

Are people still using WPF?

Yes, in enterprise and desktop line-of-business applications where long-lived .NET codebases, rich UI, and Windows-only deployment are permissible, people are still using WPF. The article referenced is not theoretical; it shows up-to-date practical work on a real-world problem (using .NET 4.6.1, MVVM, Unity DI, multi-monitor handling via System.Windows.Forms.Screen), solving window positioning issues in WPF that matter to the world. Most greenfield apps might be getting an investment of attention from newer UI stacks like WinUI, MAUI, and a web-based frontend to extend existing WPF solutions; however, they should be rewritten. As long as complex desktop tools run on Windows, WPF is relevant.

Is WPF still relevant in 2025?

WPF remains a good pick in 2025 for enterprise and pro desktop use apps that need deep links to the .NET framework world. New WPF apps use MVVM, dependency injection boxes, support for many monitors, and deal with things like DPI scaling, text size settings, and remote vs local sessions. WinUI, MAUI, and web front ends might be seen as the newer picks for cross-platform or greenfield work by some people, but old WPF code bases are key in live systems that are not just kept up but also grown. It works only on Windows and is a very stable, rich desktop UI case where stability plus long-term help is needed, so WPF proves to be a really useful choice.

What are the disadvantages of WPF?

WPF is locked to Windows and the old .NET desktop stack; therefore, it can’t be used for cross‑platform client applications. It gets weird on modern multi‑monitor, high‑DPI setups where window coordinates, scaling, and text size settings may throw all sorts of layout or positioning problems onto the screen that have to be coded around. The framework has a steep learning curve (XAML, data binding, MVVM, dependency injection), and newer UI frameworks are actively invested in by both Microsoft and the community at large; thus, WPF will feel dated if you’re working greenfield or targeting multiple platforms.