How to define a monitor as the primary display in C#

By FoxLearn 2/14/2025 7:29:43 AM   63
In this article, I'll walk you through how to dynamically change the primary display on Windows 10 using C# in a WinForms application.

1. Create the MonitorChanger Class

To change the primary monitor programmatically, you need to include a class along with helper structs and methods in your project.

using System;
using System.Runtime.InteropServices;

class MonitorChanger
{
    /// <summary>
    /// Static method to set a specified monitor as the primary.
    /// </summary>
    public static void SetAsPrimaryMonitor(uint id)
    {
        DISPLAY_DEVICE device = new DISPLAY_DEVICE();
        DEVMODE deviceMode = new DEVMODE();
        device.cb = Marshal.SizeOf(device);

        NativeMethods.EnumDisplayDevices(null, id, ref device, 0);
        NativeMethods.EnumDisplaySettings(device.DeviceName, -1, ref deviceMode);
        int offsetX = deviceMode.dmPosition.x;
        int offsetY = deviceMode.dmPosition.y;
        deviceMode.dmPosition.x = 0;
        deviceMode.dmPosition.y = 0;

        NativeMethods.ChangeDisplaySettingsEx(
            device.DeviceName,
            ref deviceMode,
            (IntPtr)null,
            (ChangeDisplaySettingsFlags.CDS_SET_PRIMARY | ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | ChangeDisplaySettingsFlags.CDS_NORESET),
            IntPtr.Zero
        );

        device = new DISPLAY_DEVICE();
        device.cb = Marshal.SizeOf(device);

        // Update other monitors
        for (uint otherId = 0; NativeMethods.EnumDisplayDevices(null, otherId, ref device, 0); otherId++)
        {
            if (device.StateFlags.HasFlag(DisplayDeviceStateFlags.AttachedToDesktop) && otherId != id)
            {
                device.cb = Marshal.SizeOf(device);
                DEVMODE otherDeviceMode = new DEVMODE();

                NativeMethods.EnumDisplaySettings(device.DeviceName, -1, ref otherDeviceMode);

                otherDeviceMode.dmPosition.x -= offsetX;
                otherDeviceMode.dmPosition.y -= offsetY;

                NativeMethods.ChangeDisplaySettingsEx(
                    device.DeviceName,
                    ref otherDeviceMode,
                    (IntPtr)null,
                    (ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | ChangeDisplaySettingsFlags.CDS_NORESET),
                    IntPtr.Zero
                );
            }

            device.cb = Marshal.SizeOf(device);
        }

        // Apply new display settings
        NativeMethods.ChangeDisplaySettingsEx(null, IntPtr.Zero, (IntPtr)null, ChangeDisplaySettingsFlags.CDS_NONE, IntPtr.Zero);
    }
}

2. Set the Primary Monitor by its ID

Once the MonitorChanger class is integrated into your project, setting the primary monitor is simple. You just need the monitor's identifier, which can be easily retrieved from the Windows Display Settings.

For example, if you have multiple monitors, their IDs will start from 0.

Consider a setup with three monitors:

  • Monitor 1: Samsung U28E590 4K
  • Monitor 2: BenQ Zowie 27" XL2740 (240Hz)
  • Monitor 3: Samsung C24F390

If you'd like to set Monitor 1 as the primary display, the code would look like this:

// Set Monitor #1 as Primary
MonitorChanger.SetAsPrimaryMonitor(0);

// Set Monitor #2 as Primary
// MonitorChanger.SetAsPrimaryMonitor(1);

// Set Monitor #3 as Primary
// MonitorChanger.SetAsPrimaryMonitor(2);

The SetAsPrimaryMonitor method takes the monitor's ID as its argument. After running the code, you can confirm the change by checking the Display settings in Windows, where your chosen monitor will now be set as the primary display.

In order to make everything work smoothly, here are the required structs, enums, and external methods.

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct DEVMODE
{
    public const int CCHDEVICENAME = 32;
    public const int CCHFORMNAME = 32;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
    [System.Runtime.InteropServices.FieldOffset(0)]
    public string dmDeviceName;
    // Additional fields...
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DISPLAY_DEVICE
{
    public int cb;
    public string DeviceName;
    public string DeviceString;
    public DisplayDeviceStateFlags StateFlags;
    // Additional fields...
}

[Flags()]
public enum DisplayDeviceStateFlags : int
{
    AttachedToDesktop = 0x1,
    MultiDriver = 0x2,
    PrimaryDevice = 0x4,
    // Additional flags...
}

[Flags()]
public enum ChangeDisplaySettingsFlags : uint
{
    CDS_NONE = 0,
    CDS_SET_PRIMARY = 0x00000010,
    CDS_UPDATEREGISTRY = 0x00000001,
    // Additional flags...
}

public class NativeMethods
{
    [DllImport("user32.dll")]
    public static extern DISP_CHANGE ChangeDisplaySettingsEx(string lpszDeviceName, ref DEVMODE lpDevMode, IntPtr hwnd, ChangeDisplaySettingsFlags dwflags, IntPtr lParam);

    [DllImport("user32.dll")]
    public static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);

    [DllImport("user32.dll")]
    public static extern bool EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE devMode);
}

public enum DISP_CHANGE : int
{
    Successful = 0,
    Failed = -1,
    // Additional values...
}

public struct POINTL
{
    public int x;
    public int y;
}

By following these steps, you can easily switch the primary monitor in Windows 10 programmatically using C#. This can be especially useful when switching between tasks like gaming and work, as it saves time and eliminates the need for manual adjustments.