C# – How to force WPF startup window to specific screen

cstartupwindowwpfxaml

I have a WPF application that will show information on a projector through a dedicated window.
I would like to configure what screen to be used for projector display and what to be used for main application window.

This code will generate projector output on specified screen:

var screen = GetProjectorScreen();
_projectorWindow = new ProjectorWindow();
_projectorWindow.Left = screen.WorkingArea.Left;
_projectorWindow.Top = screen.WorkingArea.Top;
_projectorWindow.Owner = _parentWindow;
_projectorWindow.Show();


public static Screen GetProjectorScreen()
{
    var screens = Screen.AllScreens;
    if (screens.Length > 1 && Settings.Default.DisplayScreen < screens.Length)
    {
        return screens[Settings.Default.DisplayScreen];
    }
    return screens[0];
}

I have tried to do the same trick with startup form, but so far without success.
I tried to set Top and Left properties in MainWindow constructor but that did not work.

The startup window is launched from App.xaml.cs by setting StartupUri:

StartupUri = new Uri("Windows/MainWindow.xaml", UriKind.Relative);

Is there any other way to launch startup form?
I tried to just call the constructor but that causes a crash because some resources are no longer loaded.

Best Answer

I got it working. It is necessary to set WindowState to Normal before setting window location. And the setting will not work at all until the window is created, i.e. after constructor call. I therefore call the explicit setting in Windows_Loaded event. That might cause a flickering if window need to be moved, but that is acceptable to me.

The SetScreen method should also be called after screen settings have changed manually by user.

private void SetScreen()
{
    var mainScreen = ScreenHandler.GetMainScreen();
    var currentScreen = ScreenHandler.GetCurrentScreen(this);
    if (mainScreen.DeviceName != currentScreen.DeviceName)
    {
        this.WindowState = WindowState.Normal;
        this.Left = mainScreen.WorkingArea.Left;
        this.Top = mainScreen.WorkingArea.Top;
        this.Width = mainScreen.WorkingArea.Width;
        this.Height = mainScreen.WorkingArea.Height;
        this.WindowState = WindowState.Maximized;
    }
}

The backup ScreenHandler utility is very simple:

public static class ScreenHandler
{
    public static Screen GetMainScreen()
    {
        return GetScreen(Settings.Default.MainScreenId);
    }

    public static Screen GetProjectorScreen()
    {
        return GetScreen(Settings.Default.ProjectorScreenId);
    }

    public static Screen GetCurrentScreen(Window window)
    {
        var parentArea = new Rectangle((int)window.Left, (int)window.Top, (int)window.Width, (int)window.Height);
        return Screen.FromRectangle(parentArea);
    }

    private static Screen GetScreen(int requestedScreen)
    {
        var screens = Screen.AllScreens;
        var mainScreen = 0;
        if (screens.Length > 1 && mainScreen < screens.Length)
        {
            return screens[requestedScreen];
        }
        return screens[0];
    }
}
Related Topic