{"id":38380,"date":"2022-01-13T11:05:58","date_gmt":"2022-01-13T18:05:58","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=38380"},"modified":"2022-01-14T14:33:32","modified_gmt":"2022-01-14T21:33:32","slug":"state-of-the-windows-forms-designer-for-net-applications","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/state-of-the-windows-forms-designer-for-net-applications\/","title":{"rendered":"State of the Windows Forms Designer for .NET Applications"},"content":{"rendered":"<p>For the last several Visual Studio release cycles, the Windows Forms (WinForms) Team has been\nworking hard to bring the WinForms designer for .NET applications to parity with\nthe .NET Framework designer. As you may be aware, a new WinForms\ndesigner was needed to support .NET Core 3.1 applications, and later .NET 5+\napplications. The work required a near-complete rearchitecting of the designer,\nas we responded to the differences between .NET and the .NET Framework based\nWinForms designer everyone knows and loves. The goal of this blog post is to\ngive you some insight into the new architecture and what sorts of changes we\nhave made. And of course, how those changes may impact you as you create custom\ncontrols and .NET WinForms applications.<\/p>\n<p>After reading this blog post you will be familiar with the underlying problems\nthe new WinForms designer is meant to solve and have a high-level understanding\nof the primary components in this new approach. Enjoy this look into the\ndesigner architecture and stay tuned for future blogs!<\/p>\n<h2>A bit of history<\/h2>\n<p>WinForms was introduced with the first version of .NET and Visual Studio in\n2001. WinForms itself can be thought of as a wrapper around the complex Win32\nAPI. It was built so that enterprise developers didn\u2019t need to be ace C++\ndevelopers to create data driven line-of-business applications. WinForms was\nimmediately a hit because of its WYSIWYG designer where even novice developers\ncould throw together an app in minutes for their business needs.<\/p>\n<p>Until we added a support for .NET Core applications there was only a single\nprocess, <em>devenv.exe<\/em>, that both the Visual Studio environment and the application\nbeing designed ran within. But .NET Framework and .NET Core can\u2019t both run\ntogether within <em>devenv.exe<\/em>, and as a result we had to take the designer <em>out of\nprocess<\/em>, thus we called the new designer &#8211; WinForms Out of Process Designer (or\n<em>OOP designer<\/em> for short).<\/p>\n<h2>Where are we today?<\/h2>\n<p>While we aimed at complete parity between the OOP designer and the .NET\nFramework designer for the release of <a href=\"https:\/\/aka.ms\/vs2022gablog\">Visual Studio 2022<\/a>,\nthere are still a few issues on our backlog. That said, the OOP designer in its current iteration\nalready has most of the significant improvements at all important levels:<\/p>\n<ul>\n<li><strong>Performance:<\/strong> Starting with Visual Studio 2019 v16.10, the performance of\nthe OOP designer has been improved considerably. We\u2019ve worked on reducing\nproject load times and improved the experience of interacting with controls\non the design surface, like selecting and moving controls.<\/li>\n<li><strong>Databinding Support:<\/strong> WinForms in Visual Studio 2022 brings a\nstreamlined approach for managing Data Sources in the OOP designer with the\nprimary focus on <em>Object<\/em> Data Sources. This new approach is unique to the\nOOP designer and .NET based applications.<\/li>\n<li><strong>WinForms Designer Extensibility SDK:<\/strong> Due to the conceptional differences\nbetween the OOP designer and the .NET Framework designer, providers for 3rd\nparty controls for .NET will need to use a dedicated WinForms Designer SDK\nto develop custom Control Designers which run in the context of the OOP\ndesigner. We have published a pre-release version of the SDK last month as a\nNuGet package, and you can <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.WinForms.Designer.SDK\">download it\nhere.<\/a> We\nwill be updating this package to make it provide IntelliSense in the first\nquarter of 2022. There will also be a dedicated blog post about the SDK in\nthe coming weeks.<\/li>\n<\/ul>\n<h2>A look under the hood of the WinForms designer<\/h2>\n<p>Designing Forms and UserControls with the WinForms designer holds a couple of\nsurprises for people who look under the hood of the designer for the first time:<\/p>\n<ol>\n<li>The designer doesn\u2019t \u201csave\u201d (serialize) the layout in some sort of XML or\nJSON. It serializes the Forms\/UserControl definition directly to code \u2013 in\nthe new OOP designer that is either C# or Visual Basic .NET. When the user\nplaces a Button on a Form, the code for creating this Button and assigning\nits properties is generated into a method of the Form called\n`InitializeComponent`. When the Form is opened in the designer, the\n`InitializeComponent` method is parsed and a <em>shadow .NET assembly<\/em> is\nbeing created on the fly from that code. This assembly contains an\nexecutable version of `InitializeComponent` which is loaded in the context\nof the designer. `InitializeComponent` method is then executed, and the\ndesigner is now able to display the resulting Form with all its control\ndefinitions and assigned properties. We call this kind of serialization\n<em>Code Document Object Model serialization<\/em>, or <em>CodeDOM serialization<\/em> for\nshort. This is the reason, you shouldn\u2019t edit `InitializeComponent`\ndirectly: the next time you visually edit something on the Form and save it,\nthe method gets overwritten, and your edits will be lost.<\/li>\n<li>All WinForms controls have two code layers to them. First there is the code\nfor a control that runs during runtime, and then there is a control\n<em>designer,<\/em> which controls the behavior at design-time. The control designer\nfunctionality for each control is not implemented in the designer\nitself. Rather, a dedicated control <em>designer<\/em> interacts with Visual Studio\nservices and features.Let\u2019s look at `SplitContainer` as an example:<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/01\/SplitPanelUIDemo.gif\" alt=\"An animated gif of the various control designer features of the SplitContainer control\" \/><\/p>\n<p>The design-time behavior of the SplitContainer is implemented in an\nassociated designer, in this case the `SplitContainerDesigner`. This class\nprovides the key functionality for the design-time experience of the\n`SplitContainer` control:<\/p>\n<ul>\n<li>The way the outer Panel and the inner Panels get selected on mouse\nclick.<\/li>\n<li>The ability of the splitter bar to be moved to adjust the sizes of the\ninner panels.<\/li>\n<li>To provide the Designer Action Glyph, which allows a developer using the\ncontrol to manage the Designer Actions through the respective short cut\nmenu.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>When we decided to support apps built on .NET Core 3.1 and .NET 5+ in\nthe original designer we faced a major challenge. Visual Studio is\nbuilt on .NET Framework but needs to round-trip the designer code by serializing\nand deserializing this code for projects which target a different runtime. While, with some\nlimitations, you <em>can<\/em> run .NET Framework based types in a .NET Core\/.NET 5+\napplications, the reverse is not true. This problem is known as \u201c<em>type\nresolution problem<\/em>\u201d. A great example of this can be seen in the TextBox\ncontrol: in .NET Core 3.1 we added a new property called `PlaceholderText`. In\n.NET Framework that property does not exist on `TextBox`. So, if the .NET\nFramework based CodeDom Serializer (running in Visual Studio) encountered the\n`PlaceholderText` property it would fail.<\/p>\n<p>In addition, a Form with all its controls and components renders itself in the designer\nat design time. Therefore, the code that instantiates the form and shows it in the\nDesigner window must also be executed in .NET and not in .NET Framework, so that\nnewer properties available only in .NET also reflect the actual appearance and\nbehavior of the controls, components, and ultimately the entire Form or UserControl.<\/p>\n<p>Because we plan to continue innovating and adding new features in the future,\nthe problem only grows over time. So we had to design a mechanism that supported\nsuch cross-framework interactions between the WinForms designer and Visual Studio.<\/p>\n<h3>Enter the DesignToolsServer<\/h3>\n<p>Developers need to see their Forms in the designer looking precisely the way it\nwill at runtime (WYSIWYG). Whether it is `PlaceholderText` property from the\nearlier example, or the layout of a form with the desired default font \u2013 the\nCodeDom serializer must run in the context of the version of .NET the project is\ntargeting. And we naturally can\u2019t do that, if the CodeDom serialization is\nrunning in the same process as Visual Studio. To solve this, we run the designer\nout-of-process (hence the moniker <em>Out of Process Designer<\/em>) in a new\n.NET (Core) process called <em>DesignToolsServer<\/em>. The DesignToolsServer process\nruns the same version of .NET and the same bitness (x86 or x64) as your\napplication.<\/p>\n<p>Now, when you double-click on a Form or a UserControl in Solution Explorer,\nVisual Studio\u2019s designer loader service determines the targeted .NET version and\nlaunches a DesignToolsServer process. Then the designer loader passes the code\nfrom the `InitializeComponent` method to the DesignToolsServer process where\nit can now execute under the desired .NET runtime and is now able to deal with\nevery type and property this runtime provides.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/01\/DesignToolsServerBitness.png\" alt=\"Screenshot showing each Form of projects with different bitness and the entries of DesignToolsServer in TaskManager\" \/><\/p>\n<p>While going out of process solves the type-resolution-problem , it introduces a\nfew other challenges around the user interaction inside Visual Studio. For\nexample, the Property Browser, which is part of Visual Studio (and therefore\nalso .NET Framework based). It is supposed to show the .NET Types, but it can\u2019t\ndo this for the same reasons the CodeDom serializer cannot (de)serialize .NET\ntypes.<\/p>\n<h3>Custom Property Descriptors and Control Proxies<\/h3>\n<p>To facilitate interaction with Visual Studio, the DesignToolsServer introduces\nproxy classes for the components and controls on a form which are created in the\nVisual Studio process along with the real components and controls on the form in\nthe DesignToolsServer.exe process. For each one on the form, an object proxy is\ncreated. And while the real controls live in the DesignToolsServer process, the\nobject proxy instances live in the client \u2013 the Visual Studio process. If you\nnow select an actual .NET WinForms control on the form, from Visual Studio\u2019s\nperspective an object proxy is what gets selected. And that object proxy doesn\u2019t\nhave the same properties of its counterpart control on the server side. It\nrather <em>maps<\/em> the control\u2019s properties 1:1 with custom proxy property\ndescriptors through which Visual Studio can talk to the server process.<\/p>\n<p>So, clicking now on a button control on the form, leads to the following\n(somewhat simplified) chain of events to get the properties to show in the\nProperty Browser:<\/p>\n<ol>\n<li>The mouse click happens on special window in the Visual Studio process,\ncalled the <em>Input Shield.<\/em> It acts like a sneeze guard, if you will, and is\npurely to intercept the mouse messages which it sends to the\nDesignToolsServer process.<\/li>\n<li>The DesignToolsServer receives the mouse click and passes it to the\n<em>Behavior Service<\/em>. The Behavior Service finds the control and passes it to\nthe <em>Selection Service<\/em> that takes the necessary steps to select that\ncontrol.<\/li>\n<li>In that process, the Behavior Service has also located the correlating\n<em>Control Designer<\/em>, and initiates the necessary steps to let that Control\nDesigner render whatever adorners and glyphs it needs to render for that\ncontrol. Think of the Designer Action Glyphs or the special selection\nmarkers from the earlier SplitPanel example.<\/li>\n<li>The Selection Service reports the control selection back to Visual Studio\u2019s\nSelection Service.<\/li>\n<li>Visual Studio now knows, what object proxy maps to the selected control in\nthe DesignToolsServer. The Visual Studio\u2019s selection service selects that\nobject proxy. This again triggers an update for the values of the selected\ncontrol (object proxy) in the Property Browser.<\/li>\n<li>The Property Browser in turn now queries the Property Descriptors of the\nselected object proxy which are mapped to the proxy descriptors of the\nactual control in the DesignToolsServer\u2019s process. So, for each property the\nProperty Browser needs to update, the Property Browser calls <em>GetValue<\/em> on\nthe respective proxy Property Descriptor, which leads to a cross-process\ncall to the server to retrieve the <em>actual<\/em> value of that control\u2019s\nproperty, which is eventually displayed in the Property Browser.<\/li>\n<\/ol>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/01\/DesignerProcessesDiagram.png\" alt=\"Diagram which shows the process chain how Visual Studio communicates with the DesignToolsServer\" \/><\/p>\n<h3>Compatibility of Custom Controls with the DesignToolsServer<\/h3>\n<p>With the knowledge of these new concepts, it is obvious that adjustments to\nexisting custom control designers targeting .NET will be required. The extent to\nwhich the adjustments are necessary depends purely on how extensively the custom\ncontrol utilize the typical custom Control Designer functionality.<\/p>\n<p>Here\u2019s a simple a simplified guide on how to decide whether a control would\nlikely require adjustments for the OOP designer for typical Designer\nfunctionality:<\/p>\n<ul>\n<li>Whenever a control brings a special UI functionality (like custom adorners,\nsnap lines, glyphs, mouse interactions, etc.), the control will need to be\nadjusted for .NET and <em>at least<\/em> recompiled against the new WinForms\nDesigner SDK. The reason for this is that the OOP Designer re-implements a\nlot of the original functionality, and that functionality is organized in\ndifferent namespaces. Without recompiling, the new OOP designer wouldn\u2019t\nknow how to deal with the control designer and would not recognize the\ncontrol designer types as such.<\/li>\n<li>If the control brings its own <em>Type Editor<\/em>, then the required adjustments\nare more considerable. This is the same process the team underwent\nwith the library of the standard controls: While the modal dialogs\nof a control\u2019s designer can only work in the context of the Visual Studio\nprocess, the rest of the control\u2019s designer runs in the context of the\nDesignToolServer\u2019s process. That means a control with a custom type editor,\nwhich is shown in a modal dialog, always needs a Client\/Server\nControl Designer combination. It needs to communicate between the modal UI\nin the Visual Studio process and the actual instance of the control in the\nDesignToolsServer process.<\/li>\n<li>Since the control and most of its designers now live in the\nDesignToolsServer (instead of Visual Studio) process, reacting to a\ndeveloper\u2019s UI interaction by handling those in <em>WndProc<\/em> code won\u2019t work\nanymore. As already mentioned, we will publishing a blog post that will\ncover the authoring of custom controls for .NET and dive into the .NET\nWindows Forms SDK in more details.<\/li>\n<\/ul>\n<p>If a Control\u2019s property, however, does only implement a custom Converter, then\nno change is needed, unless the converter needs a custom painting in the\nproperty grid. Properties, however, which are using custom Enums or provide a\nlist of standard settings through the custom Converter at design time, are\nrunning just fine.<\/p>\n<h2>Features yet to come and phased out Features<\/h2>\n<p>While we reached almost parity with the .NET Framework Designer, there are still\na few areas where the OOP Designer needs work:<\/p>\n<ul>\n<li>The <strong>Tab Order interaction<\/strong> has been implemented and is currently tested.\nThis feature will be available in Visual Studio 17.1 Preview 3.\nApart from the Tab Order functionality you already found in the .NET\nFramework Designer, we have planned to extend the Tab Order Interaction,\nwhich will make it easier to reorder especially in large forms or parts of a\nlarge form.<\/li>\n<li>The <strong>Component Designer<\/strong> has not been finalized yet, and we\u2019re actively\nworking on that. The usage of Components, however, is fully supported, and\nthe Component Tray has parity with the .NET Framework Designer. Note though,\nthat not all components which were available by default in the ToolBox in\n.NET Framework are supported in the OOP Designer. We have decided <em>not<\/em> to\nsupport those components in the OOP Designer, which are only available\nthrough .NET Platform Extensions (see <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/porting\/windows-compat-pack\">Windows Compatibility\nPack<\/a>).\nYou can, of course, use those components directly in code in .NET, should\nyou still need them.<\/li>\n<li>The <strong>Typed DataSet Designer<\/strong> is not part of the OOP Designer. The same is\ntrue for type editors which lead directly to the SQL Query Editor in .NET\nFramework (like the DataSet component editor). Typed DataSets need the\nso-called <em>Data Source Provider Service<\/em>, which does not belong to WinForms.\nWhile we have modernized the support for <em>Object<\/em> Data Sources and encourage\nDevelopers to use this along with more modern ORMs like EFCore, the OOP\nDesigner can handle typed DataSets on existing forms, which have been ported\nfrom .NET Framework projects, in a limited scope.<\/li>\n<\/ul>\n<h2>Summary and key takeaways<\/h2>\n<p>So, while most of the basic Designer functionality is in parity with the .NET Framework Designer,\nthere are key differences:<\/p>\n<ul>\n<li>We have taken the .NET WinForms Designer out of proc. While Visual Studio 2022 is 64-Bit .NET Framework only,\nthe new Designer&#8217;s server process runs in the respective bitness of the project and as a .NET process.\nThat, however, comes with a couple of breaking changes, mostly around the authoring of Control Designers.<\/li>\n<li>Databinding is focused around <em>Object<\/em> Data Sources. While legacy support for maintaining\nTyped DataSet-based data layers is currently supported in a limited way, for .NET we recommend\nusing modern ORMs like EntityFramework or even better: EFCore. Use the <em>DesignBindingPicker<\/em>\nand the new Databinding Dialog to set up Object Data Sources.<\/li>\n<li>Control library authors, who need more Design Time Support for their controls than custom type editors,\nneed the <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.WinForms.Designer.SDK\">WinForms Designer Extensibility SDK<\/a>.\nFramework control designers no longer work without adjusting them for the\nnew OOP architecture of the .NET WinForms Designer.<\/li>\n<\/ul>\n<p>Let us know what topics you would like hear from us around the WinForms Designer-\nthe new <em>Object Data Source<\/em> functionality in the OOP Designer and the <em>WinForms Designer SDK<\/em>\nare the topics already in the making and on top of our list.<\/p>\n<p>Please also note that the WinForms .NET runtime is open source, and you can contribute!\nIf you have ideas, encountered bugs, or even want to take on PRs around the WinForms runtime,\nhave a look at the <a href=\"https:\/\/github.com\/dotnet\/winforms\">WinForms Github repo<\/a>.\nIf you have suggestions around the WinForms Designer,\nfeel free to file new issues there as well.<\/p>\n<p>Happy coding!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Windows Forms designer for .NET is ready for prime time! There will be differences if you previously have used the designer with .NET Framework apps. In this blog post, I break down what you need to know.<\/p>\n","protected":false},"author":9483,"featured_media":38381,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,7163],"tags":[7342,7221],"class_list":["post-38380","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-winforms","tag-designer","tag-windows-forms"],"acf":[],"blog_post_summary":"<p>The Windows Forms designer for .NET is ready for prime time! There will be differences if you previously have used the designer with .NET Framework apps. In this blog post, I break down what you need to know.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/38380","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/9483"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=38380"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/38380\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/38381"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=38380"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=38380"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=38380"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}