Updated Modern Code Generation for WinForm’s InitializeComponent

Klaus Loeffelmann

When you create a WinForms Form or User Control with the WinForms Designer inVisual Studio, it does not have a special definition or file format like XML orHTML to represent the user interface. From the beginning, the only formatWinForms has used is program code. A Form or User Control defined in a WinFormsVisual Basic project gets saved into VB Code. In a C# project, that is C# code.That code will be placed in a dedicated Designer file, which sits behind theactual Form code file and contains the code to control the UI.

Screenshot of a WinForms Form code-behind Designer file in the Solution Explorer

When your Form or User Control needs to be opened again inside of the WinFormsDesigner, that code is interpreted and – based on the resulting object graph –the Form/User Control gets recreated in the Designer. That is the reason we callthe process of saving a Form CodeDOM serialization.CodeDOMhere refers to an object model (the Code Document Object Model)which allows the developer to define aspects of a program or part of a programby objects of certain types.

Screenshot of a WinForms Form with a Button and the respective generated code in InitializeComponent

While CodeDOM is pretty flexible, can be extended comparatively easily andsupports more languages than Visual Basic or C#, generating a CodeDOM graph froman existing code file is a completely different beast. And although CodeDOMhas the option to actually write a code file for particular languages throughits existing Compilerimplementation,the style of that resulting code is the same it has been from the beginning of.NET Framework, which is in many cases no longer up to current coding standards.

In WinForms, when you design a Form, everything which is relevant is generatedin one method per Form or User Control. That method (amongst a bit ofinfrastructure and initialization code in addition) is calledInitializeComponent.

This method is called by a Form’s constructor unconditionally. In the C# casethat’s pretty obvious, where a new Form which you add to your project, alwayshas that constructor and the required call:

    public partial class Form1 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }
    }

In Visual Basic, if you don’t add a constructor Sub New explicitly, the VisualBasic compiler inserts the call to InitializeComponent automatically in thebackground. If you however add a constructor to the code file, the editor alsoinserts the call to InitializeComponent in the VB code:

Public Class Form1

    Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.

    End Sub
End Class

Note that in Visual Basic, the Inherits statement, which lets your new Formclass inherit from the System.Windows.Forms.Form base class, in contrast to C#is only part of the Designer code behind file. In VB it’s also sufficient for apartial class only to state the Partialkeywordin one of the partial class’ code files. That is the reason why a Visual BasicWinForms Form code file does not contain anything but the Form’s classdefinition by default.

Up to recently, the WinForms Designer used the CodeModelInterfaceto interpret source code of the different programming language to build therequired internal CodeDOM graph for the Designer to hold a Form’s or a UserControl’s definition. But we changed that.

Enter Roslyn

WinForms introduced with Visual Studio 2022 version 17.5 a modernized way toread and generate the code for InitializeComponent for the WinFormsOut-of-ProcessDesigner.It does so by using the APIs of the .NET CompilerPlatform – better knowas the Roslyn SDK – for all related tasks. The Roslyn Compiler is a set ofopen-source compilers and code analysis APIs for .NET languages. It allowsdevelopers to write, analyze, and manipulate code in C# and Visual Basic .NETusing modern language features. It also provides a rich set of diagnostic andcode refactorings to improve code quality and developer productivity. It is thegold standard and the current best practice for code generation in C# and VB.And, since it’s the same tooling that is used for compilation and build purposesinside Visual Studio for any C# or Visual Basic project its code generationresult is completely in-line with current coding standards.

Also, since the Roslyn compiler provides certain APIs to not know only about thecorrect syntax of a particular statement, command or method, but also about thesemantics of a code block already at WinForms design time, the WinForms designercan point out potential problems with the code inside of InitializeComponentfar earlier and more precisely than before. So, it not only knows when you’vespelled ‘Buttne’ wrong – it would also know that a variable with a typo definedinside of InitializeComponent would be an unknown symbol, and be able to pointthat out.

But there is a series of additional benefits:

  • Previously, the CodeModel based building up of the CodeDOM could only run onthe UI Thread. That was not only a blocking operation, it couldn’t utilize thefull potential of a modern, multi-core processor. Using the Roslyn compiler,we will be able to optimize the building process over time by usingparallelization.
  • The old system didn’t have an easy way to interpret more recently introducedlanguage features. Using Roslyn, we will have the option to introduce languagefeatures like NameOf to generate more robust code, especially for databinding purposes. In addition it opens up the path to more complex codegeneration inside of InitializeComponent in the future, which will help tooptimize and equalize code generation for HighDPI scenarios generated onmachines with different HighDPI settings.
  • The Roslyn compiler honors many aspects of .editorconfigconfigurations,so the code generated in InitializeComponent is close to what you and yourteam are enforcing as your coding standards by custom .editorconfigdefinitions.

That all said, there are a couple of coding elements which are fundamentallydifferent than before. The omission of this in C# or Me in Visual Basic isone of such an example. The following screenshot shows the difference in thecode generation with Roslyn for a Button in InitializeComponent:

Screenshot of a diff of InitializeComponent with the classic and the new Roslyn-based code generation

If you’re interested in a more technical background about moving the codegeneration in the WinForms designer to Roslyn or how to configure theInitializeComponent code generation with .editorconfig, take a look at thistechnical article in the WinFormsrepowho points out all those things in greater detail.

Feedback about the subject matter is really important to us, so please let usknow your thoughts or ideas you might have around WinForms code generations inthe comments. If you have suggestions around the WinForms Designer or think youfound a bug, feel free to file new issues in the WinForms Githubrepo.

Happy designing and coding!