State of the Windows Forms Designer for .NET Applications

Klaus Loeffelmann

For the last several Visual Studio release cycles, the Windows Forms (WinForms) Team has beenworking hard to bring the WinForms designer for .NET applications to parity withthe .NET Framework designer. As you may be aware, a new WinFormsdesigner was needed to support .NET Core 3.1 applications, and later .NET 5+applications. The work required a near-complete rearchitecting of the designer,as we responded to the differences between .NET and the .NET Framework basedWinForms designer everyone knows and loves. The goal of this blog post is togive you some insight into the new architecture and what sorts of changes wehave made. And of course, how those changes may impact you as you create customcontrols and .NET WinForms applications.

After reading this blog post you will be familiar with the underlying problemsthe new WinForms designer is meant to solve and have a high-level understandingof the primary components in this new approach. Enjoy this look into thedesigner architecture and stay tuned for future blogs!

A bit of history

WinForms was introduced with the first version of .NET and Visual Studio in2001. WinForms itself can be thought of as a wrapper around the complex Win32API. It was built so that enterprise developers didn’t need to be ace C++developers to create data driven line-of-business applications. WinForms wasimmediately a hit because of its WYSIWYG designer where even novice developerscould throw together an app in minutes for their business needs.

Until we added a support for .NET Core applications there was only a singleprocess, devenv.exe, that both the Visual Studio environment and the applicationbeing designed ran within. But .NET Framework and .NET Core can’t both runtogether within devenv.exe, and as a result we had to take the designer out ofprocess, thus we called the new designer – WinForms Out of Process Designer (orOOP designer for short).

Where are we today?

While we aimed at complete parity between the OOP designer and the .NETFramework designer for the release of Visual Studio 2022,there are still a few issues on our backlog. That said, the OOP designer in its current iterationalready has most of the significant improvements at all important levels:

  • Performance: Starting with Visual Studio 2019 v16.10, the performance ofthe OOP designer has been improved considerably. We’ve worked on reducingproject load times and improved the experience of interacting with controlson the design surface, like selecting and moving controls.
  • Databinding Support: WinForms in Visual Studio 2022 brings astreamlined approach for managing Data Sources in the OOP designer with theprimary focus on Object Data Sources. This new approach is unique to theOOP designer and .NET based applications.
  • WinForms Designer Extensibility SDK: Due to the conceptional differencesbetween the OOP designer and the .NET Framework designer, providers for 3rdparty controls for .NET will need to use a dedicated WinForms Designer SDKto develop custom Control Designers which run in the context of the OOPdesigner. We have published a pre-release version of the SDK last month as aNuGet package, and you can download ithere. Wewill be updating this package to make it provide IntelliSense in the firstquarter of 2022. There will also be a dedicated blog post about the SDK inthe coming weeks.

A look under the hood of the WinForms designer

Designing Forms and UserControls with the WinForms designer holds a couple ofsurprises for people who look under the hood of the designer for the first time:

  1. The designer doesn’t “save” (serialize) the layout in some sort of XML orJSON. It serializes the Forms/UserControl definition directly to code – inthe new OOP designer that is either C# or Visual Basic .NET. When the userplaces a Button on a Form, the code for creating this Button and assigningits properties is generated into a method of the Form called`InitializeComponent`. When the Form is opened in the designer, the`InitializeComponent` method is parsed and a shadow .NET assembly isbeing created on the fly from that code. This assembly contains anexecutable version of `InitializeComponent` which is loaded in the contextof the designer. `InitializeComponent` method is then executed, and thedesigner is now able to display the resulting Form with all its controldefinitions and assigned properties. We call this kind of serializationCode Document Object Model serialization, or CodeDOM serialization forshort. This is the reason, you shouldn’t edit `InitializeComponent`directly: the next time you visually edit something on the Form and save it,the method gets overwritten, and your edits will be lost.
  2. All WinForms controls have two code layers to them. First there is the codefor a control that runs during runtime, and then there is a controldesigner, which controls the behavior at design-time. The control designerfunctionality for each control is not implemented in the designeritself. Rather, a dedicated control designer interacts with Visual Studioservices and features.Let’s look at `SplitContainer` as an example:An animated gif of the various control designer features of the SplitContainer control

    The design-time behavior of the SplitContainer is implemented in anassociated designer, in this case the `SplitContainerDesigner`. This classprovides the key functionality for the design-time experience of the`SplitContainer` control:

    • The way the outer Panel and the inner Panels get selected on mouseclick.
    • The ability of the splitter bar to be moved to adjust the sizes of theinner panels.
    • To provide the Designer Action Glyph, which allows a developer using thecontrol to manage the Designer Actions through the respective short cutmenu.

When we decided to support apps built on .NET Core 3.1 and .NET 5+ inthe original designer we faced a major challenge. Visual Studio isbuilt on .NET Framework but needs to round-trip the designer code by serializingand deserializing this code for projects which target a different runtime. While, with somelimitations, you can run .NET Framework based types in a .NET Core/.NET 5+applications, the reverse is not true. This problem is known as “typeresolution problem”. A great example of this can be seen in the TextBoxcontrol: in .NET Core 3.1 we added a new property called `PlaceholderText`. In.NET Framework that property does not exist on `TextBox`. So, if the .NETFramework based CodeDom Serializer (running in Visual Studio) encountered the`PlaceholderText` property it would fail.

In addition, a Form with all its controls and components renders itself in the designerat design time. Therefore, the code that instantiates the form and shows it in theDesigner window must also be executed in .NET and not in .NET Framework, so thatnewer properties available only in .NET also reflect the actual appearance andbehavior of the controls, components, and ultimately the entire Form or UserControl.

Because we plan to continue innovating and adding new features in the future,the problem only grows over time. So we had to design a mechanism that supportedsuch cross-framework interactions between the WinForms designer and Visual Studio.

Enter the DesignToolsServer

Developers need to see their Forms in the designer looking precisely the way itwill at runtime (WYSIWYG). Whether it is `PlaceholderText` property from theearlier example, or the layout of a form with the desired default font – theCodeDom serializer must run in the context of the version of .NET the project istargeting. And we naturally can’t do that, if the CodeDom serialization isrunning in the same process as Visual Studio. To solve this, we run the designerout-of-process (hence the moniker Out of Process Designer) in a new.NET (Core) process called DesignToolsServer. The DesignToolsServer processruns the same version of .NET and the same bitness (x86 or x64) as yourapplication.

Now, when you double-click on a Form or a UserControl in Solution Explorer,Visual Studio’s designer loader service determines the targeted .NET version andlaunches a DesignToolsServer process. Then the designer loader passes the codefrom the `InitializeComponent` method to the DesignToolsServer process whereit can now execute under the desired .NET runtime and is now able to deal withevery type and property this runtime provides.

Screenshot showing each Form of projects with different bitness and the entries of DesignToolsServer in TaskManager

While going out of process solves the type-resolution-problem , it introduces afew other challenges around the user interaction inside Visual Studio. Forexample, the Property Browser, which is part of Visual Studio (and thereforealso .NET Framework based). It is supposed to show the .NET Types, but it can’tdo this for the same reasons the CodeDom serializer cannot (de)serialize .NETtypes.

Custom Property Descriptors and Control Proxies

To facilitate interaction with Visual Studio, the DesignToolsServer introducesproxy classes for the components and controls on a form which are created in theVisual Studio process along with the real components and controls on the form inthe DesignToolsServer.exe process. For each one on the form, an object proxy iscreated. And while the real controls live in the DesignToolsServer process, theobject proxy instances live in the client – the Visual Studio process. If younow select an actual .NET WinForms control on the form, from Visual Studio’sperspective an object proxy is what gets selected. And that object proxy doesn’thave the same properties of its counterpart control on the server side. Itrather maps the control’s properties 1:1 with custom proxy propertydescriptors through which Visual Studio can talk to the server process.

So, clicking now on a button control on the form, leads to the following(somewhat simplified) chain of events to get the properties to show in theProperty Browser:

  1. The mouse click happens on special window in the Visual Studio process,called the Input Shield. It acts like a sneeze guard, if you will, and ispurely to intercept the mouse messages which it sends to theDesignToolsServer process.
  2. The DesignToolsServer receives the mouse click and passes it to theBehavior Service. The Behavior Service finds the control and passes it tothe Selection Service that takes the necessary steps to select thatcontrol.
  3. In that process, the Behavior Service has also located the correlatingControl Designer, and initiates the necessary steps to let that ControlDesigner render whatever adorners and glyphs it needs to render for thatcontrol. Think of the Designer Action Glyphs or the special selectionmarkers from the earlier SplitPanel example.
  4. The Selection Service reports the control selection back to Visual Studio’sSelection Service.
  5. Visual Studio now knows, what object proxy maps to the selected control inthe DesignToolsServer. The Visual Studio’s selection service selects thatobject proxy. This again triggers an update for the values of the selectedcontrol (object proxy) in the Property Browser.
  6. The Property Browser in turn now queries the Property Descriptors of theselected object proxy which are mapped to the proxy descriptors of theactual control in the DesignToolsServer’s process. So, for each property theProperty Browser needs to update, the Property Browser calls GetValue onthe respective proxy Property Descriptor, which leads to a cross-processcall to the server to retrieve the actual value of that control’sproperty, which is eventually displayed in the Property Browser.

Diagram which shows the process chain how Visual Studio communicates with the DesignToolsServer

Compatibility of Custom Controls with the DesignToolsServer

With the knowledge of these new concepts, it is obvious that adjustments toexisting custom control designers targeting .NET will be required. The extent towhich the adjustments are necessary depends purely on how extensively the customcontrol utilize the typical custom Control Designer functionality.

Here’s a simple a simplified guide on how to decide whether a control wouldlikely require adjustments for the OOP designer for typical Designerfunctionality:

  • Whenever a control brings a special UI functionality (like custom adorners,snap lines, glyphs, mouse interactions, etc.), the control will need to beadjusted for .NET and at least recompiled against the new WinFormsDesigner SDK. The reason for this is that the OOP Designer re-implements alot of the original functionality, and that functionality is organized indifferent namespaces. Without recompiling, the new OOP designer wouldn’tknow how to deal with the control designer and would not recognize thecontrol designer types as such.
  • If the control brings its own Type Editor, then the required adjustmentsare more considerable. This is the same process the team underwentwith the library of the standard controls: While the modal dialogsof a control’s designer can only work in the context of the Visual Studioprocess, the rest of the control’s designer runs in the context of theDesignToolServer’s process. That means a control with a custom type editor,which is shown in a modal dialog, always needs a Client/ServerControl Designer combination. It needs to communicate between the modal UIin the Visual Studio process and the actual instance of the control in theDesignToolsServer process.
  • Since the control and most of its designers now live in theDesignToolsServer (instead of Visual Studio) process, reacting to adeveloper’s UI interaction by handling those in WndProc code won’t workanymore. As already mentioned, we will publishing a blog post that willcover the authoring of custom controls for .NET and dive into the .NETWindows Forms SDK in more details.

If a Control’s property, however, does only implement a custom Converter, thenno change is needed, unless the converter needs a custom painting in theproperty grid. Properties, however, which are using custom Enums or provide alist of standard settings through the custom Converter at design time, arerunning just fine.

Features yet to come and phased out Features

While we reached almost parity with the .NET Framework Designer, there are stilla few areas where the OOP Designer needs work:

  • The Tab Order interaction has been implemented and is currently tested.This feature will be available in Visual Studio 17.1 Preview 3.Apart from the Tab Order functionality you already found in the .NETFramework Designer, we have planned to extend the Tab Order Interaction,which will make it easier to reorder especially in large forms or parts of alarge form.
  • The Component Designer has not been finalized yet, and we’re activelyworking on that. The usage of Components, however, is fully supported, andthe Component Tray has parity with the .NET Framework Designer. Note though,that not all components which were available by default in the ToolBox in.NET Framework are supported in the OOP Designer. We have decided not tosupport those components in the OOP Designer, which are only availablethrough .NET Platform Extensions (see Windows CompatibilityPack).You can, of course, use those components directly in code in .NET, shouldyou still need them.
  • The Typed DataSet Designer is not part of the OOP Designer. The same istrue for type editors which lead directly to the SQL Query Editor in .NETFramework (like the DataSet component editor). Typed DataSets need theso-called Data Source Provider Service, which does not belong to WinForms.While we have modernized the support for Object Data Sources and encourageDevelopers to use this along with more modern ORMs like EFCore, the OOPDesigner can handle typed DataSets on existing forms, which have been portedfrom .NET Framework projects, in a limited scope.

Summary and key takeaways

So, while most of the basic Designer functionality is in parity with the .NET Framework Designer,there are key differences:

  • We have taken the .NET WinForms Designer out of proc. While Visual Studio 2022 is 64-Bit .NET Framework only,the new Designer’s server process runs in the respective bitness of the project and as a .NET process.That, however, comes with a couple of breaking changes, mostly around the authoring of Control Designers.
  • Databinding is focused around Object Data Sources. While legacy support for maintainingTyped DataSet-based data layers is currently supported in a limited way, for .NET we recommendusing modern ORMs like EntityFramework or even better: EFCore. Use the DesignBindingPickerand the new Databinding Dialog to set up Object Data Sources.
  • Control library authors, who need more Design Time Support for their controls than custom type editors,need the WinForms Designer Extensibility SDK.Framework control designers no longer work without adjusting them for thenew OOP architecture of the .NET WinForms Designer.

Let us know what topics you would like hear from us around the WinForms Designer-the new Object Data Source functionality in the OOP Designer and the WinForms Designer SDKare the topics already in the making and on top of our list.

Please also note that the WinForms .NET runtime is open source, and you can contribute!If you have ideas, encountered bugs, or even want to take on PRs around the WinForms runtime,have a look at the WinForms Github repo.If you have suggestions around the WinForms Designer,feel free to file new issues there as well.

Happy coding!