{"id":5502,"date":"2023-11-19T01:13:28","date_gmt":"2023-11-19T00:13:28","guid":{"rendered":"https:\/\/eskerahn.dk\/?p=5502"},"modified":"2024-05-14T11:47:03","modified_gmt":"2024-05-14T09:47:03","slug":"the-easy-way-to-make-a-net-8-0-winforms-app-per-monitor-dpi-aware","status":"publish","type":"post","link":"https:\/\/eskerahn.dk\/?p=5502","title":{"rendered":"The EASY way to make a .Net 8.0 WinForms app per monitor DPI aware"},"content":{"rendered":"<p>As posted in <a href=\"https:\/\/eskerahn.dk\/?p=1747\" target=\"_blank\" rel=\"noopener noreferrer\">a rather old post,<\/a> many apps have problems with windows scaling.<\/p>\n<p>See these screen-dumps from Windows lengthy guide that was found <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/dn469266(v=vs.85).aspx\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a><\/p>\n<p><a href=\"https:\/\/eskerahn.dk\/?p=1779\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1781\" src=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2017\/01\/g64cdc4g.bmp\" alt=\"\" width=\"600\" height=\"132\" \/><\/a><\/p>\n<p>With previous versions of .Net <strong>Framework<\/strong> this have been somewhat cumbersome, see my post <a href=\"https:\/\/eskerahn.dk\/?p=1779\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<p>But with .Net 8.0 WinForms (well also 7.0 and 6.0), and Windows 11 (or newer 10), it is now fairly simple, as long as you follow a few rules, that can be relatively easy applied copying existing Framework projects. I experimented with VS 17.8 on Win 11 23H2.<\/p>\n<p><!--more--><\/p>\n<p>Below I have used C#, but not limited to that.<\/p>\n<h2>Guide creating a <span style=\"text-decoration: underline;\">NEW<\/span> .net 8.0 WinForm App<\/h2>\n<h3>1)<\/h3>\n<p>In the outer program.cs, be sure it calls<\/p>\n<p><strong>ApplicationConfiguration.Initialize();<\/strong><\/p>\n<p>BEFORE<\/p>\n<p>Application.Run(new Form1());<\/p>\n<p>(A new .net 8.0 WinForm project has this as default)<\/p>\n<p>&nbsp;<\/p>\n<h3>2)<\/h3>\n<p>You would need a project file similar to this with settings used by the <em>ApplicationConfiguration.Initialize()<\/em><\/p>\n<p>&lt;Project Sdk=&#8221;Microsoft.NET.Sdk&#8221;&gt;<\/p>\n<blockquote><p>&lt;PropertyGroup&gt;<br \/>\n&lt;OutputType&gt;WinExe&lt;\/OutputType&gt;<br \/>\n&lt;TargetFramework&gt;<strong>net8.0-windows<\/strong>&lt;\/TargetFramework&gt;<br \/>\n&lt;Nullable&gt;enable&lt;\/Nullable&gt;<br \/>\n&lt;UseWindowsForms&gt;<strong>true<\/strong>&lt;\/UseWindowsForms&gt;<\/p>\n<p>&lt;ApplicationHighDpiMode&gt;<strong>PerMonitorV2<\/strong>&lt;\/ApplicationHighDpiMode&gt;<br \/>\n&lt;!&#8211; https:\/\/learn.microsoft.com\/en-us\/dotnet\/desktop\/winforms\/whats-new\/net60?view=netdesktop-6.0#project-level-application-settings &#8211;&gt;<\/p>\n<p>&lt;\/PropertyGroup&gt;<\/p><\/blockquote>\n<p>&lt;\/Project&gt;<br \/>\nThe other settings in the link, are not important for the scaling.<\/p>\n<h3>3)<\/h3>\n<p>To get <strong>ListBoxes, CheckBoxes<\/strong> et cetera to work, a little <strong>trick<\/strong> is needed, as they are (still) not properly supported, as the bottom of any list creeps up, moving back and forth between monitors, ignoring any anchor(!), the bug originates in that they want to keep the height an integral multiple of the item height, and by a <strong>bug<\/strong> do not handle rounding issues.<\/p>\n<p><strong>Fix: Place<\/strong> each and every bottom-anchored list-control <strong>in<\/strong> its own dummy <strong>Panel<\/strong> and use <strong>DockStyle.Fill<\/strong> for the list-control filling the panel, as fill overrules the creeping! And they DO support that the panels themselves <em>can<\/em> be either docked or anchored as you please&#8230;.<\/p>\n<p>(Obviously if the list is already filling a control, say a split-panel, there is no need for an extra panel, just make sure it uses DockStyle.Fill and not just anchors)<\/p>\n<p><strong>That is really all there is to it&#8230; AFAIK<br \/>\n<\/strong><\/p>\n<p>There <em>might<\/em> be other limitations that needs workarounds. If you encounter any types not mentioned, <strong>please comment<\/strong>.<\/p>\n<p>&nbsp;<\/p>\n<p>Here the trick applied to the right list only:<\/p>\n<p><a href=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2023\/11\/WinFormsAppCreepingListOnRescale_workaround.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-5511\" src=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2023\/11\/WinFormsAppCreepingListOnRescale_workaround-1024x507.png\" alt=\"\" width=\"640\" height=\"317\" srcset=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2023\/11\/WinFormsAppCreepingListOnRescale_workaround-1024x507.png 1024w, https:\/\/eskerahn.dk\/wp-content\/uploads\/2023\/11\/WinFormsAppCreepingListOnRescale_workaround-300x149.png 300w, https:\/\/eskerahn.dk\/wp-content\/uploads\/2023\/11\/WinFormsAppCreepingListOnRescale_workaround-150x74.png 150w, https:\/\/eskerahn.dk\/wp-content\/uploads\/2023\/11\/WinFormsAppCreepingListOnRescale_workaround-768x380.png 768w, https:\/\/eskerahn.dk\/wp-content\/uploads\/2023\/11\/WinFormsAppCreepingListOnRescale_workaround.png 1078w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<p>Here the left bottom anchored list is placed directly in a split panel. (the right one in a dummy panel).<br \/>\nIf I put the list-box directly on the native form, it collapses completely(!) on the first return to 100% making it <em>totally<\/em> useless, in a container it is faulty but not entirely useless&#8230;. And with the extra panel, it works!<\/p>\n<p>Here the source if you would like to experiment: <a href=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2023\/11\/WinFormsAppCreepingListOnRescale_workaround.zip\">WinFormsAppCreepingListOnRescale_workaround<\/a><\/p>\n<p>You perhaps noticed that the window certainly isn&#8217;t 125% scaled above. It is 114% \u00d7 133%.\u00a0 Selecting 350% gave 335% \u00d7 382%.<br \/>\nBut that is a more general and unrelated Windows scaling bug, out of scope here&#8230;<\/p>\n<p>&nbsp;<\/p>\n<hr \/>\n<h2>Guide in upgrading an <span style=\"text-decoration: underline;\">EXISTING<\/span> .Net Framework 4.8 app<\/h2>\n<p>Start a new .Net 8.0 WinForms project.<\/p>\n<p>Close VS, copy all your code files in, be careful on program.cs. (Avoid <em>.vs<\/em>, <em>bin<\/em> and <em>obj dirs<\/em>, and the <em><span style=\"color: #999999;\">xxxx<\/span>.sln<\/em>, <em><span style=\"color: #999999;\">xxxx<\/span>.csproj<\/em> and <em><span style=\"color: #999999;\">xxxx.<\/span>csproj.user<\/em> files)<br \/>\n(if your program.cs is standard, ignore it. If it your code in, then just make sure to add the initialisation-line as in the NEW project above, before creating any form)<\/p>\n<p><strong>Note<\/strong> that the below fix MIGHT only be needed if the parent control is a <strong>Horizontal splitContainer<\/strong>, I&#8217;m not sure though, if other controls can cause the same issue..<\/p>\n<p>The fix can be done either by UI or by code<\/p>\n<h3>Method 1, by VS UI<\/h3>\n<ol>\n<li>Repeat the below steps for all list-type controls, for all forms in your project&#8230;<\/li>\n<li>If the list is already filling a control, say a split-panel, just make <strong>sure<\/strong> it uses <strong>DockStyle.Fill<\/strong> and not just anchors. And continue to next list<\/li>\n<li>Else note the alignments (or size and position) and any anchors of the list-control.<\/li>\n<li>Shrink the list substantially (to less than 50%, both directions).<\/li>\n<li>Add a dummy panel large enough to contain the list in current reduced size.<\/li>\n<li>Move the shrunken list into the panel.<\/li>\n<li>Adjust the panel-size to match the size&amp;position of the original list, this is fairly easy in the normal case where it was aligned with something.<\/li>\n<li>Apply the new panel the same anchors as the list had.<\/li>\n<li>Select the list, and select <strong>Dock<\/strong> = <strong>Fill<\/strong>, filling the whole new panel.<\/li>\n<\/ol>\n<h3><\/h3>\n<h3>Method 2, pure code<\/h3>\n<p>Doing it codewise may look hard, but if you got a large project you might want to make something that automates this, rather than doing it in the UI<\/p>\n<p>For each form-designer file do the following:<\/p>\n<p>For each list type object, replace code lik below (I did it in the designer, here the resulting difference&#8230;)<\/p>\n<p>replace<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n    this.checkedListBoxSaved = new System.Windows.Forms.CheckedListBox();\r\n<\/pre>\n<p>with<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n    checkedListBoxSaved = new CheckedListBox();\r\n    panelSavedList = new Panel();\r\n    panelSavedList.SuspendLayout();\r\n<\/pre>\n<p>and add the panel with the anchors and dimensions from the lits, and changethe list position, and mark it Dock.Fill<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n    \/\/ \r\n    \/\/ checkedListBoxSaved\r\n    \/\/ \r\n    this.checkedListBoxSaved.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top\r\n    | System.Windows.Forms.AnchorStyles.Bottom) \r\n    | System.Windows.Forms.AnchorStyles.Left) \r\n    | System.Windows.Forms.AnchorStyles.Right)));\r\n    this.checkedListBoxSaved.FormattingEnabled = true;\r\n    this.checkedListBoxSaved.HorizontalScrollbar = true;\r\n    this.checkedListBoxSaved.Location = new System.Drawing.Point(69, 58);\r\n    this.checkedListBoxSaved.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0);\r\n    this.checkedListBoxSaved.Name = &quot;checkedListBoxSaved&quot;;\r\n    this.checkedListBoxSaved.Size = new System.Drawing.Size(622, 94);\r\n    this.checkedListBoxSaved.TabIndex = 6;\r\n<\/pre>\n<p>with<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n    \/\/ \r\n    \/\/ checkedListBoxSaved\r\n    \/\/ \r\n    checkedListBoxSaved.Dock = DockStyle.Fill;\r\n    checkedListBoxSaved.FormattingEnabled = true;\r\n    checkedListBoxSaved.HorizontalScrollbar = true;\r\n    checkedListBoxSaved.Location = new Point(0, 0);\r\n    checkedListBoxSaved.Margin = new Padding(3, 0, 3, 0);\r\n    checkedListBoxSaved.Name = &quot;checkedListBoxSaved&quot;;\r\n    checkedListBoxSaved.Size = new Size(616, 130);\r\n    checkedListBoxSaved.TabIndex = 6;\r\n    \/\/ \r\n    \/\/ panelSavedList\r\n    \/\/ \r\n    panelSavedList.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;\r\n    panelSavedList.Controls.Add(checkedListBoxSaved);\r\n    panelSavedList.Location = new Point(71, 76);\r\n    panelSavedList.Name = &quot;panelSavedList&quot;;\r\n    panelSavedList.Size = new Size(616, 130);\r\n    panelSavedList.TabIndex = 14;\r\n<\/pre>\n<p>and where added to the parent change to adding the panel<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n    this.splitContainer1.Panel1.Controls.Add(this.checkedListBoxSaved);\r\n<\/pre>\n<p>with<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n    splitContainer1.Panel1.Controls.Add(panelSavedList);\r\n<\/pre>\n<p>and extend the declaration<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n    private System.Windows.Forms.CheckedListBox checkedListBoxSaved;\r\n<\/pre>\n<p>to<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n    private CheckedListBox checkedListBoxSaved;\r\n    private Panel panelSavedList;\r\n<\/pre>\n<p>and in the Form section insert a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n    panelSavedList.ResumeLayout(false);\r\n<\/pre>\n<p>And repeat&#8230;<\/p>\n<hr \/>\n<h2>Visual Studio long standing bug<\/h2>\n<p>Micrososoft at the least have admitted to their scale-bug, but I would much prefer that they fixed it!!!<br \/>\n<a href=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/09\/VS_Scale_Bug.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-4086\" src=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/09\/VS_Scale_Bug.png\" alt=\"\" width=\"594\" height=\"123\" srcset=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/09\/VS_Scale_Bug.png 594w, https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/09\/VS_Scale_Bug-300x62.png 300w, https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/09\/VS_Scale_Bug-150x31.png 150w\" sizes=\"auto, (max-width: 594px) 100vw, 594px\" \/><\/a><\/p>\n<p>It has been like this for years, the <strong>VS2022<\/strong> 17.8 <span style=\"text-decoration: underline;\"><strong>still<\/strong><\/span> shows this warning if the primary monitor is NOT at 100%. And they <span style=\"text-decoration: underline;\"><strong>still<\/strong><\/span> do not make new form app scalable by default&#8230;.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As posted in a rather old post, many apps have problems with windows scaling. See these screen-dumps from Windows lengthy guide that was found here With previous versions of .Net Framework this have been somewhat cumbersome, see my post here. But with .Net 8.0 WinForms (well also 7.0 and 6.0), and Windows 11 (or newer [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15],"tags":[],"class_list":["post-5502","post","type-post","status-publish","format-standard","hentry","category-pc-and-code-samples"],"_links":{"self":[{"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/posts\/5502","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5502"}],"version-history":[{"count":1,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/posts\/5502\/revisions"}],"predecessor-version":[{"id":5505,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/posts\/5502\/revisions\/5505"}],"wp:attachment":[{"href":"https:\/\/eskerahn.dk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5502"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5502"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5502"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}