{"id":43635,"date":"2022-12-05T09:10:00","date_gmt":"2022-12-05T17:10:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=43635"},"modified":"2022-12-05T07:48:15","modified_gmt":"2022-12-05T15:48:15","slug":"custom-controls-for-winforms-out-of-process-designer","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/custom-controls-for-winforms-out-of-process-designer\/","title":{"rendered":"Custom Controls for WinForm&#8217;s Out-Of-Process Designer"},"content":{"rendered":"<p>WinForms is a success story that is now in its 20th year. Developers were\nthrilled from the beginning with how quickly even complex user interfaces could\nbe built &#8211; often with considerably less coding effort compared to any other UI\nstack. The popularity of WinForms would not have grown so widely over the years\nhowever, if not for two critical success factors: First, the simple way in which\ndevelopers can create additional UI Controls with comparatively little effort,\nincluding professional design-time support. And secondly the resulting huge\necosystem of WinForms user control libraries for almost every imaginable\ndomain-specific purpose.<\/p>\n<p>Creating user controls for .NET Core\/.NET 5-8 (which we call just .NET from\nhere on) or migrating them from .NET Framework works with close to no effort &#8211;\nat least when it is about the runtime functionality. WinForms applications\nwritten in .NET can benefit from <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/performance-improvements-in-net-6\/\">3 &#8211; 5x performance improvements in many\nareas<\/a>\n(which again <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/performance_improvements_in_net_7\/\">increased in .NET\n7<\/a>).\nAdditionally, migrated applications and control libraries profit from much more\nmodest memory consumption when rendering elements from GDI+. That said, there is\none aspect of a custom control or a control library which needs somewhat more\neffort to target the .NET runtime: The new Out-Of-Process WinForms Designer.<\/p>\n<h2>The Out-Of-Process WinForms Designer<\/h2>\n<p>Until we added support in Visual Studio for .NET Core WinForms applications,\nthere was only a single process, <em>devenv.exe<\/em>, that both the Visual Studio\nenvironment and the WinForms application being designed ran within. But .NET\nFramework and .NET Core can\u2019t both run together within the <em>devenv.exe<\/em> process,\nand as a result we had to take part of the designer out of process to support\n.NET applications. Since .NET Core 3.0 started to support the WinForms .NET\nRuntime, a new WinForms designer was needed to support .NET applications. This\nwork required a near-complete re-architect of the designer as we responded to\nthe differences between .NET and the .NET Framework based WinForms designer\neveryone knows and loves. Developers need to see their Forms in the designer\nlooking precisely the way it will at runtime (WYSIWYG). Whether it is about a\n<code>TextBox<\/code> and its <code>PlaceholderText<\/code> property, <code>Button<\/code> and its <code>Command<\/code>, or the\nlayout of a form with the desired default font \u2013 in order for it to generate the\nWinForms code which sets up the form or UserControl at runtime in\n<code>InitializeComponent<\/code>, the CodeDom Serializer must run in the context of the\nversion of .NET the project is targeting. And we naturally can\u2019t do that, if the\nCodeDom serialization is running in the same .NET Framework process as Visual\nStudio. To stick with the example: In .NET Framework, <code>TextBox<\/code> does not have a\n<code>PlaceHolder<\/code> property (introduced in .NET Core 3.0), and <code>Button<\/code> is missing\nthe <code>Command<\/code> property, <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/winforms-enhancements-in-dotnet-7\/#databinding-improvements\">which has been introduced in .NET\n7<\/a>.\nThe correct version of those .NET types simply cannot be resolved in the .NET\nFramework process. To solve this, we run the designer out-of-process 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, x64 or ARM64) as your\napplication.<\/p>\n<p>To handle the interaction with Visual Studio, we have introduced proxy classes\n(.NET Framework <code>ObjectProxy<\/code>) for the components and controls on a form which\nare created along with the real components and controls on the form in the\nDesignToolsServer process. For each component or control on the form, an Object\nProxy is created. While the real controls live in the DesignToolsServer process,\nthe Object Proxy instances live in the client \u2013 the Visual Studio process.\nObject Proxies then talk with their actual .NET counter parts across the\nprocesses.<\/p>\n<p>You can read more about the details about the concept of the two processes <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/state-of-the-windows-forms-designer-for-net-applications\/\">in\nthis blog post\nabout<\/a>\nthe state of the WinForms Designer.<\/p>\n<p>For the design-time support, this has consequences. One principle of WinForms\ncontrols is that each control brings its own Designer: For a <code>TextBox<\/code> control\nthere is <code>TextBoxDesigner<\/code> class which provides special TextBox design-time\nsupport. For the <code>ListBox<\/code> control, there is the <code>ListBoxDesigner<\/code> class, which\nprovides special design-time support for <em>that<\/em> control, and so on. Design-time\nsupport often means that there is UI-related functionality which the Designer\nneeds to provide. For example, when the user picks an item from a list for a\ncontrol&#8217;s property in the Property Browser, the control&#8217;s designer has to\nprovide that list as a <em>Type Converter<\/em>. For more sophisticated scenarios,\ncontrol designers can also provide custom painting at design time. The handling\nof Action Lists, which provide a quick access to special design-time\nfunctionalities or properties (the feature image of this blog post is such an\nAction List) is one additional Designer feature. Ultimately, they can also\nprovide real complex UIs, which are modal dialogs, shown in the context of\nVisual Studio.<\/p>\n<h3>Migrating existing Control Designers with almost no effort<\/h3>\n<p>Depending on what type of UI-based Design time functionality your Designer\nprovides, the coding effort compared to .NET Framework maybe the same or almost the\nsame. Here is what that means exactly:<\/p>\n<ul>\n<li>\n<p>If your control requires a UI which is based on a type-converter and\ntherefore, shown in the context of the Property Browser (like Enums or dedicated\nitems to show in a property grid\u2019s property grid cell ComboBox), your UI will\nbe supported by the new designer model out of the box.<\/p>\n<\/li>\n<li>\n<p>If your control requires a UI, which shows up as part of the control (like\ncustom painted adorners or Action Lists) at design time, then you would need\nto write your control library against the WinForms Designer SDK, but you don\u2019t\nneed to take care about round-tripping data to the Server process. Everything\nfrom the Developer\u2019s perspective seems to actually be done server-side, and\nyou can reuse most of the existing control designer Code.<\/p>\n<p>Note though, that in those cases you need to target the control Designer\nagainst the <em>WinForms Designer SDK<\/em>. That means, you need to reference the\n<a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.WinForms.Designer.SDK\">Microsoft.WinForms.Designer.SDK NuGet\npackage<\/a> from\nyour control library. In addition, you need to refactor the namespaces from\n<code>System.ComponentModel.Design<\/code> to <code>Microsoft.DotNet.DesignTools.Designers<\/code>,\nwhere it applies. Some classes are more finely granulated when it comes to\nnamespace definitions, so for example the <code>DesignerActionList<\/code> base class,\nwhich is originally located in <code>System.ComponentModel.Design<\/code> in the runtime,\nyou&#8217;d find in <code>Microsoft.DotNet.DesignTools.Designers.Actions<\/code>. It&#8217;s easiest\nto let Visual Studio do the namespace lookup in those refactoring cases:<\/p>\n<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/NamespaceLookupInVisualStudio.png\" alt=\"Screenshot showing how to look up WinForms Designer-SDK namespaces in Visual Studio\" \/><\/p>\n<p>If your control designer however needs to provide a custom Type Editor for a\nproperty whose type&#8217;s value need a more complex user interaction, then there is\nsome additional effort for the different processes, which you need to take into\naccount.<\/p>\n<p><strong>Note:<\/strong> This blog post provides a couple of samples, which you can use as a\nguidance to either create new .NET based or migrate your existing\n.NET Framework-based control designers to .NET. Section <a href=\"#two-net-samples-and-the-why\"><em>Two<\/em> .NET samples, and the\nWhy<\/a> describes those scenarios in more detail.<\/p>\n<h2>Recap: Type Editors for special control properties<\/h2>\n<p>While more simple control designer scenarios like type converters, Action Lists\nor CodeDom Serializers don\u2019t need any substantial rewrites, <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/system.drawing.design.uitypeeditor?view=netframework-4.7.2\">Type\nEditors<\/a>,\nwhich are responsible for a complex dialog-based UI experience, are a different\nbeast altogether. But before we get into that, let&#8217;s recap what Type Editors in\nthe context of WinForms Control Designers are and what purpose they serve.<\/p>\n<p>Technically speaking, a Type Editor is a mechanism which provides the front end\nfor a user to set the value for a certain control property via some data entry\ndevice. The simplest one: A string editor for a control&#8217;s property of type\n<code>string<\/code>, whose value is entered via the keyboard in the Property Browser.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/EnteringValueInPropertyBrowser.png\" alt=\"Entering hexadecimal RGB values for a Background property in the Property Browser\" \/><\/p>\n<p>But not all properties are of type <code>string<\/code>. So, for most of the cases, a <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/system.componentmodel.typeconverter?view=net-7.0\">type\n<em>converter<\/em><\/a> will\nextend the functionality of the build-in string editor by providing a mechanism\nto convert the characters you types to the actual type&#8217;s value. For example, the\ncharacters &#8216;#202020&#8217; are assumed to be hexadecimal representations for the\nRGB-color values &#8216;Red: 32, Green: 32, Blue: 32&#8217; and thus converted to a color of\nvery dark gray for a background. Unfortunately, not all properties have a\nmeaningful way to convert to its native value based on a string. Or, it is way\nmore useful to interact on a visual representation of a type&#8217;s value. For a\nproperty which represents a picture for example, the respective Type Editor must\nprovide the UI to select a picture from disk, serialize the picture, store it as\na resource or file in the project, and generate the required code to assign that\npicture at runtime.<\/p>\n<p>Let&#8217;s see how such a Type Editor works for a .NET Framework control in the\nIn-Proc-WinForms Designer, and then, why that approach does no longer work in\nthe context of the out-of-process WinForms Designer.<\/p>\n<h3>The Demo scenario \u2013 the WinForms TileRepeater Control<\/h3>\n<p>In our Template and Samples GitHub Repo, you find <a href=\"https:\/\/github.com\/microsoft\/winforms-designer-extensibility\/tree\/main\/Samples\/TypeEditor\/Framework\/TileRepeater_Medium\">a sample WinForms\napp<\/a>\nfor this scenario. It is a picture browser, which shows the jpeg-Images of a\nfolder in a special way:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/PictureBrowser_FrameworkSample.gif\" alt=\"Animated gif showing the opening of a folder and then a preview of jpegs of different dog.\" \/><\/p>\n<p>In our example app, those are items holding JPeg-image filenames of a folder on\ndisk. On the UI-side you have a user control which represents the template for\neach element of the data source. At runtime, for each element in the data source\none tile based on this user control template gets created. Each item of the data\nsource is then data-bound to the instance of that user control, and the user\ncontrol is responsible for rendering the content. To this end, our\n<code>TileRepeater<\/code> control has an <code>ItemTemplate<\/code> property. This property determines,\nwhich type of an element of the data source results in what user control\ntemplate type in the UI. And to make this approach even more flexible,\nTileRepeater also has a <code>SeparatorTemplate<\/code> property: It determines another type\nfor the items, which would serve as a visible separator at runtime.<\/p>\n<p>So, the data source list which holds the pictures in the list is not completely\nhomogenous. Rather, we find two types of elements in it &#8211; but we still can use a\ngeneric list, since the types built an inheritance type hierarchy.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/DataSourceElementList.png\" alt=\"Data source list of picture and separator elements in the Auto tool window of Visual Studio\" \/><\/p>\n<p><strong>DISCLAIMER<\/strong>: The <code>TileRepeater<\/code> control is a demo for the specific .NET\nWinForms Designer scenario &#8211; it lacks a virtual rendering mode. It works fine\nfor up to 100 elements, but more than that drains the available resources too\nmuch, and a virtual mode would be necessary to show a large number of items\nreliably.<\/p>\n<h3>How a Type Editor works in the In-Process WinForms Designer<\/h3>\n<p>Here is what happens behind the scenes at design-time when the user sets the\nvalues for <code>ItemTemplate<\/code> or <code>SeparatorTemplate<\/code>. When they determine which of\nthe element types in the data source list should lead to what user control type\n(derived from <code>TileRepeaterTemplate<\/code>) for the actual rendering container at\nruntime, this happens behind the scenes.<\/p>\n<ol>\n<li>The user wants to show the Type Editor for the <code>TemplateAssignment<\/code> type of the\n<code>ItemTemplate<\/code> property of the TileRepeater control. To that end, they either\nuse a command from the Action List or the Property Browser of Visual Studio\n(by clicking on the <em>&#8230;<\/em> button in the grid for that property).<\/li>\n<\/ol>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/InitiateEditTemplateItems.png\" alt=\"User calling Type Editor of ItemTemplate property via Action List of the TileRepeater control designer\" \/><\/p>\n<ol start=\"2\">\n<li>Since the <code>TemplateAssignment<\/code> type is annotated with the <code>EditorAttribute<\/code>,\nthe Type Editor <code>TemplateAssignmentEditor<\/code> is found and instantiated. Since\nits <code>GetEditStyle()<\/code> method returns <code>UITypeEditorEditStyle.Modal<\/code>, the\nWinForms Designer knows that this Type Editor as a modal WinForms Dialog. The\nDesigner calls the <code>EditValue<\/code> method and passes the type descriptor context,\nthe service provider and the actual value to edit. The type descriptor context\nprovides information about the context in which the Type Editor has been\ncalled, e.g. if that has been the Property Browser or an Action List. The\nService Provider allows the Editor to acquire required Visual Studio services &#8211; for example the <code>WindowsFormsEditorService<\/code> to show the actual Dialog in the\ncontext of Visual Studio.  <\/li>\n<\/ol>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/ClassEditorAnnotation.png\" alt=\"Class code annotated with the EditorAttribute\" \/><\/p>\n<ol start=\"3\">\n<li>\n<p>The UI of the Type Editor is controlled by a ViewModel\n(<code>TemplateAssignmentViewModel<\/code>). That means that code for displaying the UI\nelements and the code for figuring out <em>what<\/em> to display are separated from\neach other. This is the recommended architectural practice. The Dialog UI\n(<code>TemplateAssignmentDialog<\/code>) is then instantiated and passed the ViewModel.<\/p>\n<\/li>\n<li>\n<p>The Type Editor dialog is shown on the screen by passing it to the\n<code>ShowDialog<\/code> method of Visual Studio&#8217;s editor service. In that dialog, the\nuser can now pick the two types which needs to be assigned to each other:<\/p>\n<\/li>\n<\/ol>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/TileRepeaterTypeEditor.png\" alt=\"The Type Editor of the TileRepeater control allowing to assign data source item and user control template.\" \/><\/p>\n<ol start=\"5\">\n<li>When the user commits the new setting with <em>OK<\/em>, the dialog calls the\n<code>OKClick<\/code> method in the ViewModel to actually create the new value for the\nproperty in its <code>TemplateAssignment<\/code> property. The code flow returns to the\n<code>EditValue<\/code> method, where the new property value is taken from that\n<code>TemplateAssignment<\/code> property and returned to the Designer, which then\neventually assigned the new value to the <code>TileRepeater<\/code> control.<\/li>\n<\/ol>\n<h2>The challenge of Type Editors in the Out-Of-Process Designer<\/h2>\n<p>To make the <code>TileRepeater<\/code> control work at design time in the new out-of-process\nDesigner, we need to add the support for the process separation. When we want to\nshow the Type Editor as a modal Dialog, which allows the user to pick the types\nfor the data template assignment, we have to deal with the different processes:\nThe Visual Studio Process runs in .NET Framework. But the actual control, for\nwhich we are showing the custom UI, runs in the dedicated .NET server process.\nIf your WinForms project using the control, is targeting .NET Core 6.0, then\nthat process runs .NET Core 6.0. If your target is 7.0, the Server Process runs\nagainst .NET 7.0, and so on. That is necessary, because you need and use types\nonly the specific version of .NET knows about. The Visual Studio and <em>.NET\nFramework<\/em>-based client process is simply not able to discover or handle those\n.NET types. From that fact arises the real challenge: Since the control\ndesigner\u2019s dialogs are running in the context of .NET Framework, it cannot\nsimply search for the types (in our example neither both the items to bind and\nthe resulting UserControl types) it is supposed to offer the user in that\ndialog. Rather, the .NET Framework part of the Type Editor needs to ask the .NET\nProcess for those types and then use transport classes to get those types\ncross-process back to the .NET Framework based Visual Studio process. It can now\nprocess the user\u2019s input and send the results back to the server process. And\nyes, that is a necessary breaking change from the previous .NET Framework-only\ncontrol designers, and it involves some refactoring of the design time code. But\nthis refactoring is needed <em>only<\/em> if there <em>is<\/em> an actual UI which needs to be\nshown on top of the UI that is presented in the context of your actual control,\nas we already discussed in the section [Control Designers with almost no\nmigration effort] (#control-designers-with-almost-no-migration-effort).<\/p>\n<ul>\n<li>\n<p>If you have custom Type Editors, which are displaying dedicated modal dialogs,\nthen there is some rewriting effort involved for round-tripping the required\ndata between the two processes.<\/p>\n<\/li>\n<li>\n<p>If you have Type Editors which are derived from existing Type Editors (like\n<code>ColorEditor<\/code> or <code>FileNameEditor<\/code>) for editing certain types of values for\nexisting controls in .NET Framework, then you <em>also<\/em> need the client\/server\napproach. That said, your control designer solution most probably doesn&#8217;t need\nto have additional communication code to exchange data between the server and\nthe client process in those cases. As long as you do not change the type the\noriginal editor is handling, the Designer handles the necessary communication\nbehind the covers. But: That communication is still required to happen, and the\nmodified (inherited) editor types still need to be run in the context of Visual\nStudio &#8211; which at this time is either the 32-Bit .NET Framework process (VS 2019) or\nthe 64-bit .NET Framework process (VS 2022).<\/p>\n<\/li>\n<li>\n<p>If you however just <em>use<\/em> the editors (which again need to be provided by the\nclient process), a server-only control designer suffices. In that case though,\nyou need to state the types in the required attributes as strings, and cannot\nuse <code>typeof<\/code> (in C#) or <code>GetType<\/code> (in VB). It would look something like this:<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"language-csharp\">[Editor(\"System.Windows.Forms.Design.FileNameEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\",\n        \"System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\")]\npublic string? Filename { get; set; }<\/code><\/pre>\n<h2><em>Two<\/em> .NET samples, and the Why<\/h2>\n<p>The example that you&#8217;ve seen earlier showed the approach of control\ndesigner functionality and Type Editors in .NET Framework. When we either port\nthose designers from .NET Framework to .NET, or develop new control designers\nfor .NET altogether, we&#8217;ve already discussed that different controls designers\nmight need different kind of UIs. When you use only stock Type Editors editors,\nor you just need your control designers to provide custom Adorner painting,\nAction Lists or custom CodeDOM serializers, a server-only version for the\nDesigner will do, and which is pretty much the way, you&#8217;ve developed in control designers in .NET\nFramework. <em>Only<\/em> if your Control or your Control Library needs custom UI Type\nEditors, then you need a Client\/Server-solution for your Control library.<\/p>\n<p>That is the reason, the sample folder for this blog post contains yet <a href=\"https:\/\/github.com\/microsoft\/winforms-designer-extensibility\/tree\/main\/Samples\/TypeEditor\/Dotnet\/TileRepeater_Medium\">another\nversion of the sample<\/a>:<\/p>\n<p>We won&#8217;t got to much into this sample &#8211; it&#8217;s pretty much self-explanatory as the\nonly difference to a .NET Framework version is that it targets the WinForms\nDesigner SDK instead of the APIs in <code>System.ComponentModel<\/code> of the runtime as\nexplained in section [Existing Control Designers with almost no migration\neffort] (#existing-control-designers-with-almost-no-migration-effort).<\/p>\n<p>The second sample, however, is the full <code>TileRepeater<\/code> control version ported\nfrom .NET Framework to .NET. Before we take a look at that solution, let&#8217;s learn\nmore about about the differences for Type Editors between In-Process and\nOut-Of-Process WinForms Designer, and what the easiest way is to port a Type\nEditor for a .NET Framework Control (or control library) to .NET &#8211; or start authoring\na .NET Control Library from scratch.<\/p>\n<h2>Type Editor templates for creating the out-of-process Designer Projects<\/h2>\n<p>A Type Editor for the out-of-process WinForms Designer needs to be built from\nseveral projects. The section <a href=\"#using-the-type-editor-template\">Using the Type Editor\nTemplate<\/a> gives all the necessary background\ninformation in detail. What\u2019s important for now: The Type Editor Solution\nTemplates are based on two sample solutions, one in C# and one in Visual Basic.\nThese solutions are located in the path<\/p>\n<pre><code class=\"language-powershell\">.\\\\winforms-designer-extensibility\\\\Templates\\\\TypeEditor\\\\src\\\\TemplateSolutions*.<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/TemplateSolutionItems.png\" alt=\"Overview of the projects in their solution folders divisions\" \/><\/p>\n<p>The actual Solution Templates (which are NuGet packages) are built from these solutions by a batch file (more about that below).<\/p>\n<p>These solutions provide:<\/p>\n<ul>\n<li>A WinForms .NET 6 custom control project named <em>CustomControlLibrary<\/em> which\nholds the actual custom control. The custom control\u2019s only purpose is to\nrender the content of a composite type named <em>CustomPropertyStore<\/em>, which is\njust the composition of a bunch of properties of different types.<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/CustomControlWithOpenedActionList.png\" alt=\"Custom Control of the Type Editor template in WinForms Designer, showing the expanded action list\" \/><\/p>\n<ul>\n<li>\n<p>Three projects which make up the control&#8217;s designer:<\/p>\n<ul>\n<li>The <em>CustomControlLibrary.Client<\/em> project which targets the same .NET\nFramework version as Visual Studio (in the sample .NET Framework 4.7.2). It\nholds the actual WinForms Type Editor, the Type Editor\u2019s UI, and the\nclient-side ViewModel.<\/li>\n<li>The <em>CustomControlLibrary.Server<\/em> project, which targets .NET 6. It holds\nthe actual control designer, along with a custom CodeDom serializer which\ndetermines the necessary steps to generate custom property code for\n<code>InitializeComponent<\/code>. The project also contains a designer action list implementation for the\ncontrol (see screenshot above). Finally, it includes a couple of methods in the\nserver-side ViewModel, which are called by the client to control aspects of\nthe UI.<\/li>\n<li>The <em>CustomControlLibrary.Protocol<\/em> project which holds all the classes\nwhich are necessary to handle the communication between the client and the\nserver process.<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>A Package project named <em>CustomControlLibrary.Package<\/em>, which packs the\nbinaries of all those projects in a special structure as a NuGet project, so\nthey can be loaded by the WinForms Designer in the individual client and\nserver processes.<\/p>\n<\/li>\n<li>\n<p>A .NET 6 WinForms project named <em>CustomTypeEditorTest<\/em> to test the control and\nits design-time functionality.<\/p>\n<\/li>\n<\/ul>\n<p>The procedure for building the actual templates from the template solutions is\nas follows:<\/p>\n<ul>\n<li>Make sure the template solutions work as planned if you\u2019ve made any modifications.<\/li>\n<li>In the command line, change the current directory to <em>Templates<\/em>.<\/li>\n<li>Run the <em>prepareTemplates.bat<\/em> batch file.<\/li>\n<\/ul>\n<p>This copies the relevant project files from the template solution to the\ntemplates folder. The batch file then calls <em>dotnet pack<\/em> to create the solution\ntemplate package and also installs the package with <em>dotnet new install<\/em>. You\nshould see the result of that operation:<\/p>\n<pre><code class=\"language-powershell\">D:\\Git\\winforms-designer-extensibility\\Templates\\TypeEditor\\src\\Templates&gt;dotnet pack\nMSBuild version 17.4.0-preview-22470-08+6521b1591 for .NET\n  Determining projects to restore...\n  All projects are up-to-date for restore.\nC:\\Program Files\\dotnet\\sdk\\7.0.100-rc.2.22477.23\\Sdks\\Microsoft.NET.Sdk\\targets\\Microsoft.NET.RuntimeIdentifierInference.targets(257,5): message NETSDK1057: You are using a preview version of .NET. See: https:\/\/aka.ms\/dotnet-support-policy [D:\\Git\\winforms-designer-extensibility\\Templates\\TypeEditor\\src\\Templates\\CustomTypeEditorTemplatePack.csproj]\n  CustomTypeEditorTemplatePack -&gt; D:\\Git\\winforms-designer-extensibility\\Templates\\TypeEditor\\src\\Templates\\bin\\Debug\\netstandard2.0\\CustomTypeEditorTemplatePack.dll\n  Successfully created package 'D:\\Git\\winforms-designer-extensibility\\Templates\\TypeEditor\\src\\Templates\\bin\\Debug\\Microsoft.WinForms.Designer.TypeEditorTemplate.1.1.0-prerelease-preview3.nupkg'.\n\nD:\\Git\\winforms-designer-extensibility\\Templates\\TypeEditor\\src\\Templates&gt;dotnet new install .\\bin\\Debug\\Microsoft.WinForms.Designer.TypeEditorTemplate.1.1.0-prerelease-preview3.nupkg\nThe following template packages will be installed:\n   D:\\Git\\winforms-designer-extensibility\\Templates\\TypeEditor\\src\\Templates\\bin\\Debug\\Microsoft.WinForms.Designer.TypeEditorTemplate.1.1.0-prerelease-preview3.nupkg\n\nSuccess: Microsoft.WinForms.Designer.TypeEditorTemplate::1.1.0-prerelease-preview3 installed the following templates:\nTemplate Name                   Short Name          Language  Tags\n------------------------------  ------------------  --------  ---------------------------------------------------------\nWinForms .NET Custom Type E...  WinFormsTypeEditor  [C#],VB   WinForms\/Designer\/TypeEditor\/ActionList\/CodeDomSerializer<\/code><\/pre>\n<h2>Using the Type Editor Template<\/h2>\n<p>After building the templates they are ready to use from the CLI as well as from\nVisual Studio.<\/p>\n<h3>Creating a new Type Editor Solution within Visual Studio<\/h3>\n<ul>\n<li>Start Visual Studio, and click on <em>Create a new Project<\/em>.<\/li>\n<li>In the New-Project-Dialog, type \u201cwinforms\u201d in the filter textbox.<\/li>\n<li>Pick one of the newly available Type Editor templates, either for C# or\nVisual Basic.  <\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/CreateNewTypeEditorSolution.png\" alt=\"The VS new project dialog showing the Type Editor templates\" \/> <\/p>\n<ul>\n<li>\n<p>Click <em>Next<\/em>.<\/p>\n<\/li>\n<li>\n<p>In the <em>Configure your Project<\/em> page, specify the following options:<\/p>\n<ul>\n<li><strong>Project name:<\/strong> This will become the base name of the Project.<\/li>\n<li><strong>Location:<\/strong> This is the path where the solution and the respective\nprojects will be created.<\/li>\n<li><strong>Solution:<\/strong> You can choose here if the projects should be created anew or\nadded to an existing solution.<\/li>\n<li><strong>Solution name:<\/strong> The name of the solution.<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>Click <em>Next<\/em>.<\/p>\n<\/li>\n<li>\n<p>On the <em>Additional Information<\/em> page, specify the following options:<\/p>\n<ul>\n<li><strong>Framework:<\/strong> Pick the .NET Version you want the server components (control\nlibrary, control designer project) targeted against. <strong>NOTE:<\/strong> At this point\nthe client projects will always target .NET Framework 4.7.2, as this is the\nVisual Studio target framework version. The Type Editor templates support\n.NET Versions from 6.0 on.<\/li>\n<li><strong>PropertyTypeName:<\/strong> This is the name of the individual custom property the\nType Editor will offer the editing functionality for. In the sample project\nthe templates are based on, this is the <code>CustomPropertyStore<\/code> type. Every\nreference to this type name or file name will be renamed to the class name\nyou enter here.<\/li>\n<li><strong>Type Editor Name:<\/strong> This is the name of the Type Editor. In the sample\nproject the templates are based on, this is the <code>CustomTypeEditor<\/code> type.\nEvery reference to this type name or file name will be renamed to the class\nname you enter here.<\/li>\n<li><strong>CustomControlName:<\/strong> This is the name of the custom control. In the sample\nproject the templates are based on, this is the <code>CustomControl<\/code> type. And\nagain, every reference to this type name or file name will be renamed to the\nclass name you\u2019re entering here.<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>Click <em>Create<\/em> to create the solution.<\/p>\n<\/li>\n<\/ul>\n<h3>Creating a new Type Editor Solution from the dotnet CLI<\/h3>\n<p>After installing the templates, you use the Type Editor solution templates\nlike every other Visual Basic or C# templates from the CLI. Refer to the help\noption for the exact parameter names. The parameters are the same as in the\n<em>additional options<\/em> description above.<\/p>\n<h3>Projects which the templates create<\/h3>\n<p>Setting all the aforementioned projects up manually means coordinating a lot of\nmoving parts, and there is some potential that things go wrong. The individual\nprojects created by this template help to prevent falling into those traps. The\ntemplates create a series of projects and important solution folders, depending\non your needs for both C# and Visual Basic. Let\u2019s look at the projects which\nare part of the template solution in detail:<\/p>\n<ul>\n<li>\n<p><strong>_Solution Items:<\/strong> This is not really a project, but rather a solution\nfolder, which holds the readme, the <em>Directory.Build<\/em> target which determines\nthe NuGet package version for the WinForms Designer Extensibility SDK version\nused, and the <em>NuGet.config<\/em> setting. If at any point you need to change\nthe Designer SDK version which is used throughout the solution, you would only\nneed to change them in this one spot.<\/p>\n<\/li>\n<li>\n<p><strong>CustomControlLibrary.Client<\/strong> This is a project of the same target framework\nversion as Visual Studio, and it holds the actual Type Editor UI running in\nthe context of Visual Studio. It also contains the client <em>ViewModel<\/em>, which\nis a UI controller class. There are actually two ViewModel versions. One in\nthe client, and one in the server. Only the latter has access to the actual\nserver types, while only the client one has direct access to the UI &#8211; that&#8217;s\nwhy both are needed. Both are communicating with each other, so that the\nclient ViewModel can control the UI based on that.<\/p>\n<\/li>\n<li>\n<p><strong>CustomControlLibrary.Server:<\/strong> This project holds every aspect of the\ncontrol designer, which needs to be executed in the context of the server\nprocess. Those are:<\/p>\n<ul>\n<li>The server-side ViewModel, which provides the necessary data to the\nclient-side ViewModel.<\/li>\n<li>The factory class, which generates the server-side ViewModel.<\/li>\n<li>A custom CodeDom serializer for the custom property type the Type Editor is\nhandling, should one be needed.<\/li>\n<li>A custom designer action list which can be accessed at design time through\nthe designer action glyph of the control. Please note, that although these\nclasses are hosted purely in the server-side designer assembly, the UI for\nthe respective action list is still shown in the context of Visual Studio.\nThe necessary communication with the client is done completely behind the\nscenes by the out-of-process WinForms Designer.<\/li>\n<li>The actual control designer, which among other things, paints the adorners\nfor the controls. Although it looks like this rendering is done in the\ncontext of Visual Studio, it is not. The rendering of the Form and all its\ncomponents at design time is done by the DesignToolsServer process and\nprojected on the client-area of Visual Studio Design surface. Although\nrendered on the server-side, there is no direct interaction with the message\nqueue of the server. Every keyboard- and mouse-input is still received in\nthe Visual Studio-based client process, and then communicated to the server.\nThis is the key to how the WinForms Designer ensures that no deadlocks can\noccur due to competing message queues of different processes.<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>CustomControlLibrary.Protocol:<\/strong> This project contains all the classes\nwhich are necessary for the communication between the client and the server\nprocess via <a href=\"https:\/\/www.jsonrpc.org\/\">JSON-RPC<\/a>. The Designer SDK provides\na series of base classes which are optimized for transferring\nWinForms-typical data types between the client- and the server-process. A\ntypical protocol library for a control designer builds on those classes.<\/p>\n<\/li>\n<li>\n<p><strong>CustomControlLibrary:<\/strong> This is the project, which contains your actual\ncustom control(s).<\/p>\n<\/li>\n<li>\n<p><strong>CustomControlLibrary.Package:<\/strong> This is the project which creates the\ncontrol library&#8217;s NuGet package. This NuGet package organizes the individual\ncontrol designer components for the DesignToolsServer process and the Visual\nStudio client process in respective folders, so that the required parts are\navailable for the processes at design time.<\/p>\n<\/li>\n<\/ul>\n<h2>Invoking Type Editors, In-Process vs. Out-Of-Process<\/h2>\n<p>The differences between the in-process and the out-of-process WinForms Designer\nare very fundamental. So, especially when you need to migrate a classic Type\nEditor to work in the out-of-process Designer, understanding where and how to\nmake the necessary adjustments is paramount.<\/p>\n<p>In the in-process WinForms Designer the invoking of a Type Editor in .NET\nFramework is a straightforward procedure, and from the .NET Framework version of\nthe TileRepeater project, you already know how Type Editors are getting invoked\nin the In-Proc-WinForms Designer.<\/p>\n<h3>Type Editors in the Out-of-Process WinForms Designer<\/h3>\n<p>Here now is the all-important difference compared to the in-process Designer\nscenario: When the Property Browser asks the Type Editor to display the visual\nrepresentation of the value, that value&#8217;s type is not available in the context\nof Visual Studio. The Property Browser runs in a process targeting a different\n.NET version than the process that defines the type. Visual Studio runs against\n.NET Framework 4.7.2 while the custom control library you are developing is e.\ng. targeting .NET 7. So, instead of giving the UITypeEditor the control\u2019s\ncustom\/special property\u2019s value directly, it\u2019s handing it via the <em>Object Proxy<\/em>.<\/p>\n<p>The concept of Object Proxies in the client (Visual Studio) process does require\na special infrastructure for handling user inputs in custom Type Editors. Let\u2019s\nlook at what infrastructure components of the Designer we need to understand,\nbefore we talk about the workflow for setting the value in the out-of-process\nscenario:<\/p>\n<ul>\n<li>\n<p><strong>Using ViewModels:<\/strong> ViewModels are for controlling aspects of a UI without\nhaving a direct reference to the UI specific components &#8211; we have seen that\nconcept already in the .NET Framework sample. Don\u2019t confuse\nview models we use here with ViewModels you might know from XAML languages:\nWhile they are also controlling the UI without having any direct\ndependencies on the UI technology, in contrast to XAML, they are not doing\nthis by direct data binding. Here, they are used to synchronize certain\naspects of the UI between the client and the server process. The class\n<code>CustomTypeEditorVMClient<\/code> provides a static method <code>Create<\/code>, which is the\ndedicated way to create a ViewModel. You pass the service provider and also\nthe Object Proxy representing the instance of the property&#8217;s value, which\nthe client-side Type Editor just got from the Property Browser, to edit to\nthe <code>Create<\/code> method.<\/p>\n<\/li>\n<li>\n<p><strong>Sessions and the DesignToolsClient:<\/strong> For the communication with the\nDesignToolsServer process the Designer needs a sending and a receiving\nendpoint. The <code>DesignToolsClient<\/code> class represents the client-side sending\nendpoint and provides the basic mechanisms for communication with the\nserver. To separate the concerns of each WinForms document within Visual\nStudio which has been opened, each designer document is associated with a\nsession and its related session ID. The <code>Create<\/code> method in the sample\ndemonstrates how to retrieve a session and the <code>DesignToolsClient<\/code> through\nthe Service Provider, and use those objects how to subsequently talk to the\nserver \u2013 in this case to create the respective <em>server-side<\/em> ViewModel.<\/p>\n<\/li>\n<li>\n<p><strong>Object Proxy classes:<\/strong> These classes solve the basic challenge of\nrepresenting objects of server-side .NET version types which are not known\nto the .NET Framework based client. If you select a component in the\nDesigner, what the property browser \u201csees\u201d is an Object <em>Proxy<\/em> which\nrepresents the real object in the server process. A value of a property of a\ncomplex type is also represented by a proxy object, since \u2013 again \u2013 its type\nonly exists on the server, because it\u2019s targeting a different .NET version.\nAnd remember: the server-side ViewModel <em>returned<\/em> from the server is\nnot the actual ViewModel instance, but rather its corresponding Object\nProxy.<\/p>\n<\/li>\n<li>\n<p><strong>Data transport and remote procedure calls:<\/strong> The communication between\nclient and server is always synchronous, in other words, blocking. You define\nendpoints in the server-process, which the client calls. The client waits\nuntil the server has finished processing those remote procedure calls.\nBasically, each endpoint needs three dedicated classes:<\/p>\n<ul>\n<li>A <em>Request<\/em> class, defined in the Protocol project (see below), which\ntransports necessary data to the DesignToolsServer.<\/li>\n<li>A <em>Response<\/em> class, which transports result data back to the client process\n\u2013 also defined in the Protocol project.<\/li>\n<li>A <em>Handler<\/em> class, which is the server-side remote-procedure to call, if you\nwill. This class is defined in the Server-Project, since it will most likely use the actual Control types.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>In the Type Editor solution template, two endpoints are already predefined:\n<code>CreateCustomTypeEditorVM<\/code> creates the server-side ViewModel, whose instance is\nthen hosted as a proxy-object in the client-side ViewModel. The communication\nand data exchange can be simplified over those two instances. And then there is\nalso the <code>TypeEditorOKClick<\/code> endpoint: This method is called when the user\nclicked the <em>OK<\/em> button of the Type Editor&#8217;s dialog during design time to\nindicate that they finished changing the value passed by the Property Browser.\nSince the custom property type only exists in the DesignToolsServer, the client\ncan only pass over the individual data fragments from what the user entered in\nthe dialog to the server process. But it is the server which then creates the\nactual instance of the value passed from the client. And it\neventually assigns that value to the property of the user control.<\/p>\n<p>Now, with these important basics in mind, here is the internal workflow for setting a\nproperty value via a Type Editor in the out-of-process Designer scenario in\ndetail:<\/p>\n<ol>\n<li>\n<p>As in the classic in-process-Scenario, the user wants to set a value for a\ncustom property. And again, a Type Editor for that property type is defined by\nthe <code>EditorAttribute<\/code> (see class <code>CustomPropertyStore<\/code> in the template project).\nThe first important difference: Since the type in question might not be\navailable in the client process\u2019 target framework, the type can only be defined\nas a string. Also as before, the custom Type Editor class is instantiated, when\nthe user clicks on the <em>&#8230;<\/em> button in the property\u2019s cell of the Property Browser.\nNow, here is a first exciting challenge that the modern WinForms control developer faces: When the\ncustom control lives only in the server process, and the actual Type Editor\nlives only in the client, how does the WinForms Designer finds the Type Editor\non the client side? This is where an important component in the client designer\nproject comes into play: the <code>TypeRoutingProvider<\/code>. It holds a table of\n<code>TypeRoutingDefinition<\/code> objects and assigns the names of the editors to the\nactual types. That means, if you were ever to add additional Type Editors for\nother property types or controls to your control library solution, this table\nmust be maintained accordingly. It\u2019s best practice to use the <code>EditorNames<\/code>\ndefinitions in the Protocol project to that end, since it minimizes typos by\nproviding IntelliSense support.<\/p>\n<\/li>\n<li>\n<p>The Property Browser now calls the <code>EditValue<\/code> method of the Type Editor and\npasses the value of the property to set. But, again, the value is not the\nactual value of the property. Instead, it is the Object Proxy, which points to\nthe instance of the property type in the server process. This also\nmeans the processing of the value must happen in the server-process. To this\nend, the two ViewModel types to control the edit procedure need now to be\nused: one on the client side (<code>CustomTypeEditorVMClient<\/code>), and one on the\nserver side (<code>CustomTypeEditorVM<\/code>). The template creates both classes for you,\nalong with the infrastructure methods to set them up.<\/p>\n<\/li>\n<li>\n<p>The static <code>Create<\/code> method in the client ViewModel  has now all the\ninformation to create the actual ViewModel instance and to that end, it can\ncall the <code>CreateViewModelClient<\/code> method of the Designer service provider.<\/p>\n<\/li>\n<li>\n<p>The Type Editor\u2019s main task is to edit the value of type\n<code>CustomPropertyStore<\/code>. To keep the example simple, this is just a composite\ntype, composed of a <code>string<\/code>, a <code>DateTime<\/code>, a list of <code>string<\/code> elements and a\ncustom Enum. As a reminder: since this type only exists server-side, the UI\n(living in the context of Visual Studio) cannot use this type. This is where\nthe Protocol project\/assembly comes into play. The Protocol project defines\nall the transport classes, which can be used in either process. It is defined\nas a .NET Standard 2.0 library, so all its types can be projected and used in\nboth .NET and .NET Framework projects. We mirror the <code>CustomPropertyStore<\/code>\ntype with a special data class we define in the Protocol project named\n<code>CustomPropertyStoreData<\/code>, so that it becomes available on both sides. This\ntype also provides the necessary methods to convert the data it&#8217;s hosting into\nthe JSON format and back from it, which is needed to transport it across the\nprocess\u2019s boundaries. The response class for the endpoint to create the\nserver-side ViewModel not only takes the proxy of the server-side ViewModel,\nbut also the original values of the types which the custom property type is\ncomposed of. And this data we now use to populate the Type Editor\u2019s dialog\nclient side.<\/p>\n<\/li>\n<li>\n<p>The user now edits the values.<\/p>\n<\/li>\n<li>\n<p>When the user clicks <em>OK<\/em>, we validate the data on the client inside the\n<code>CustomTypeEditorDialog<\/code>. If that validation passes, the dialog returns\n<code>DialogResult.OK<\/code>, and we call the <code>ExecuteOKCommand<\/code> method of the client\nViewModel to kick of the data transfer to the server. This method now sends\nthe <code>CustomTypeEditorOKClickRequest<\/code> to the server passing the individual\nretrieved data from the user\u2019s input of the dialog along. The endpoint\u2019s\nhandler gets those data and passes &#8211; in turn &#8211; that data to the server-side\nViewModel. That again calls its <code>OnClick<\/code> method, composes the actual instance\nof the custom control\u2019s property type, and stores it in the <code>PropertyStore<\/code>\nproperty of the server-side ViewModel. And with that, the call chain seems to\nbe finished. So, the server-side ViewModel now holds the edited and committed\nresult. One question remains: How does the ViewModel property find the way\nback to the control\u2019s property, which we see in the Designer and &#8211; through the\nObjectProxy &#8211; reflected in the property browser? That last step is done\nclient-side, and it\u2019s kind of subtle. Remember? When the client-side view\nmodel got created, it not only triggered the creation of the server-side view\nmodel. It also requested the proxy of that ViewModel to be returned to the\nclient side. On the client, the client-side ViewModel holds the reference to\nthe server-side ViewModel\u2019s <code>PropertyStore<\/code> property over a proxy object. When\nthe user clicks <em>OK<\/em> in the editor, that code flow is returned to the Type\nEditor (running in the context of Visual Studio), which had opened the modal\ndialog to begin with. Now, back in the actual Type Editor class, it is where\nthe assignment from this ViewModel to the actual property of the control\nhappens:<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"language-csharp\">    var dialogResult = editorService.ShowDialog(_customTypeEditorDialog);\n    if (dialogResult == DialogResult.OK)\n    {\n        \/\/ By now, the UI of the Editor has asked its (client-side) ViewModel\n        \/\/ to run the code which updates the property value. It passes the data to\n        \/\/ the server, which in turn updates the server-side ViewModel.\n        \/\/ When it's time to return the value from the client-side ViewModel back to the\n        \/\/ Property Browser (which has called the Type Editor in the first place), the client-side\n        \/\/ ViewModel accesses its PropertyStore property, which in turn gets the required PropertyStore\n        \/\/ proxy object directly from the server-side ViewModel.\n        value = viewModelClient.PropertyStore;\n    }<\/code><\/pre>\n<p>The <code>PropertyStore<\/code> property of the <code>ViewModelClient<\/code> doesn\u2019t have a dedicated\nbacking field to hold the value. Rather, it uses the infrastructure of the proxy\nto communicate with the server-side ViewModel to get the just created proxy of\nthe server-side ViewModel\u2019s <code>PropertyStore<\/code> content directly. And the proxy\nobject is what we need here. Again, since the client doesn\u2019t know the type, it\ncan only deal with the proxy objects which point and represent the server types\ninstead.<\/p>\n<h2>Migrating the TileRepeater sample from .NET Framework to .NET<\/h2>\n<p>With the knowledge of how the Type Editor (which is created by the solution\ntemplate) works, the migration of a Type Editor from .NET Framework to .NET is a\nstraight forward process, despite the fact that there is some effort needed to\nwrite the respective boiler-plate code.<\/p>\n<p>The principle approach applied to move a .NET Framework control designer is this:<\/p>\n<ul>\n<li>\n<p>If you haven&#8217;t yet, separate the UI inside your Type Editor(s) from the\ncontrolling logic by introducing ViewModels. This is important, because the\nnew WinForms-SDK Type Editor base classes use the concept of ViewModels to\ncontrol the UI both from the client and the server-side. Separating a\nUI-Controller of a Type Editor&#8217;s Dialog into client and server part is way\neasier, when you already have a UI-independent ViewModel that holds all the\nUI-logic.<\/p>\n<\/li>\n<li>\n<p>Use the solution template to create the necessary projects. For our the\nmigration of our TileRepeater-example, we use <em>TileRepeater<\/em> as solution name.\nFor entering the project details, we use these settings for naming the\nrespective solution items:<\/p>\n<ul>\n<li><em>TemplateAssignment<\/em> for PropertyTypeName,<\/li>\n<li><em>TemplateAssignmentEditor<\/em> for TypeEditorName,<\/li>\n<li><em>TileRepeater<\/em> for CustomControlName,<\/li>\n<li>and we Target the solution to either .NET 6 or .NET 7.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/TileRepeaterSolutionNamingElements.png\" alt=\"Naming the Type Editor solution template elements for the framework-to.net migration\" \/><\/p>\n<p>The most important thing in the migration process is to figure out which\ncomponents need to go to to what side and why. Obviously, when you&#8217;re starting\noff with a brand new control designer solution, there are already components in\nplace, which need to be replaced. This is what you can use as guidance for their\nreplacements by your actual components.<\/p>\n<p><strong>Note:<\/strong> The completely migrated solution <a href=\"https:\/\/github.com\/microsoft\/winforms-designer-extensibility\/tree\/main\/Samples\/TypeEditor\/Dotnet\/TileRepeater_Medium\">you also find in the new WinForms\nDesigner Extensibility GitHub\nrepo<\/a>.\nYou can always check against its solution explorer structure to make sure you\n&#8220;got&#8221; all necessary adjustments.<\/p>\n<p>As you know from the previous description of the scenario that the templates\ncreates, these are the major area that needs rework:<\/p>\n<ul>\n<li>The actual Custom Control needs to be replaced. So, everything which is placed\nin the <em>Controls<\/em> solution folder of the template generated solution should be\naddressed first. That would already enable the TileRepeater control at\nruntime. We discuss this in <a href=\"#migrating-runtime-functionality\">Migrating runtime\nfunctionality<\/a>.<\/li>\n<li>The ViewModel needs to be split into server and client part. We discuss this\nin [Migrating the ViewModel to server and client areas]\n(#migrating-the-view-model-to-server-and-client-areas).<\/li>\n<li>We need communication classes which serve as proxies for types .NET Framework\ncannot use from the server process. We discuss this in <a href=\"#providing-the-protocol-classes-for-communication-between-client-and-server\">Providing the protocol\nclasses for communication between client and\nserver<\/a>.<\/li>\n<li>We need to modify the endpoints so that actions, which get initiated client-side, are ultimately executed in the server process, and then return the respective result back to the client. We discuss this in <a href=\"#implementing-endpoints-for-server-side-functionality\">Implementing endpoints for server-side functionality<\/a>.<\/li>\n<\/ul>\n<h3>Migrating runtime functionality<\/h3>\n<p>In most cases, migrating the actual control from .NET Framework to .NET, or\ncreating a control in .NET from scratch will not engender additional effort. On\nthe contrary: running in .NET means way better performance, more considerate\nmemory consumption and just broader options due to a way bigger .NET runtime\nAPI. To migrate the control&#8217;s runtime part of our .NET Framework TileRepeater\nversion to the solution create by the Type Editor .NET template&#8230;<\/p>\n<ul>\n<li>We would need to swap out the class code for <code>TemplateAssignment<\/code> and\n<code>TileRepeater<\/code>.<\/li>\n<li>In addition to those files, we&#8217;d need the classes <code>Tile<\/code> and <code>TileContent<\/code>,\nand we can copy them just over from the .NET Framework solution.<\/li>\n<li>Our TileRepeater doesn&#8217;t have a custom Enum, so wouldn&#8217;t need that code file\n(<code>TemplateAssignmentEnum<\/code>) at all, and can delete it.<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/MigratingTheControlClasses.png\" alt=\"Showing in solution explorer, which classes to migrate for the control&#039;s runtime\" \/><\/p>\n<h3>Migrating the ViewModel to server and client areas<\/h3>\n<p>When looking at the ViewModel, we stumble over the core problem, due to which we\nhad to take the Designer itself out of the Visual Studio process in the first\nplace. Let&#8217;s take a look at the following code of the ViewModel in the .NET\nFramework source version:<\/p>\n<pre><code class=\"language-csharp\">using System;\nusing System.ComponentModel;\nusing System.ComponentModel.Design;\nusing System.Linq;\n\nnamespace WinForms.Tiles.Designer\n{\n    internal partial class TemplateAssignmentViewModel \n    {\n        private const string SystemNamespace = \"System\";\n        private const string MicrosoftNamespace = \"Microsoft\";\n        private const string AccessibilityNamespace = \"Accessibility\";\n\n        private ITypeDiscoveryService _typeDiscoveryService;\n\n        private TemplateAssignmentViewModel(IServiceProvider serviceProvider)\n        {\n            _typeDiscoveryService = (ITypeDiscoveryService)serviceProvider.GetService(typeof(ITypeDiscoveryService));\n        }\n\n        .\n        .\n        .\n\n        private TypeInfoData[] GetTemplateTypelist()\n        {\n            var types = _typeDiscoveryService.GetTypes(typeof(object), true)\n                .Cast&lt;Type&gt;()\n\n        .\n        .\n        .\n\n            return types.ToArray();\n        }\n    }\n}\n<\/code><\/pre>\n<p>The <code>GetTemplateTypeList<\/code> method of the <code>TemplateAssignmentViewModel<\/code> class\nshows how the list of all available types within the target framework is first\ndetermined via Visual Studio&#8217;s <code>TypeDiscoveryService<\/code> and then filtered for the\ntypes we&#8217;re interested in. And it is exactly this which would not work for .NET as target\nframework: If the ViewModel ran in the context of Visual Studio, then of course\nonly those types would be returned from the .NET Framework version that would\ncorrespond to that of Visual Studio. And that would be .NET Framework 4.7.2. But\nwe need the type list based on .NET, and therefore the determination of the\ntypes must be done server-side. So, we need a second ViewModel running in the\nserver process.<\/p>\n<p>Thus the ViewModel class needs to be split in two parts:<\/p>\n<ul>\n<li>\n<p>The project <em>TileRepeater.Designer.Client<\/em> will have all the Type Editor classes\nlike before and the client part of the ViewModel.<\/p>\n<\/li>\n<li>\n<p>The project <em>TileRepeater.Designer.Server<\/em> will have the server-side view\nmodel classes. It will also have a server-side Factory of that ViewModel,\nwhich is part of the WinForms Designer infrastructure:<\/p>\n<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/ViewModelSplitting.png\" alt=\"Splitting of the .NET Framework ViewModel classes into server\/client-version in solution explorer.\" \/><\/p>\n<p>The original .NET Framework ViewModel class now needs some refactoring.<\/p>\n<ul>\n<li>First of all, it needs to use the inbuilt ViewModel infrastructure of the\nOut-of-process designer, and that means the client-side ViewModel needs to be\ninherited from <code>Microsoft.DotNet.DesignTools.Client.Views.ViewModelClient<\/code>. It\nalso needs to use a special internal procedure to create the ViewModel, and\nhas to implement a respective factory to that end. Thirdly, the client\nViewModel needs to have access to the server-side ViewModel, which of course\nalso must be created. For that reason, the definition and initialization of\nthe client ViewModel changes into this:<\/li>\n<\/ul>\n<pre><code class=\"language-csharp\">using Microsoft.DotNet.DesignTools.Client;\nusing Microsoft.DotNet.DesignTools.Client.Proxies;\nusing Microsoft.DotNet.DesignTools.Client.Views;\nusing System;\nusing WinForms.Tiles.ClientServerProtocol;\nusing WinForms.Tiles.Designer.Protocol;\nusing WinForms.Tiles.Designer.Protocol.Endpoints;\n\nnamespace WinForms.Tiles.Designer.Client\n{\n    internal partial class TemplateAssignmentViewModelClient : ViewModelClient\n    {\n        [ExportViewModelClientFactory(ViewModelNames.TemplateAssignmentViewModel)]\n        private class Factory : ViewModelClientFactory&lt;TemplateAssignmentViewModelClient&gt;\n        {\n            protected override TemplateAssignmentViewModelClient CreateViewModelClient(ObjectProxy? viewModel)\n                =&gt; new(viewModel);\n        }\n\n        private TemplateAssignmentViewModelClient(ObjectProxy? viewModel)\n            : base(viewModel)\n        {\n            if (viewModel is null)\n            {\n                throw new NullReferenceException(nameof(viewModel));\n            }\n        }\n\n        \/\/\/ &lt;summary&gt;\n        \/\/\/  Creates an instance of this ViewModelClient and initializes it with the ServerTypes \n        \/\/\/  from which the Data Sources can be generated.\n        \/\/\/ &lt;\/summary&gt;\n        \/\/\/ &lt;param name=\"session\"&gt;\n        \/\/\/  The designer session to create the ViewModelClient server side.\n        \/\/\/ &lt;\/param&gt;\n        \/\/\/ &lt;returns&gt;\n        \/\/\/  The ViewModelClient for controlling the NewObjectDataSource dialog.\n        \/\/\/ &lt;\/returns&gt;\n        public static TemplateAssignmentViewModelClient Create(\n            IServiceProvider provider,\n            object? templateAssignmentProxy)\n        {\n            var session = provider.GetRequiredService&lt;DesignerSession&gt;();\n            var client = provider.GetRequiredService&lt;IDesignToolsClient&gt;();\n\n            var createViewModelEndpointSender = client.Protocol.GetEndpoint&lt;CreateTemplateAssignmentViewModelEndpoint&gt;().GetSender(client);\n\n            var response = createViewModelEndpointSender.SendRequest(new CreateTemplateAssignmentViewModelRequest(session.Id, templateAssignmentProxy));\n            var viewModel = (ObjectProxy)response.ViewModel!;\n\n            var clientViewModel = provider.CreateViewModelClient&lt;TemplateAssignmentViewModelClient&gt;(viewModel);\n            clientViewModel.Initialize(response.TemplateServerTypes, response.TileServerTypes);\n\n            return clientViewModel;\n        }\n\n        private void Initialize(TypeInfoData[] templateServerTypes, TypeInfoData[] tileServerTypes)\n        {\n            TemplateServerTypes = templateServerTypes;\n            TileServerTypes = tileServerTypes;\n        }<\/code><\/pre>\n<p>There are a series of changes to observe, which always need to become part of the migration effort for a Type Editor.<\/p>\n<p><strong>Note:<\/strong> It&#8217;s important at this point, that you are comfortable with the\nfundamentals of <strong>ViewModels<\/strong>, <strong>sessions<\/strong> and <strong>proxy classes<\/strong> in the\ncontext of the Out-of-process Designer. You may review the section <a href=\"#type-editors-in-the-out-of-process-winforms-designer\">Type Editors\nin the Out-of-Process WinForms\nDesigner<\/a> for more\ndetails.<\/p>\n<ul>\n<li>\n<p>Since the Designer must be notified that there is now a new client-side\nViewModel, the correlating factory class <code>Factory<\/code> needs to be a) implemented,\nb) derived from <code>ViewModelClientFactory&lt;TemplateAssignmentViewModelClient&gt;<\/code>\nand c) annotated with the\n<code>ExportViewModelClientFactory(ViewModelNames.TemplateAssignmentViewModel)<\/code>\nattribute.<\/p>\n<\/li>\n<li>\n<p>The constructor of our client-side ViewModel class is private. The reason for\nthis is: Only the factory class should be able to create it. The constructor\nalso takes an argument of type <code>ObjectProxy<\/code>, and that is the proxy class of\nthe server-side ViewModel. To get that, the constructor of our factory class\ncalls the static method <code>Create<\/code> of our client-side ViewModel which initiates\nthe following:<\/p>\n<ul>\n<li>It gets a <code>DesignerSession<\/code> and the <code>DesignToolsClient<\/code> to be able to\ncommunicate with the DesignToolsServer.<\/li>\n<li>It gets the Endpoint <code>CreateTemplateAssignmentViewModelEndpoint<\/code>, so that it\ncan call a method server-side in which the server-side ViewModel will be\ncreated.<\/li>\n<li>In the <code>CreateTemplateAssignmentViewModelResponse<\/code> class, that ViewModel\nproxy is then returned, and can be used to create the actual client side\nViewModel.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Now, obviously, the types that we need to display in the Dialog cannot be the\nactual <code>Type<\/code> class instances. And, as mentioned before, the reason is the\nclient-side ViewModel runs against .NET Framework 4.7.2 while the types it needs\nto display are .NET types.<\/p>\n<p>For that reason, we also need to have the server-side ViewModel, which looks like this:<\/p>\n<pre><code class=\"language-csharp\">using Microsoft.DotNet.DesignTools.ViewModels;\nusing System;\nusing System.ComponentModel;\nusing System.ComponentModel.Design;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing WinForms.Tiles.ClientServerProtocol;\nusing WinForms.Tiles.Designer.Protocol.Endpoints;\n\nnamespace WinForms.Tiles.Designer.Server\n{\n    internal partial class TemplateAssignmentViewModel : ViewModel\n    {\n        private ITypeDiscoveryService? _typeResolutionService;\n\n        private const string SystemNamespace = \"System\";\n        private const string MicrosoftNamespace = \"Microsoft\";\n        private const string AccessibilityNamespace = \"Accessibility\";\n\n        private static readonly Type s_tileContentType = typeof(TileContent);\n        private static readonly string[] s_systemAssembliesFilter = new[]\n        {\n            AccessibilityNamespace,\n            $\"{SystemNamespace}.\",\n            $\"{MicrosoftNamespace}.\"\n        };\n\n        public TemplateAssignmentViewModel(IServiceProvider provider)\n            : base(provider)\n        {\n        }\n\n        public CreateTemplateAssignmentViewModelResponse Initialize(object templateAssignment)\n        {\n            \/\/ Here in the Server process, we first get the list of potential template types...\n            TemplateTypeList = GetTemplateTypelist();\n\n            \/\/ ...and then every type which is derived from 'Tile'.\n            TileContentTypeList = GetTileTypeList();\n\n            this.TemplateAssignment = (TemplateAssignment)templateAssignment;\n\n            return new CreateTemplateAssignmentViewModelResponse(this, TemplateTypeList, TileContentTypeList);\n        }\n\n        private TypeInfoData[] GetTemplateTypelist()\n        {\n            _typeResolutionService ??= GetRequiredService&lt;ITypeDiscoveryService&gt;();\n\n            var types = _typeResolutionService.GetTypes(typeof(object), true)\n                .Cast&lt;Type&gt;()\n\n            .\n            .\n            .\n\n            return types.ToArray();\n        }\n        private TypeInfoData[] GetTileTypeList()\n        {\n            _typeResolutionService ??= GetRequiredService&lt;ITypeDiscoveryService&gt;();\n\n            .\n            .\n            .\n\n            return types.ToArray();\n        }\n\n        \/\/ When we reach this, TemplateQualifiedTypename as well as\n        \/\/ TileContentQualifiedTypename have been set by the Client-\n        \/\/ ViewModel (see there).\n        internal void OKClick()\n        {\n            \/\/ Create a new Instance of the TemplateAssignment:\n            var templateType = Type.GetType(TemplateQualifiedTypename!);\n            var tileContentType = Type.GetType(TileContentQualifiedTypename!);\n            TemplateAssignment = new(templateType, tileContentType);\n        }\n\n        [AllowNull]\n        public TypeInfoData[]? TemplateTypeList { get; private set; }\n\n        [AllowNull]\n        public TypeInfoData[]? TileContentTypeList { get; private set; }\n\n        public string? TemplateQualifiedTypename { get; set; }\n        public string? TileContentQualifiedTypename { get; set; }\n\n        [AllowNull]\n        public TemplateAssignment TemplateAssignment { get; set; }\n    }\n}<\/code><\/pre>\n<p>You can see that the actual domain-specific code has not changed at all. The\nonly thing the server-side ViewModel has in addition to the .NET Framework\nversion is that the public properties providing the type-results-list are no\nlonger based on the actual type classes, but rather on the data transport\nclasses, which can be understood by both target framework versions.<\/p>\n<p>So, from that the question derives: Where are those transport classes defined and how can we get the type-information from the server to the client side?<\/p>\n<h3>Providing the protocol classes for communication between client and server<\/h3>\n<p>This brings us to the point where the protocol classes take the stage. The protocol project contains the entire infrastructure to transfer required information between the two processes via JSON-RPC.<\/p>\n<ul>\n<li>It holds transport classes which carry data for those types which can not be\nused by both target frameworks.<\/li>\n<li>It provides the serialization mechanism for those classes from and to JSON.<\/li>\n<li>It defines the endpoint definitions and the parameter classes (<code>Request<\/code> and\n<code>Response<\/code>) for those endpoints.<\/li>\n<\/ul>\n<p>To stay with our specific TileRepeater example: Instead of presenting the type\ninformation as a string representation directly in the UI, the protocol project\nprovides two classes named <code>TypeInfoData<\/code> and <code>TemplateAssignmentItemData<\/code> that\ncontain necessary type information in a way that is compatible for both target\nframework versions. By the way: this is also the reason why the protocol\nproject&#8217;s target framework is <code>.NET Standard 2.0<\/code>. The resulting assembly\ncontains only types that can be projected to both assembly target framework\nversions of client and server and thus can be used compatibly. Let&#8217;s look at the\n<code>TypeInfoData<\/code> class:<\/p>\n<pre><code class=\"language-csharp\">using Microsoft.DotNet.DesignTools.Protocol.DataPipe;\nusing System;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace WinForms.Tiles.ClientServerProtocol\n{\n    [DebuggerDisplay(\"{\" + nameof(GetDebuggerDisplay) + \"(),nq}\")]\n    public partial class TypeInfoData : IDataPipeObject\n    {\n        [AllowNull]\n        public string AssemblyFullName { get; private set; }\n\n        [AllowNull]\n        public string Namespace { get; private set; }\n\n        [AllowNull]\n        public string FullName { get; private set; }\n\n        [AllowNull]\n        public string AssemblyQualifiedName { get; private set; }\n\n        public bool ImplementsINotifyPropertyChanged { get; private set; }\n\n        [AllowNull]\n        public string Name { get; private set; }\n\n        public TypeInfoData()\n        {\n        }\n\n        public TypeInfoData(\n            string assemblyFullName,\n            string @namespace,\n            string fullName,\n            string assemblyQualifiedName,\n            bool implementsINotifyPropertyChanged,\n            string name)\n        {\n            AssemblyFullName = assemblyFullName ?? throw new ArgumentNullException(nameof(assemblyFullName));\n            Namespace = @namespace ?? throw new ArgumentNullException(nameof(@namespace));\n            FullName = fullName ?? throw new ArgumentNullException(nameof(fullName));\n            AssemblyQualifiedName = assemblyQualifiedName ?? throw new ArgumentNullException(nameof(assemblyQualifiedName));\n            ImplementsINotifyPropertyChanged = implementsINotifyPropertyChanged;\n            Name = name ?? throw new ArgumentNullException(nameof(name));\n        }\n\n        public static TypeInfoData CreateMissingTypeInfoData(string assemblyQualifiedName, string name)\n            =&gt; new(\n                assemblyFullName: string.Empty,\n                @namespace: string.Empty,\n                fullName: string.Empty,\n                assemblyQualifiedName: assemblyQualifiedName,\n                implementsINotifyPropertyChanged: false,\n                name: name);\n\n        public void ReadProperties(IDataPipeReader reader)\n        {\n            AssemblyFullName = reader.ReadString(nameof(AssemblyFullName));\n            FullName = reader.ReadString(nameof(FullName));\n            AssemblyQualifiedName = reader.ReadString(nameof(AssemblyQualifiedName));\n            Namespace = reader.ReadString(nameof(Namespace));\n            ImplementsINotifyPropertyChanged = reader.ReadBooleanOrFalse(nameof(ImplementsINotifyPropertyChanged));\n            Name = reader.ReadString(nameof(Name));\n        }\n\n        public void WriteProperties(IDataPipeWriter writer)\n        {\n            writer.Write(nameof(AssemblyFullName), AssemblyFullName);\n            writer.Write(nameof(FullName), FullName);\n            writer.Write(nameof(AssemblyQualifiedName), AssemblyQualifiedName);\n            writer.Write(nameof(Namespace), Namespace);\n            writer.WriteIfNotFalse(nameof(ImplementsINotifyPropertyChanged), ImplementsINotifyPropertyChanged);\n            writer.Write(nameof(Name), Name);\n        }\n\n        private string GetDebuggerDisplay()\n            =&gt; $\"{Name}: {Namespace}, {AssemblyFullName} INPC:{(ImplementsINotifyPropertyChanged ? \"Yes\" : \"No\")}\";\n    }\n}<\/code><\/pre>\n<p>When implementing data classes to transport results back and forth from and to\nthe server-process, keep this in mind:<\/p>\n<ul>\n<li>Transport classes need to implement the <code>IDataPipeObject<\/code> interface. This\ninterface ensures that the necessary methods for converting the content of\nthe class from (<code>ReadProperties(IDataPipeReader reader)<\/code>) and to\n(<code>WriteProperties(IDataPipeWriter writer)<\/code>) JSON are implemented. You need to\nmake sure that the properties are written in the same order as they are being\nread, otherwise the deserialization of an object&#8217;s content would fail.<\/li>\n<li>You can only use data types which are part of .NET Standard 2.1.<\/li>\n<li>If property types are not nullable, they should be annotated with the\n<code>AllowNull<\/code> attribute (thus <em>allowing<\/em> the compiler to accept a type to be\nnull for a while, if you will). The reason is that every transport class needs\na public, parameterless constructor. Technically, there would be a chance that\nthe properties are not initialized. But the Designer takes care of that during\nthe deserialization process. Still, you don&#8217;t want to make the properties\nnullable just because of that semantic convention.<\/li>\n<\/ul>\n<p><strong>Note:<\/strong> In some circumstances it is necessary that you display a rendered\nresult from a control or component, which is only available server-side. In that\ncase, you would need to create an endpoint which renders that result into a\nbitmap, and then implement a respective transport class, which would serialize\nand transfer the content of that bitmap between the processes by byte array.<\/p>\n<h3>Implementing endpoints for server-side functionality<\/h3>\n<p>When the transport classes are in place, the endpoints need to be implemented.\nThe classes which pass the arguments from and to the endpoint handlers are part\nof the protocol assembly along with their endpoint definitions. The actual\nmethods which will be executed server-side are the endpoint handler classes, and\nbecause they need to have access to the actual control types and the control&#8217;s\nproperty types, they are part of the server-side designer assemblies, which\nreference the control library.<\/p>\n<ul>\n<li>For creating the server-side ViewModel, which also retrieves the list of\ntypes at the same time, we have the\n<code>CreateTemplateAssignmentViewModelEndpoint<\/code>. The <code>Request<\/code> and <code>Response<\/code>\nclasses for that endpoint are defined in the\n<em>TileRepeater.ClientServer.Protocol<\/em> project.<\/p>\n<ul>\n<li><strong><code>CreateTemplateAssignmentViewModelRequest<\/code><\/strong>: This class needs to take the\nsession ID and the Object Proxy of the actual control, and passes it to the\n<code>CreateTemplateAssignmentViewModelHandler<\/code> class in the server.<\/li>\n<li><strong><code>CreateTemplateAssignmentViewModelResponse<\/code><\/strong>: This class gets the\nserver-side ViewModel, the list with template types and the list with\nTile-user-control types, and returns it to the client ViewModel.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/12\/EndpointClassesInProtocolAndServerProjects.png\" alt=\"Screenshot of the protocol and server endpoint classes in the solution explorer\" \/><\/p>\n<ul>\n<li>For notifying the server ViewModel that the <em>OK<\/em>-Button of the Type Editor was\nclicked and the selected Template\/Tile-user-control combination now needs to\nbe committed as the new <code>TemplateAssignment<\/code> property value, we have the\n<code>TemplateAssignmentEditorOKClickEndpoint<\/code>.<\/p>\n<ul>\n<li><strong><code>TemplateAssignmentEditorOKClickRequest<\/code><\/strong>: This class just passes the\nserver-side ViewModel, so the handler knows, which ViewModel instance to\nexecute the <code>OKClick<\/code> method on.<\/li>\n<li><strong><code>TemplateAssignmentEditorOKClickResponse<\/code><\/strong>: This class doesn&#8217;t carry any\nadditional parameter. But it must be there to fullfil the required\nconvention.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>The actual methods that are getting executed are defined in the server-side\nDesigner assembly, which is made from the project\n<em>TileRepeater.Designer.Server<\/em>. They basically call the respective methods in\nthe server-side ViewModel. Let&#8217;s look at the implementation of how the\n<code>CreateTemplateAssignmentViewModelEndpoint<\/code> is generating the ViewModel and\nreturns the list of types back to Visual Studio&#8217;s client process (see comments\nof listing):<\/p>\n<pre><code class=\"language-csharp\">using Microsoft.DotNet.DesignTools.Protocol.Endpoints;\nusing WinForms.Tiles.Designer.Protocol.Endpoints;\n\nnamespace WinForms.Tiles.Designer.Server.Handlers\n{\n    [ExportRequestHandler(EndpointNames.CreateTemplateAssignmentViewModel)]\n    internal class CreateTemplateAssignmentViewModelHandler \n        : RequestHandler&lt;CreateTemplateAssignmentViewModelRequest, CreateTemplateAssignmentViewModelResponse&gt;\n    {\n        public override CreateTemplateAssignmentViewModelResponse HandleRequest(CreateTemplateAssignmentViewModelRequest request)\n        {\n            \/\/ We need the DesignerHost to have the Designer-infrastructure create our server-side ViewModel:\n            \/\/ We get the DesignerHost for the session that is correlating to the open design document in VS\n            \/\/ by the session ID:\n            var designerHost = GetDesignerHost(request.SessionId);\n\n            \/\/ With the designerHost instance, we're now able to have the server-side ViewModel created.\n            var viewModel = CreateViewModel&lt;TemplateAssignmentViewModel&gt;(designerHost);\n\n            \/\/ The ViewModel now not only creates the list of types we need to show \"on the other side\";\n            \/\/ it also creates the necessary response class, which wraps everything - so with this one line,\n            \/\/ we're returning the server-side ViewModel (as a proxy) and both of the type lists in one go.\n            return viewModel.Initialize(request.TileRepeaterProxy!);\n        }\n    }\n}<\/code><\/pre>\n<p><strong>NOTE:<\/strong> Whenever you need to create a new Endpoint to execute an endpoint\nhandler on the server-side, keep this checklist in mind:<\/p>\n<ul>\n<li>In the protocol project, define the <code>Endpoint<\/code>, <code>Request<\/code> and <code>Response<\/code>\nclasses.<\/li>\n<li>Make sure, <code>Request<\/code> and <code>Response<\/code> class are inherited from those base\nclasses.<\/li>\n<li>As data transport properties in <code>Request<\/code> or <code>Response<\/code> classes, only use\ntypes which are defined by the .NET Standard 2.0 target framework. If you\nneed classes specific to your control which only run in the context of the\nserver target framework, you need create wrapper classes. These wrapper classes\nalso need to be defined in the project <em>ClientServerProtocol<\/em>, and must\nimplement <code>IDataPipeObject<\/code> so the content can be serialized to JSON and\ntransported across the process-boundaries. Alternatively (or in addition),\nimplement results which need to be rendered on the Type Editor UI by providing\nbitmaps, and transport those as byte-arrays back to the client, where they can\nconverted back to bitmaps and rendered on the surface of the Type Editor\ndialog.<\/li>\n<li>Implement the respective endpoint handler classes, which are representing the\nactual method to be executed in the context of the server-assembly (with full\naccess to the custom control&#8217;s types).<\/li>\n<li>Make sure, you annotate each endpoint handler class with the\n<code>ExportRequestHandler(EndpointNames.EndpointName)<\/code>. See the code above for an\nexample.<\/li>\n<li>Make sure to maintain the list of endpoint names in <em>EndpointNames.cs<\/em>, the\nlist of editors in <em>EditorNames.cs<\/em> and the names of the ViewModels in\n<em>ViewModelNames.cs<\/em>.<\/li>\n<li>And last but not least: Make sure the Type Routing for Type Editors is\nconfigured correctly. How that works is described in the following section.<\/li>\n<\/ul>\n<h3>Setting up the Type Routing where it applies<\/h3>\n<p>When you are converting Type Editors from .NET Framework to .NET, there is one\nadditional aspect to keep in mind: Usually, the designer host (normally the\nWinForms In-process Designer) finds Type Editors for types which need a\ndedicated UI by their <code>EditorAttribute<\/code>. In the out-of-process Designer, this is\nnot the case. The problem is that the Property Browser needs to examine the type\nto detect if it contains the <code>EditorAttribute<\/code>. But it can&#8217;t, since the type\nneeding that attribute is not a .NET Framework Type. To overcome this, we need\nto implement client-side type routing providers for each Type Editor like this:<\/p>\n<pre><code class=\"language-c#\">using Microsoft.DotNet.DesignTools.Client.TypeRouting;\nusing System.Collections.Generic;\nusing WinForms.Tiles.Designer.Protocol;\n\nnamespace WinForms.Tiles.Designer.Client\n{\n    [ExportTypeRoutingDefinitionProvider]\n    internal class TypeRoutingProvider : TypeRoutingDefinitionProvider\n    {\n        public override IEnumerable&lt;TypeRoutingDefinition&gt; GetDefinitions()\n        {\n            return new[]\n            {\n                new TypeRoutingDefinition(\n                    TypeRoutingKinds.Editor, \n                    nameof(EditorNames.TemplateAssignmentEditor), \n                    typeof(TemplateAssignmentEditor)),\n            };\n        }\n    }\n}<\/code><\/pre>\n<p>This is important. If that routing is missing, the client-side Designer process\nisn&#8217;t able to establish the relation to the server-side definition of a type&#8217;s\neditor, and the adherence on the client side won&#8217;t take place: The property grid\ncell would be missing the <em>&#8230;<\/em> button to call the type editor, which could\nnever be called.<\/p>\n<h3>Creating of the Designer\/Control Library NuGet package and using it in the Demo App<\/h3>\n<p>During Designer startup, Visual Studio&#8217;s Designer-Startup-Service checks for\nout-of-process Control Designer NuGet packages that need to be loaded into the\ncontext of the two Designer processes. The structure of these NuGet packages is\nan implementation detail defined by the Type Editors&#8217; Solution Templates. (If\nyou are interested in exactly how this structure looks like, just examine a\ngenerated NuGet more closely: Since a NuGet is basically nothing more than a\ncollection of files in a certain order structure packaged as a ZIP package, you\ncan simply rename the extension of a NuGet package to .zip, and then see the\nfolder structure in plain text.)<\/p>\n<p>What&#8217;s important to know when you&#8217;re developing a control library: It&#8217;s likely\nthat you want to test immediately after a change to a control or its designer\nfunctionality whether the changes had the desired effect. To do this, you need\nanother project in the solution that consumes the control library, and you need\nto make sure of the following:<\/p>\n<ul>\n<li>\n<p><strong>Never reference the control library from your test project directly<\/strong> as a\nproject reference. Always and only reference the NuGet package.<\/p>\n<\/li>\n<li>\n<p>The solution template sets up the generation of the NuGet package in a way so\nit can be easily detected from a local package source. This definition is done\nin the solution folder <em>Solution_Items<\/em> with the file NuGet.Config. It points\nto the folder which will contain the respective next version of the NuGet\nPackage after a successful build:<\/p>\n<pre><code class=\"language-csharp\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;configuration&gt;\n  &lt;packageSources&gt;\n    &lt;add key=\"local\" value=\".\\NuGet\\BuildOut\" \/&gt;\n  &lt;\/packageSources&gt;\n  &lt;disabledPackageSources \/&gt;\n&lt;\/configuration&gt;<\/code><\/pre>\n<p>It&#8217;s important to know in this context that <strong>every new build will generate a\nNuGet package with a new version<\/strong>. And this is important, since your test app\nwill pick up a new version of the NuGet package, when the version number has\nactually changed. If the version number has not changes, the NuGet package\nwill not be reloaded, even if the content of the package has changed and the\nbuild date of the NuGet package is newer.<\/p>\n<\/li>\n<li>\n<p>For that reason, it is also important to not <strong>use a fixed version of the NuGet\npackage during development<\/strong>. You should reference the <em>latest<\/em> version of the\nNuGet package in the consuming project, like this:<\/p>\n<pre><code class=\"language-csharp\">  &lt;ItemGroup&gt;\n    &lt;PackageReference Include=\"TileRepeater.Package\" Version=\"*\" \/&gt;\n  &lt;\/ItemGroup&gt;<\/code><\/pre>\n<\/li>\n<li>\n<p>And then lastly, please make sure to <strong>setup the build dependencies\ncorrectly<\/strong>:<\/p>\n<ul>\n<li>Client- and Server Projects should reference the Protocol library.<\/li>\n<li>The Server-project needs to reference the actual Control Library in\naddition, since it needs to handle those types in the server-side Designer\nprocess.<\/li>\n<li>The NuGet-Package-Project should have dependencies on all the involved\nprojects, so a rebuild of the NuGet package would trigger a rebuild of all\nthe involved projects.<\/li>\n<li>And then your test project which consumes Control Library and its Control\nDesigners should only reference the NuGet package, and also shouldn&#8217;t have\nany further build dependencies. This is important, so that a new version\nwill only be pulled in, when you rebuild the NuGet-Package. And you need to\nbuild NuGet package and test app always only independently of each other.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2>Final thoughts<\/h2>\n<p>Migrating a .NET Framework Control Designer to .NET is an easy and straight\nforward process in most of the aspects of a Control Designer&#8217;s functional areas.\nThat said, this doesn&#8217;t necessarily count for Type Editors which need more work\nto make them integrate in the client-server infrastructure. The template should\nhelp in the process, and as soon as the first type editor of a control library\ncan be used successfully, the migration or integration of all following type\neditors for further types of the library is much faster.<\/p>\n<p>As always: Feedback about the subject matter is really important to us, so\nplease let us know your thoughts, and about your experiences with migrating\nexisting .NET Framework Control Libraries to .NET or creating new ones from\nscratch. Also, we&#8217;re interested about what additional topics of this area you\nwould like hear about from us. Please also note that the WinForms .NET runtime\nis open source, and you can contribute! If you have ideas, encountered bugs, or\neven want to take on PRs around the WinForms runtime, have a look at the\n<a href=\"https:\/\/github.com\/dotnet\/winforms\">WinForms Github repo<\/a>. If you have\nsuggestions around the WinForms Designer, feel free to file new issues there as\nwell.<\/p>\n<p>Happy coding!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A rich user control ecosystem has always been one of the most important WinForms success guarantors. While the runtime support for Custom Controls remains unchanged, there are breaking changes with the design time support for the new Windows Forms (WinForms) .NET Designer.<\/p>\n","protected":false},"author":9483,"featured_media":43655,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,756,7199,7163],"tags":[],"class_list":["post-43635","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-csharp","category-visual-basic","category-winforms"],"acf":[],"blog_post_summary":"<p>A rich user control ecosystem has always been one of the most important WinForms success guarantors. While the runtime support for Custom Controls remains unchanged, there are breaking changes with the design time support for the new Windows Forms (WinForms) .NET Designer.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/43635","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=43635"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/43635\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/43655"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=43635"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=43635"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=43635"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}