Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,98 +1,87 @@
# Support for application runtime configuration in WinForms applications
# Support for application runtime configuration in Windows Forms applications

## Overview
\
NET Winforms applications currently have [limited application

.NET Windows Forms applications currently have limited [application
configurations](https://aka.ms/applicationconfiguration) capabilities that are defined via MSBuils properties and are emitted into source code using source
generators at compile time. This document outlines expansion of those application-wide configurations further to cover runtime config options for .NET Windows Forms applications.

## Runtime configuration in .NET Framework applications
\
.NET Framework Windows Forms applications use app.config to define runtime configurations and application-wide settings. Following are the various sections in the app.config that define application's runtime behavior.
## .NET Framework runtime configuration

.NET Framework Windows Forms applications use `app.config` to define runtime configurations and application-wide settings. The following are the various sections in the app.config that define the application's runtime behavior.

### AppContext switches

These settings are used to opt-in or opt-out of a particular feature from WinForms runtime. Please refer to the [AppContext Switches](https://docs.microsoft.com/dotnet/framework/configure-apps/file-schema/runtime/appcontextswitchoverrides-element) documentation for more information
These settings are used to opt-in or opt-out of a particular feature from Windows Forms runtime. Refer to the [AppContext Switches](https://docs.microsoft.com/dotnet/framework/configure-apps/file-schema/runtime/appcontextswitchoverrides-element) documentation for more information.

```XML
```xml
<configuration>
<runtime>
<AppContextSwitchOverrides value="Switch.System.Globalization.NoAsyncCurrentCulture=true" />
</runtime>
<runtime>
<AppContextSwitchOverrides value="Switch.System.Globalization.NoAsyncCurrentCulture=true" />
</runtime>
</configuration>
```
### System.Windows.Forms.ApplicationConfigurationSection
### `System.Windows.Forms.ApplicationConfigurationSection` section

This was introduced in .NET Framework 4.7 and is primarily used by Winforms runtime to enable high Dpi and other accessibility improvements. Please refer to the [ApplicationConfigurationSection](https://docs.microsoft.com/dotnet/framework/configure-apps/file-schema/winforms/windows-forms-add-configuration-element) documentation for more information.
This was introduced in .NET Framework 4.7, and it is primarily used by Windows Forms runtime to enable high DPI and other accessibility improvements. Please refer to the [ApplicationConfigurationSection](https://docs.microsoft.com/dotnet/framework/configure-apps/file-schema/winforms/windows-forms-add-configuration-element) documentation for more information.

```XML
```xml
<configuration>
<System.Windows.Forms.ApplicationConfigurationSection>
...
<!-- content -->
</System.Windows.Forms.ApplicationConfigurationSection>
</configuration>
```

### App settings from Settings designer/editor page

Unlike above, these settings are used by the user application. These are commonly defined via the Settings designer in Visual Studio, which intern serialize them into the app.config file. Please refer to the [Application Settings](https://docs.microsoft.com/dotnet/desktop/winforms/advanced/using-application-settings-and-user-settings?view=netframeworkdesktop-4.8) documentation for more information.
```XML
<userSettings>
<WinFormsApp2.Properties.Settings>
<setting name="ButtonName" serializeAs="String">
<value>LocalButton</value>
</setting>
</WinFormsApp2.Properties.Settings>
</userSettings>
<applicationSettings>
<WinFormsApp2.Properties.Settings>
<setting name="BoxName" serializeAs="String">
<value>LocalBox</value>
</setting>
</WinFormsApp2.Properties.Settings>
</applicationSettings>
### Application-wide and user-specific settings

These are commonly defined via the Settings designer in Visual Studio, which serializes those into `app.config`. Refer to the [Application Settings](https://docs.microsoft.com/dotnet/desktop/winforms/advanced/using-application-settings-and-user-settings) documentation for more information.

```xml
<userSettings>
<WinFormsApp2.Properties.Settings>
<setting name="ButtonName" serializeAs="String">
<value>LocalButton</value>
</setting>
</WinFormsApp2.Properties.Settings>
</userSettings>

<applicationSettings>
<WinFormsApp2.Properties.Settings>
<setting name="BoxName" serializeAs="String">
<value>LocalBox</value>
</setting>
</WinFormsApp2.Properties.Settings>
</applicationSettings>
```


## Winforms runtime configuration in .NET applications
\
app.config has limited support in .NET due to performance and reliability reasons. .NET runtime and other .NET teams use runtimeconfig.json to define .NET runtime configurations and appsettings.json to define application-level settings. In this proposal, we are leveraging runtimeconfig.json to define WinForms runtime configurations.
## .NET 7+ runtime configuration

`app.config` has limited support in .NET due to performance and reliability reasons. .NET runtime and other .NET teams use `runtimeconfig.json` to define .NET runtime configurations and `appsettings.json` to define application-level settings. In this proposal, we are leveraging `runtimeconfig.json` to define Windows Forms runtime configurations.

While this proposal is focusing on providing an alternative solution for existing configuration sections `AppContextSwitchOverrides` and `System.Windows.Forms.ApplicationConfigurationSection` that are primarily used for specifying feature flags impacting winforms runtime behavior, we will be looking into alternatives for `Application Settings` that doesn't require app.config in the upcoming releases of .NET.
While this proposal is focusing on providing an alternative solution for existing configuration sections `AppContextSwitchOverrides` and `System.Windows.Forms.ApplicationConfigurationSection` that are primarily used for specifying feature flags impacting Windows Forms runtime behavior, we will be looking into alternatives for `Application Settings` that doesn't require `app.config` in the future releases of .NET.


### Goals:

- A replacement for `AppContextSwitchOverrides` and `System.Windows.Forms.ApplicationConfigurationSection` of app.config.
- Users should be able to update/modify Winforms applications runtime configurations without recompiling the application.
- The existing applications should be able to seamlessly upgrade to the new configuration model when targeting the latest .NET runtiem.
- The existing [application configuration MSBuild properties](https://aka.ms/applicationconfiguration) continue to work.
- A replacement for `AppContextSwitchOverrides` and `System.Windows.Forms.ApplicationConfigurationSection` of `app.config`.
- Users should be able to update/modify Windows Forms applications runtime configurations without recompiling the application.
- The existing applications should be able to seamlessly upgrade to the new configuration model when targeting .NET 7+ runtime.
- The existing [application configuration MSBuild properties](https://aka.ms/applicationconfiguration) continue to work.

### Out of scope:

App settings that are serialized by the Settings designer/editor page. Applications should continue to use the current model.
- Dynamic/real-time (re-)loading of configuration values from runtimeconfig.json.
- Unification of comple/runtime configurations into one place, runtimeconfig.json.
- App settings that are serialized by the Settings designer/editor page. Applications should continue to use the current model.
- Dynamic/real-time (re-)loading of configuration values from `runtimeconfig.json`.
- Unification of comple/runtime configurations into one place, e.g., `runtimeconfig.json`.


#### Syntax of runtimeConfig.template.Json.

```xml
{
"configProperties": {
"System.Globalization.UseNls": true,
"System.Net.DisableIPv6": true,
"System.GC.Concurrent": false,
"System.Threading.ThreadPool.MinThreads": 4,
"System.Threading.ThreadPool.MaxThreads": 25
}
}
```

Windows Forms switches will be added to [`configProperties` section](https://docs.microsoft.com/dotnet/core/runtime-config) with the rest of the .NET switches. To avoid name conflics Windows Forms specific switches will be prefixed with `System.Windows.Forms`:
### Design proposal

Windows Forms switches will be added to **`runtimeConfig.template.json`** to the [`configProperties` section](https://docs.microsoft.com/dotnet/core/runtime-config) with the rest of the .NET switches. To avoid name conflics Windows Forms specific switches will be prefixed with `System.Windows.Forms`:

```xml
```json
{
"configProperties": {
"System.Globalization.UseNls": true,
Expand All @@ -101,21 +90,23 @@ Windows Forms switches will be added to [`configProperties` section](https://doc
"System.Threading.ThreadPool.MinThreads": 4,
"System.Threading.ThreadPool.MaxThreads": 25,

<!-- Windows Forms specific switches -->
// Windows Forms specific switches
"System.Windows.Forms.ScaleTopLevelFormMinMaxSizeForDpi": true,
"System.Windows.Forms.<CustomEnumProperty>": "EnumValue"
"System.Windows.Forms.CustomEnumProperty": "EnumValue"
}
}
```


### Reading Winforms runtime configurations:
\
When a project is [built](https://docs.microsoft.com/en-us/dotnet/core/runtime-config/#runtimeconfigjson), an _[appname].runtimeconfig.json_ file is generated in the output directory. If a _runtimeconfig.template.json_ file exists in the same folder as the project file, any configuration options it contains are inserted into the _[appname].runtimeconfig.json_ file.
This proposal focuses primarily on enabling the runtime configurations for WinForms applications. A support for analyzers and source generators may be considered in the future .NET releases. We will revisit this implementation to improve user-experience further, as we make progress.
For example, the content of _[appname].runtimeconfig.json_ generated from above content:
#### Reading Windows Forms runtime configurations

When a project is [built](https://docs.microsoft.com/dotnet/core/runtime-config/#runtimeconfigjson), an `[appname].runtimeconfig.json` file is generated in the output directory. If a `runtimeconfig.template.json` file exists in the same folder as the project file, any configuration options it contains are inserted into the `[appname].runtimeconfig.json` file.

This proposal focuses primarily on enabling the runtime configurations for Windows Forms applications. A support for analyzers and source generators may be considered in the future .NET releases. We will revisit this implementation to improve user-experience further, as we make progress.

For example, the content of `[appname].runtimeconfig.json` generated from above content:

```XML
```json
{
"runtimeOptions": {
"tfm": "net7.0",
Expand All @@ -135,29 +126,31 @@ For example, the content of _[appname].runtimeconfig.json_ generated from above
"System.GC.Concurrent": false,
"System.Threading.ThreadPool.MinThreads": 4,
"System.Threading.ThreadPool.MaxThreads": 25,

"System.Windows.Forms.ScaleTopLevelFormMinMaxSizeForDpi": true,
"System.Windows.Forms.<StringProperty>": "string"
"System.Windows.Forms.<CustomProperty>": "CustomValue"
"System.Windows.Forms.StringProperty": "string",
"System.Windows.Forms.CustomProperty": "CustomValue"
}
}
}
```

The target framework information added to _[appname].runtimeconfig.json_ file always match with the application target framework irrespective of runtime/SDK installed on the machine or runtime used ([roll-forward scenarios](https://docs.microsoft.com/dotnet/core/versions/selection#framework-dependent-apps-roll-forward)) by the Windows Forms application.
The target framework information added to `[appname].runtimeconfig.json` file always match with the application target framework irrespective of runtime/SDK installed on the machine or runtime used ([roll-forward scenarios](https://docs.microsoft.com/dotnet/core/versions/selection#framework-dependent-apps-roll-forward)) by the Windows Forms application.

#### .NET runtime support in reading `runtimeconfig.json`

### .NET runtime support in reading runtimeconfig.json:
\
WinForms is leveraging the [support](https://github.com/dotnet/runtime/blob/5098d45cc1bf9649fab5df21f227da4b80daa084/src/native/corehost/runtime_config.cpp) provided by .NET runtime in reading the _[appname].runtimeconfig.json_ file, and thus get all plumbing required for various hosting and environment scenarios.
WinForms adds the following wrapper on top of .NET runtime implementation to improve the performance by caching configuration options, and also by defining custom defaults values for the WinForms specific feature flags. This wrapper is very similar to the [.NET runtime's implementation](https://github.com/dotnet/runtime/blob/04dac7b0fede29d44f896c5fd793754f83974175/src/libraries/System.Private.CoreLib/src/System/AppContextConfigHelper.cs).
The below example illustrates how to define `ScaleTopLevelFormMinMaxSizeForDpi` feature flag, opt-in by default, if an application is running on Windows 10 and is targeting .NET 8:
The Windows Forms runtime is leveraging the [support](https://github.com/dotnet/runtime/blob/5098d45cc1bf9649fab5df21f227da4b80daa084/src/native/corehost/runtime_config.cpp) provided by .NET runtime in reading `[appname].runtimeconfig.json` file, and thus get all plumbing required for various hosting and environment scenarios. The Windows Forms runtime adds the following wrapper on top of .NET runtime implementation to improve the performance by caching configuration options, and also by defining custom defaults values for the Windows Forms specific feature flags. This wrapper is very similar to the [.NET runtime's implementation](https://github.com/dotnet/runtime/blob/04dac7b0fede29d44f896c5fd793754f83974175/src/libraries/System.Private.CoreLib/src/System/AppContextConfigHelper.cs).

The below example illustrates how to define `ScaleTopLevelFormMinMaxSizeForDpi` feature flag, opt-in by default, for an application targeting .NET 8 or later running on Windows 10:

```cs
internal static partial class LocalAppContextSwitches
{
private const string ScaleTopLevelFormMinMaxSizeForDpiSwitchName = "System.Windows.Forms.ScaleTopLevelFormMinMaxSizeForDpi";

private static readonly FrameworkName? s_targetFrameworkName = GetTargetFrameworkName();
private static readonly bool s_isNetCoreApp = (s_targetFrameworkName?.Identifier) == ".NETCoreApp";

private const string ScaleTopLevelFormMinMaxSizeForDpiSwitchName = "System.Windows.Forms.ScaleTopLevelFormMinMaxSizeForDpi";
private static int s_scaleTopLevelFormMinMaxSize;

public static bool ScaleTopLevelFormMinMaxSizeForDpi
Expand Down Expand Up @@ -226,9 +219,7 @@ internal static partial class LocalAppContextSwitches
```


WinForms runtime then uses the static `LocalAppContextSwitches` class to access the runtime configurations. The below sample demonstrates how to access the feature switch `System.Windows.Forms.ScaleTopLevelFormMinMaxSizeForDpi`:

Ex: Use of feature switch in ScaleContainerForDpi() method in ContainerControl.cs to scale Min/Max size of the Container.
The Windows Forms runtime then uses the static `LocalAppContextSwitches` class to access the runtime configurations. The below sample demonstrates the use of the feature switch `System.Windows.Forms.ScaleTopLevelFormMinMaxSizeForDpi` to scale min/max size of a container control:

```cs
internal void ScaleContainerForDpi(int deviceDpiNew, int deviceDpiOld, Rectangle suggestedRectangle)
Expand All @@ -252,7 +243,7 @@ internal void ScaleContainerForDpi(int deviceDpiNew, int deviceDpiOld, Rectangle
// WM_DPICHANGED event. Failing to apply SuggestedRectangle will result in a circular WM_DPICHANGED
// events on the control.

// Note: SuggestedRectangle supplied by WM_DPICHANGED event is Dpi (not Font) scaled. if top-level window is
// Note: SuggestedRectangle supplied by WM_DPICHANGED event is DPI (not Font) scaled. if top-level window is
// Font scaled, we might see deviations in the expected bounds and may result in adding Scrollbars (horizontal/vertical)
User32.SetWindowPos(
new HandleRef(this, HandleInternal),
Expand Down Expand Up @@ -293,7 +284,7 @@ internal void ScaleContainerForDpi(int deviceDpiNew, int deviceDpiOld, Rectangle
}
finally
{
// We want to perform layout for dpi-changed high Dpi improvements - setting the second parameter to 'true'
// We want to perform layout for DPI-changed high DPI improvements - setting the second parameter to 'true'
ResumeAllLayout(this, true);
_isScaledByDpiChangedEvent = false;
}
Expand Down