Introduction
While creating a program that would allow me to quickly change my display settings, I discovered that there were not many working examples for the .NET environment and none showed the whole picture of changing resolutions and primary displays as well as attaching and detaching monitors.
The UpdateSettings
function is capable of setting the primary display, attaching and detaching, and setting the display properties Size
, BitsPerPixel
, Frequency
.
Background
I am constantly changing the display settings on my dual monitor computer depending on what I am doing. For example, when I am using Visual Studio I like to have both monitors running at 1280 by 1024, but when I am web browsing I will set them to 1024 by 768. Then there are the sim/adventure games where the edge of the screen is used to scroll the world. These do not function too well if you have a second monitor attached. So it's another display setting change to drop the second monitor.
Using the Code
Display_Settings
can be added to any form. It contains properties that allow you to give it comboboxes and panels and trackbars to populate and control. This means you need to only call the UpdateSettings
on a Apply button. All control properties are handled to ensure no incorrect settings are applied. Display_Settings
also provides events that allow you to control your own controls.
Private Sub btnApply_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnApply.Click
Me.Display_Settings1.UpdateSettings()
End Sub
The UpdateSettings
function also allows you to update stored settings by passing it an array of the Display_Settings.Display
class. The Display_Settings.Display
class is Serializable and inherits from Kim Major's ContractBase(of T) class. This allows for easy Serialization. Big thanks to Kim Major for this class.
The UpdateSettings
function starts by setting the primary display. This is achieved by moving the current primary display away from position 0,0 and moving the new primary display to position 0,0.
It then loops through each display device. If the display is to be detached, it will move it to position 0,0 and set its size to 0,0. If the display is attached, it will set it to its new settings. This also has the effect of attaching any displays not previously attached.
Finally we loop through all the display devices again and apply the settings.
UpdateSettings Function
Public Function UpdateSettings(ByVal Displays As Display()) _
As WinAPI.DisplaySetting_Results
If Not Me.PrimaryDisplayID = Me.PrimaryDisplayRegistryID Then
SetPrimaryDisplay(Me.m_Displays(Me.PrimaryDisplayRegistryID).DeviceName, _
Me.m_Displays(Me.PrimaryDisplayID).DeviceName)
End If
For Each disp As Display In Displays
If Not disp.Attached Then
Dim dm As New WinAPI.DEVMODE
dm.dmDeviceName = New [String](New Char(31) {})
dm.dmFormName = New [String](New Char(31) {})
dm.dmSize = CShort(Marshal.SizeOf(dm))
dm.dmFields = WinAPI.DEVMODE_Flags.DM_POSITION Or _
WinAPI.DEVMODE_Flags.DM_PELSWIDTH Or WinAPI.DEVMODE_Flags.DM_PELSHEIGHT
dm.dmPelsWidth = 0
dm.dmPelsHeight = 0
dm.dmPosition.x = 0
dm.dmPosition.y = 0
Dim Result As WinAPI.DisplaySetting_Results = _
WinAPI.ChangeDisplaySettingsEx_
(disp.DeviceName, dm, Nothing, WinAPI.DeviceFlags.CDS_UPDATEREGISTRY _
Or WinAPI.DeviceFlags.CDS_NORESET, 0)
Else
Dim dm As New WinAPI.DEVMODE
dm.dmDeviceName = New [String](New Char(31) {})
dm.dmFormName = New [String](New Char(31) {})
dm.dmSize = CShort(Marshal.SizeOf(dm))
dm.dmFields = WinAPI.DEVMODE_Flags.DM_POSITION Or _
WinAPI.DEVMODE_Flags.DM_PELSWIDTH Or _
WinAPI.DEVMODE_Flags.DM_PELSHEIGHT _
Or WinAPI.DEVMODE_Flags.DM_DISPLAYFLAGS _
Or WinAPI.DEVMODE_Flags.DM_BITSPERPEL _
Or WinAPI.DEVMODE_Flags.DM_DISPLAYFREQUENCY
dm.dmPelsWidth = disp.Size.Width
dm.dmPelsHeight = disp.Size.Height
dm.dmPosition.x = disp.Location.X
dm.dmPosition.y = disp.Location.Y
dm.dmBitsPerPel = disp.BitsPerPixel
dm.dmDisplayFrequency = disp.Frequency
Dim Result As WinAPI.DisplaySetting_Results = _
WinAPI.ChangeDisplaySettingsEx(disp.DeviceName, dm, _
Nothing, WinAPI.DeviceFlags.CDS_SET_PRIMARY _
Or WinAPI.DeviceFlags.CDS_UPDATEREGISTRY _
Or WinAPI.DeviceFlags.CDS_NORESET, 0)
End If
Next
For i As Integer = Displays.Length - 1 To 0 Step -1
Dim dm1 As New WinAPI.DEVMODE
dm1.dmDeviceName = New [String](New Char(31) {})
dm1.dmFormName = New [String](New Char(31) {})
dm1.dmSize = CShort(Marshal.SizeOf(dm1))
Dim lStatus As WinAPI.DisplaySetting_Results = _
WinAPI.ChangeDisplaySettingsEx(Me.m_Displays(i).DeviceName, _
dm1, Nothing, WinAPI.DeviceFlags.CDS_UPDATEREGISTRY, Nothing)
WinAPI.OutputDebugString(lStatus.ToString & vbCrLf)
Next
Me.InitializeDisplays()
End Function
SetPrimaryDisplay Function
Public Sub SetPrimaryDisplay_
(ByVal OldPrimary As String, ByVal NewPrimary As String)
Dim Result As WinAPI.DisplaySetting_Results = 0
Dim dm1 As WinAPI.DEVMODE = NewDevMode()
WinAPI.EnumDisplaySettings(NewPrimary, _
WinAPI.DEVMODE_SETTINGS.ENUM_REGISTRY_SETTINGS, dm1)
Dim dm3 As WinAPI.DEVMODE = NewDevMode()
dm3.dmFields = WinAPI.DEVMODE_Flags.DM_POSITION
dm3.dmPosition.x = dm1.dmPelsWidth
dm3.dmPosition.y = 0
Result = WinAPI.ChangeDisplaySettingsEx(OldPrimary, dm3, _
Nothing, WinAPI.DeviceFlags.CDS_UPDATEREGISTRY _
Or WinAPI.DeviceFlags.CDS_NORESET, 0)
Console.WriteLine(Result.ToString)
Dim dm2 As WinAPI.DEVMODE = NewDevMode()
WinAPI.EnumDisplaySettings(NewPrimary, _
WinAPI.DEVMODE_SETTINGS.ENUM_REGISTRY_SETTINGS, dm2)
Dim dm4 As WinAPI.DEVMODE = NewDevMode()
dm4.dmFields = WinAPI.DEVMODE_Flags.DM_POSITION
dm4.dmPosition.x = 0
dm4.dmPosition.y = 0
Result = WinAPI.ChangeDisplaySettingsEx(NewPrimary, dm4, Nothing, _
WinAPI.DeviceFlags.CDS_SET_PRIMARY Or WinAPI.DeviceFlags.CDS_UPDATEREGISTRY _
Or WinAPI.DeviceFlags.CDS_NORESET, 0)
Console.WriteLine(Result.ToString)
Dim dm5 As WinAPI.DEVMODE = NewDevMode()
Result = WinAPI.ChangeDisplaySettingsEx(OldPrimary, dm5, _
Nothing, WinAPI.DeviceFlags.CDS_UPDATEREGISTRY, Nothing)
Console.WriteLine(Result.ToString)
Dim dm6 As WinAPI.DEVMODE = NewDevMode()
Result = WinAPI.ChangeDisplaySettingsEx(NewPrimary, dm6, Nothing, _
WinAPI.DeviceFlags.CDS_SET_PRIMARY Or WinAPI.DeviceFlags.CDS_UPDATEREGISTRY, 0)
Console.WriteLine(Result.ToString)
Console.WriteLine(Result.ToString)
End Sub
Points of Interest
Always ensure your displays have a common edge. If the screens overlap or there is a gap, you can get some really strange effects, e.g. you mouse pointer does not point to where the mouse point is. I had to use the keyboard a couple of times to reset my display settings.
ChangeDisplaySettingsEX
recommends that to apply the settings, you should use ChangeDisplaySettingsEX(nothing, nothing, nothing, 0, nothing)
. This did not work for me. I had to call ChangeDisplaySettingsEX
for each device name with an empty DEVMODE
structure and the flag CDS_UPDATEREGISTRY
.
History
- 6th July, 2009: First version