Skip to content

Latest commit

 

History

History
199 lines (160 loc) · 8.44 KB

modernization-of-code-behind-in-oop-designer.md

File metadata and controls

199 lines (160 loc) · 8.44 KB

Modernization of code-behind in WinForms OOP designer

Background

The WinForms designer saves visual representations of user controls or forms on the design surface to code-behind file (Form1.Designer.cs or Form1.Designer.vb). Control instances that developer sees on the design surface are represented as a CodeDOM tree (see Using the CodeDOM), that tree is saved to the generated code-behind file in the InitializeComponent method. We call this feature design time serialization.

Previously COM-based CodeModel interface was used to write code into the editor's buffer as well as to generate CodeDOM tree from the source code when designer was loading. This COM object was accessible from the main thread only, which prevented parallelization in source code processing. Also, being more than 20 years old, it did not support some of the newer language features, for example the nameof() expression.

What's different

Starting with Visual Studio 2022 v17.5 Preview 3, the WinForms out-of-process designer uses Roslyn for design time serialization.

In Preview 3, designer serializes more modern code into InitializeComponent. We have worked hard to ensure that you can take projects between this version of Visual Studio and earlier versions. Someday in the future, we may build on the improvements in our serialization to enable performance enhancements and use newer language features. These potential future investments may break the backwards compatibility with earlier VS versions - but we will be sure to inform our developers well ahead of such changes so that you can prepare.

Why change it

  • The use of Roslyn unblocks multithreaded serialization and deserialization to solve performance delays in designer load and unload scenarios.
  • Code-behind generation now respects .editorconfig settings and thus matches code style in the source files.
  • If implicit usings feature is enabled in project properties, global namespaces are not repeated in type names.
  • Further modernization of code-behind is unblocked.
  • Newly generated code will work with modern Roslyn analyzers.

How the new code looks like

While InitializeComponent method is meant for the designer use only, and manual modifications to this method are not supported, developers might see some unexpected changes in Form1.Designer.cs and Form1.designer.vb files when comparing versions in the source control tools. This is a one-time code churn. Once the designer file is regenerated in v17.5 Preview 3 or newer and saved, designer will generate code only for controls modified in the design surface.

For example, InitializeComponent method for a form with a button will be modified like this on the first save:

Comparison of Form1.Designer.cs as generated by Visual Studio 2022 v17.4 or earlier against the same file generated by v17.5 Preview 3

Code generated by VisualStudio v17.4 or earlier:

private void InitializeComponent()
{
    this.button1 = new System.Windows.Forms.Button();
    this.SuspendLayout();
    // 
    // button1
    // 
    this.button1.Location = new System.Drawing.Point(58, 60);
    this.button1.Name = "button1";
    this.button1.Size = new System.Drawing.Size(136, 69);
    this.button1.TabIndex = 0;
    this.button1.Text = "button1";
    this.button1.UseVisualStyleBackColor = true;
    // 
    // Form1
    // 
    this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 25F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.ClientSize = new System.Drawing.Size(270, 208);
    this.Controls.Add(this.button1);
    this.Name = "Form1";
    this.Text = "Form1";
    this.ResumeLayout(false);

}

Code generated by VisualStudio v17.5 Preview 3, or newer:

private void InitializeComponent()
{
    button1 = new Button();
    SuspendLayout();
    // 
    // button1
    // 
    button1.Location = new Point(58, 60);
    button1.Name = "button1";
    button1.Size = new Size(136, 69);
    button1.TabIndex = 0;
    button1.Text = "button1";
    button1.UseVisualStyleBackColor = true;
    // 
    // Form1
    // 
    AutoScaleDimensions = new SizeF(10F, 25F);
    AutoScaleMode = AutoScaleMode.Font;
    ClientSize = new Size(270, 208);
    Controls.Add(button1);
    Name = "Form1";
    Text = "Form1";
    ResumeLayout(false);
}

Support for code style settings

The above code was generated for default C# code style settings, thus this access qualifier was removed in the Preview 3 version.

To preserve legacy code generation style in InitializeComponent method, set style preferences to legacy values in .editorconfig file.

[*.designer.cs]

# this. preferences
dotnet_style_qualification_for_event = true
dotnet_style_qualification_for_field = true
dotnet_style_qualification_for_method = true
dotnet_style_qualification_for_property = true

[*.vb]

# Me. preferences
dotnet_style_qualification_for_event = true
dotnet_style_qualification_for_field = true
dotnet_style_qualification_for_method = true
dotnet_style_qualification_for_property = true

Support for implicit usings property

Namespace names were removed from the type names in Preview 3 version. They are not needed for type resolution because design time serialization looks up the implicit usings property, available in C# starting with .NET 6 for C#, from the project file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWindowsForms>true</UseWindowsForms>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

</Project>

When implicit usings property is disabled in the C# project file, namespace names are generated.

Similar changes are present in Visual Basic

Comparison of Form1.Designer.vb as generated by Visual Studio 2022 v17.4 or earlier against the same file generated by v17.5 Preview 3

Code generated by VisualStudio v17.4 or earlier:

<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
    Me.Button1 = New System.Windows.Forms.Button()
    Me.SuspendLayout()
    '
    'Button1
    '
    Me.Button1.Location = New System.Drawing.Point(58, 60)
    Me.Button1.Name = "Button1"
    Me.Button1.Size = New System.Drawing.Size(136, 69)
    Me.Button1.TabIndex = 0
    Me.Button1.Text = "Button1"
    Me.Button1.UseVisualStyleBackColor = True
    '
    'Form1
    '
    Me.AutoScaleDimensions = New System.Drawing.SizeF(10.0!, 25.0!)
    Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
    Me.ClientSize = New System.Drawing.Size(270, 208)
    Me.Controls.Add(Me.Button1)
    Me.Name = "Form1"
    Me.Text = "Form1"
    Me.ResumeLayout(False)

End Sub

Code generated by VisualStudio v17.5 Preview 3, or newer:

<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
    Button1 = New Button()
    SuspendLayout()
    ' 
    ' Button1
    ' 
    Button1.Location = New Point(58, 60)
    Button1.Name = "Button1"
    Button1.Size = New Size(136, 69)
    Button1.TabIndex = 0
    Button1.Text = "Button1"
    Button1.UseVisualStyleBackColor = True
    ' 
    ' Form1
    ' 
    AutoScaleDimensions = New SizeF(10.0F, 25.0F)
    AutoScaleMode = AutoScaleMode.Font
    ClientSize = New Size(270, 208)
    Controls.Add(Button1)
    Name = "Form1"
    Text = "Form1"
    ResumeLayout(False)
End Sub

Summary

New code-behind in WinForm apps looks differently because it follows code style settings defined for the project. Under the covers, the design time serialization was redesigned in order to move from EnvDTE.CodeModel to Roslyn. We are planning to build upon this foundation in the future Visual Studio releases. Code-behind generated by the older releases will load into v17.5 Preview 3 or later releases and will work as expected. Code-behind generated by v17.5 Preview 3 can be loaded in the previous releases of the Visual Studio, but we are considering generating modern code that might not be supported by the previous versions of deserializer.