State of the Windows Forms Designer for .NET Applications

Klaus L

For the last several Visual Studio release cycles, the Windows Forms (WinForms) Team has been working hard to bring the WinForms designer for .NET applications to parity with the .NET Framework designer. As you may be aware, a new WinForms designer 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 based WinForms designer everyone knows and loves. The goal of this blog post is to give you some insight into the new architecture and what sorts of changes we have made. And of course, how those changes may impact you as you create custom controls and .NET WinForms applications.

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

A bit of history

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

Until we added a support for .NET Core applications there was only a single process, devenv.exe, that both the Visual Studio environment and the application being designed ran within. But .NET Framework and .NET Core can’t both run together within devenv.exe, and as a result we had to take the designer out of process, thus we called the new designer – WinForms Out of Process Designer (or OOP designer for short).

Where are we today?

While we aimed at complete parity between the OOP designer and the .NET Framework 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 iteration already has most of the significant improvements at all important levels:

  • Performance: Starting with Visual Studio 2019 v16.10, the performance of the OOP designer has been improved considerably. We’ve worked on reducing project load times and improved the experience of interacting with controls on the design surface, like selecting and moving controls.
  • Databinding Support: WinForms in Visual Studio 2022 brings a streamlined approach for managing Data Sources in the OOP designer with the primary focus on Object Data Sources. This new approach is unique to the OOP designer and .NET based applications.
  • WinForms Designer Extensibility SDK: Due to the conceptional differences between the OOP designer and the .NET Framework designer, providers for 3rd party controls for .NET will need to use a dedicated WinForms Designer SDK to develop custom Control Designers which run in the context of the OOP designer. We have published a pre-release version of the SDK last month as a NuGet package, and you can download it here. We will be updating this package to make it provide IntelliSense in the first quarter of 2022. There will also be a dedicated blog post about the SDK in the coming weeks.

A look under the hood of the WinForms designer

Designing Forms and UserControls with the WinForms designer holds a couple of surprises 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 or JSON. It serializes the Forms/UserControl definition directly to code – in the new OOP designer that is either C# or Visual Basic .NET. When the user places a Button on a Form, the code for creating this Button and assigning its 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 is being created on the fly from that code. This assembly contains an executable version of `InitializeComponent` which is loaded in the context of the designer. `InitializeComponent` method is then executed, and the designer is now able to display the resulting Form with all its control definitions and assigned properties. We call this kind of serialization Code Document Object Model serialization, or CodeDOM serialization for short. 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 code for a control that runs during runtime, and then there is a control designer, which controls the behavior at design-time. The control designer functionality for each control is not implemented in the designer itself. Rather, a dedicated control designer interacts with Visual Studio services 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 an associated designer, in this case the `SplitContainerDesigner`. This class provides the key functionality for the design-time experience of the `SplitContainer` control:

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

When we decided to support apps built on .NET Core 3.1 and .NET 5+ in the original designer we faced a major challenge. Visual Studio is built on .NET Framework but needs to round-trip the designer code by serializing and deserializing this code for projects which target a different runtime. While, with some limitations, you can run .NET Framework based types in a .NET Core/.NET 5+ applications, the reverse is not true. This problem is known as “type resolution problem”. A great example of this can be seen in the TextBox control: 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 .NET Framework 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 designer at design time. Therefore, the code that instantiates the form and shows it in the Designer window must also be executed in .NET and not in .NET Framework, so that newer properties available only in .NET also reflect the actual appearance and behavior 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 supported such 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 it will at runtime (WYSIWYG). Whether it is `PlaceholderText` property from the earlier example, or the layout of a form with the desired default font – the CodeDom serializer must run in the context of the version of .NET the project is targeting. And we naturally can’t do that, if the CodeDom serialization is running in the same process as Visual Studio. To solve this, we run the designer out-of-process (hence the moniker Out of Process Designer) in a new .NET (Core) process called DesignToolsServer. The DesignToolsServer process runs the same version of .NET and the same bitness (x86 or x64) as your application.

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 and launches a DesignToolsServer process. Then the designer loader passes the code from the `InitializeComponent` method to the DesignToolsServer process where it can now execute under the desired .NET runtime and is now able to deal with every 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 a few other challenges around the user interaction inside Visual Studio. For example, the Property Browser, which is part of Visual Studio (and therefore also .NET Framework based). It is supposed to show the .NET Types, but it can’t do this for the same reasons the CodeDom serializer cannot (de)serialize .NET types.

Custom Property Descriptors and Control Proxies

To facilitate interaction with Visual Studio, the DesignToolsServer introduces proxy classes for the components and controls on a form which are created in the Visual Studio process along with the real components and controls on the form in the DesignToolsServer.exe process. For each one on the form, an object proxy is created. And while the real controls live in the DesignToolsServer process, the object proxy instances live in the client – the Visual Studio process. If you now select an actual .NET WinForms control on the form, from Visual Studio’s perspective an object proxy is what gets selected. And that object proxy doesn’t have the same properties of its counterpart control on the server side. It rather maps the control’s properties 1:1 with custom proxy property descriptors 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 the Property 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 is purely to intercept the mouse messages which it sends to the DesignToolsServer process.
  2. The DesignToolsServer receives the mouse click and passes it to the Behavior Service. The Behavior Service finds the control and passes it to the Selection Service that takes the necessary steps to select that control.
  3. In that process, the Behavior Service has also located the correlating Control Designer, and initiates the necessary steps to let that Control Designer render whatever adorners and glyphs it needs to render for that control. Think of the Designer Action Glyphs or the special selection markers from the earlier SplitPanel example.
  4. The Selection Service reports the control selection back to Visual Studio’s Selection Service.
  5. Visual Studio now knows, what object proxy maps to the selected control in the DesignToolsServer. The Visual Studio’s selection service selects that object proxy. This again triggers an update for the values of the selected control (object proxy) in the Property Browser.
  6. The Property Browser in turn now queries the Property Descriptors of the selected object proxy which are mapped to the proxy descriptors of the actual control in the DesignToolsServer’s process. So, for each property the Property Browser needs to update, the Property Browser calls GetValue on the respective proxy Property Descriptor, which leads to a cross-process call to the server to retrieve the actual value of that control’s property, 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 to existing custom control designers targeting .NET will be required. The extent to which the adjustments are necessary depends purely on how extensively the custom control utilize the typical custom Control Designer functionality.

Here’s a simple a simplified guide on how to decide whether a control would likely require adjustments for the OOP designer for typical Designer functionality:

  • Whenever a control brings a special UI functionality (like custom adorners, snap lines, glyphs, mouse interactions, etc.), the control will need to be adjusted for .NET and at least recompiled against the new WinForms Designer SDK. The reason for this is that the OOP Designer re-implements a lot of the original functionality, and that functionality is organized in different namespaces. Without recompiling, the new OOP designer wouldn’t know how to deal with the control designer and would not recognize the control designer types as such.
  • If the control brings its own Type Editor, then the required adjustments are more considerable. This is the same process the team underwent with the library of the standard controls: While the modal dialogs of a control’s designer can only work in the context of the Visual Studio process, the rest of the control’s designer runs in the context of the DesignToolServer’s process. That means a control with a custom type editor, which is shown in a modal dialog, always needs a Client/Server Control Designer combination. It needs to communicate between the modal UI in the Visual Studio process and the actual instance of the control in the DesignToolsServer process.
  • Since the control and most of its designers now live in the DesignToolsServer (instead of Visual Studio) process, reacting to a developer’s UI interaction by handling those in WndProc code won’t work anymore. As already mentioned, we will publishing a blog post that will cover the authoring of custom controls for .NET and dive into the .NET Windows Forms SDK in more details.

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

Features yet to come and phased out Features

While we reached almost parity with the .NET Framework Designer, there are still a 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 .NET Framework Designer, we have planned to extend the Tab Order Interaction, which will make it easier to reorder especially in large forms or parts of a large form.
  • The Component Designer has not been finalized yet, and we’re actively working on that. The usage of Components, however, is fully supported, and the 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 to support those components in the OOP Designer, which are only available through .NET Platform Extensions (see Windows Compatibility Pack). You can, of course, use those components directly in code in .NET, should you still need them.
  • The Typed DataSet Designer is not part of the OOP Designer. The same is true for type editors which lead directly to the SQL Query Editor in .NET Framework (like the DataSet component editor). Typed DataSets need the so-called Data Source Provider Service, which does not belong to WinForms. While we have modernized the support for Object Data Sources and encourage Developers to use this along with more modern ORMs like EFCore, the OOP Designer can handle typed DataSets on existing forms, which have been ported from .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 maintaining Typed DataSet-based data layers is currently supported in a limited way, for .NET we recommend using modern ORMs like EntityFramework or even better: EFCore. Use the DesignBindingPicker and 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 the new 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 SDK are 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!

73 comments

Comments are closed. Login to edit/delete your existing comments

    • Klaus LoeffelmannMicrosoft employee

      If you have used preview versions, try to uninstall (all of) those, and reinstall the latest release. If that does not help, please send a bug report over the VS feedback button (upper right corner of VS) or report a bug in the WinForms GitHub repo.

    • M D

      It’s funny. That sounds a lot like remoting which is an awesome technology that Microsoft discontinued.

    • Leszek Glinowiecki

      Strange proposition:

      Try disable Windows Defender (turn it off to be sure that no process will be available at the task list) and then start Visual Studio and WinForms Designer.

      If that will help you try install any of free Antivirus Software.

      It helped me with all my problems with WinForms Designer in .NET 5/6.

  • Robson Previato

    As the main project I work on is all based on DevExpress WinForms components, due to the instability of both WinForms Designer and/or DevExpress, unfortunately we are unable to migrate the project to .net core. I hope see that working smothly as soon as possible.

    • Raul Rodriguez

      Same here. We are migrating our backend to .NET 6, but our WinForms frontend is having lots of problems due to the designer not working. Even under .NET Framework we are experimenting some problems. I hope you get all the problems solved!

    • Darko Leontic

      You can create UI in separate .NET Framework project and simply copy the code to your new .NET Core project (just paste it in the designer cs).
      This was normal practice before designer update.

      • James Lear (Portfolion Planning)

        There are two workarounds suggested by DevExpress. Rather than copying code back and forth, you can unload and reload different project files.:

        You can have two project files in the same directory as your WinForms project: the old .csproj file from the existing .NET Framework project and the new SDK-style .csproj file of the new .NET Core WinForms project.

        You’ll just have to unload and reload the project with the corresponding project file depending on whether you want to use the designer or not.

        Personally have not tried this yet.

      • Michael Otto

        We are waiting for DevExpress WinForms Designer Updates especially with visual inheritance. Currently we are running .net48, net50-windows, net60-windows in each project to just support the Designer in VS.
        The final shipped product is just net50-win or net60-win. Waiting it get rid of net48 …

    • Klaus LoeffelmannMicrosoft employee

      Let’s please try to tackle problems, backwards compatibility and Control Designer issues one by one and let’s try to isolate issues. What’s super important to us is single, isolated sample apps that illustrate the issue, with as few dependencies as possible, so we can actually have the chance to get to the root cause. You can report bugs and provide feedback over the VS feedback Button. For providing repro apps, which we really appreciate, please file a bug in the WinForms repo.

    • James Lear (Portfolion Planning)

      There are some workarounds suggested by DevExpress. E. g.:

      You can have two project files in the same directory as your WinForms project: the old .csproj file from the existing .NET Framework project and the new SDK-style .csproj file of the new .NET Core WinForms project.

      You’ll just have to unload and reload the project with the corresponding project file depending on whether you want to use the designer or not.

      Personally have not tried this yet.

  • Kirsan

    What about that new designer silently removing all code (from *.Designer.cs) of not supported controls? More on it here.

    We are not even talking about the complete transfer of all design functionality. We want to at least open forms with such controls and use the usual properties.

    • Klaus LoeffelmannMicrosoft employee

      Well, technically, it is possibly not the Designer which removes the code, but the control which no longer generates the code. I say ‘possibly’, because I do not recall the concrete case here. So, without exactly knowing: If you have a control which used to do custom CodeDOM generation on saving the Form/UserControl, but now in .NET the necessary custom CodeDomSerializer for that hasn’t been ported and recompiled against the SDK, there is no instance which generates the code. From the developer’s perspective it seems that the Designer just erased the code, while in fact that Control’s component which is supposed to generate the code is simply missing.

      There is nothing that the Designer could do here, since that specific code generation part never was its duty to begin with, and only the control knows how to do it correctly.

      Take a look here:
      https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.design.serialization.codedomserializer?view=windowsdesktop-6.0

    • Darko Leontic

      This “old” thing works very well for many developers.
      I hope they never drop it 🙂

    • Vincent Thorn

      Rather I’ll “drop” a whole Core than “clumsy port of WinForms to Core”.

  • Darko Leontic

    I appreciate your hard work in porting the Designer to a new framework. I know it was very challenging task.

  • Philip Njoroge

    Glad to see what’s coming for WinForms Designer!
    I was once in XAML, one thing that WinForms shocked me with, is the speed at which I released new features or fixes.
    On WinForms, it’s literally Drag-Drop-Add Next Feature.
    as a Solo developer, it is my answer to this troubled world of Ever-Evolving UI

  • Heinrich Moser

    Thanks for the interesting article. As someone maintaining legacy .NET Framework apps, I’m always interested to read about how to make them interop with shiny new .NET 5+ code. I had hoped for eventual in-process .NET Framework -> .NET 5+ interop support, but if even Visual Studio needs to use an out-of-process workaround, I guess that option is really off the table.

    Out of curiosity: Which technology did you choose for the inter-process communication between VS and the DesignToolsServer? Named Pipes? Some library?

  • Irakli Lomidze

    Any Official statement about when it is expected to release stable release ?

  • Igor Katenov

    We have a real-world project, a DLL with the design-time functionality for a WinForms visual control. I tried to port it to .NET 6 using the Nuget package Microsoft.WinForms.Designer.SDK you mentioned (1.0.0-prerelease-preview5.21520.4). First, I got about 80 errors trying to recompile the design-time DLL ‘as is’. I could solve most of the problems by adding proper using directives like ‘using Microsoft.DotNet.DesignTools.Designers;’. However, some problems remained unsolved because not all .NET Framework types and members have their counterparts in .NET 6. The main problem we have is the Microsoft.DotNet.DesignTools.Editors.CollectionEditor class. It does not implement the CollectionForm class and the EditValue() method:

    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)

    In addition to this, the Microsoft.DotNet.DesignTools.Designers.ComponentDesigner type is not CLS-compliant and we must remove the line

    [assembly: CLSCompliantAttribute(true)]

    from the AssemblyInfo.cs file to compile the lib. But sure, it is unacceptable in the production build.
    Can we expect any further improvements in .NET 6 aimed at solving these problems?

    In addition to that, I found that if we pack our control into a Nuget package and attach it to a WinForms .NET 6 project, the VS Toolbox is filled not only with public control classes, but it also displays control classes marked as ‘internal’. This happens even without the Microsoft.WinForms.Designer.SDK Nuget package, but with it, we have even more unwanted items in the VS Toolbox. For example, I see the tab titled ‘Microsoft.DotNet.DesignTools.Server’ with controls like DesignerFrame and DesignerWindowFrame. Fix these issues in the nearest updates of VS, please.

    • Klaus LoeffelmannMicrosoft employee

      I get that. And we’re working on another blog post, which will address Custom Editors very soon. In a nutshell, the issue here is, that you are really not supposed to show a dialog in the context of the DesignToolsServer. Visual Studio is the UI principle, if you will, so only the client is supposed to show a modal dialog (which an editor ultimately is). So, in principle, if there is an editor involved, and it is correctly annotated (by using the SDK and not the runtime namespaces), a request for actually showing the editor is routed back to the client. The client (Visual Studio) shows the UI but then talks to the DesignToolsServer for whatever data and visual representation it needs to have. And granted, Editors with dedicated, modal shown UIs will need to get the most love to properly address the new Out-of-process reality. Still – if we don’t do it that way, we quickly risk deadlock-situations (because both Client and Server have dedicated UI threads and message pumps), in that both instances might end up waiting for each other for a modal operation to be finished.

      • Kirsan

        Looking forward to it. Because I have started to port winforms-datavisualization to new designer using SDK preview. And the main problem is the same as Igor Katenov has:

        CollectionForm; EditValue; CreateCollectionForm; ArrayEditor; FileNameEditor; ColorEditor

        and constructor that need IServiceProvider

        Also (not such important but any way):

        HelpTopic; ShowHelp
  • Daniel Smith

    The efforts that you and the rest of the team have gone to are much appreciated – it’s really great to have a migration path onto modern .NET.

    It’s unfortunate that Visual Studio is still based on .NET Framework, making all these layers of indirection necessary. Do you foresee Visual Studio moving to .NET in the future? Would you then have to rip out the extra layers again? I suppose VS moving to .NET would then cause the issue to be the other way round and it would be classic Framework projects that need extra work. If that’s a concern, I think it would be reasonable at that point to just say that if you’re still using FW projects, then you have to stick with older versions of VS that are built with FW.

    I’m also really looking forward to seeing the future posts on databinding. I’d really like to see a return of support for the Data Sources window for .NET projects as it’s a huge time saver being able to just drag and drop objects onto the form and have them all bound automatically. I’m fine with the focus being on making it work with objects – DataSet binding is a bit archaic these days.

    • Klaus LoeffelmannMicrosoft employee

      Great analysis of the challenges!
      Expect a blog post about Databinding very soon. And no, for the time being there will be no Data Source Provider Service (so, no data sources window), but that’s OK, because yes: There is now an even quicker way to hook up Object Data Source just via the Add Object Data Sources link label in the DesignBindingPicker.

      • danial hughes

        As per Daniel Smith’s question – do you know if there are plans to move Visual Studio to .NET in the future? It seems that this ‘should’ be on the roadmap given that VS is MS’s premier code authoring tool. Surely it makes sense to realize the many improvements .NET has over .NET Framework….and maybe it could simplify the designer issues.