diff --git a/Samples/CommandBinding/MauiEdit.WinForms/MauiEdit.WinForms.csproj b/Samples/CommandBinding/MauiEdit.WinForms/MauiEdit.WinForms.csproj index 5263f9d..2e045e9 100644 --- a/Samples/CommandBinding/MauiEdit.WinForms/MauiEdit.WinForms.csproj +++ b/Samples/CommandBinding/MauiEdit.WinForms/MauiEdit.WinForms.csproj @@ -1,8 +1,9 @@  - true - WinExe + true + true + WinExe net7.0-windows true 10.0 diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo.sln b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo.sln new file mode 100644 index 0000000..64e66f8 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33209.295 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RootDesignerDemo", "RootDesignerDemo\RootDesignerDemo.csproj", "{5887C1E1-5924-4A07-84F8-BA9C616F4BAF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RootDesignerSupportLib", "RootDesignerSupportLib\RootDesignerSupportLib.csproj", "{548CC959-438A-4BCB-9AF3-D0F019F4C3BC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5887C1E1-5924-4A07-84F8-BA9C616F4BAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5887C1E1-5924-4A07-84F8-BA9C616F4BAF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5887C1E1-5924-4A07-84F8-BA9C616F4BAF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5887C1E1-5924-4A07-84F8-BA9C616F4BAF}.Release|Any CPU.Build.0 = Release|Any CPU + {548CC959-438A-4BCB-9AF3-D0F019F4C3BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {548CC959-438A-4BCB-9AF3-D0F019F4C3BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {548CC959-438A-4BCB-9AF3-D0F019F4C3BC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {548CC959-438A-4BCB-9AF3-D0F019F4C3BC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {48A19B94-7503-4753-AEA9-A7C03F2EF695} + EndGlobalSection +EndGlobal diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/App.config b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/App.config new file mode 100644 index 0000000..e376a0e --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/App.config @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Form1.Designer.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Form1.Designer.cs new file mode 100644 index 0000000..77e4fe1 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Form1.Designer.cs @@ -0,0 +1,164 @@ +namespace RootDesignerDemo +{ + partial class Form3 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.lkjlkjhlkfhjlkfToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.hkkjhkjhkjkjhkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.kjhkjhkjhkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.klkjlkjljToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.comboBox1 = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.textBoxEx1 = new RootDesignerDemo.Controls.TextBoxEx(); + this.textBoxEx2 = new RootDesignerDemo.Controls.TextBoxEx(); + this.menuStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // menuStrip1 + // + this.menuStrip1.ImageScalingSize = new System.Drawing.Size(32, 32); + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.lkjlkjhlkfhjlkfToolStripMenuItem, + this.hkkjhkjhkjkjhkToolStripMenuItem}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Padding = new System.Windows.Forms.Padding(4, 2, 0, 2); + this.menuStrip1.Size = new System.Drawing.Size(721, 28); + this.menuStrip1.TabIndex = 0; + this.menuStrip1.Text = "menuStrip1"; + // + // lkjlkjhlkfhjlkfToolStripMenuItem + // + this.lkjlkjhlkfhjlkfToolStripMenuItem.Name = "lkjlkjhlkfhjlkfToolStripMenuItem"; + this.lkjlkjhlkfhjlkfToolStripMenuItem.Size = new System.Drawing.Size(105, 24); + this.lkjlkjhlkfhjlkfToolStripMenuItem.Text = "lkjlkjhlkfhjlkf"; + // + // hkkjhkjhkjkjhkToolStripMenuItem + // + this.hkkjhkjhkjkjhkToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.kjhkjhkjhkToolStripMenuItem, + this.klkjlkjljToolStripMenuItem}); + this.hkkjhkjhkjkjhkToolStripMenuItem.Name = "hkkjhkjhkjkjhkToolStripMenuItem"; + this.hkkjhkjhkjkjhkToolStripMenuItem.Size = new System.Drawing.Size(113, 24); + this.hkkjhkjhkjkjhkToolStripMenuItem.Text = "hkkjhkjhkjkjhk"; + // + // kjhkjhkjhkToolStripMenuItem + // + this.kjhkjhkjhkToolStripMenuItem.Name = "kjhkjhkjhkToolStripMenuItem"; + this.kjhkjhkjhkToolStripMenuItem.Size = new System.Drawing.Size(156, 26); + this.kjhkjhkjhkToolStripMenuItem.Text = "kjhkjhkjhk"; + // + // klkjlkjljToolStripMenuItem + // + this.klkjlkjljToolStripMenuItem.Name = "klkjlkjljToolStripMenuItem"; + this.klkjlkjljToolStripMenuItem.Size = new System.Drawing.Size(156, 26); + this.klkjlkjljToolStripMenuItem.Text = "klkjlkjlj"; + // + // checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Location = new System.Drawing.Point(147, 267); + this.checkBox1.Margin = new System.Windows.Forms.Padding(4); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(95, 20); + this.checkBox1.TabIndex = 1; + this.checkBox1.Text = "checkBox1"; + this.checkBox1.UseVisualStyleBackColor = true; + // + // comboBox1 + // + this.comboBox1.FormattingEnabled = true; + this.comboBox1.Location = new System.Drawing.Point(349, 267); + this.comboBox1.Margin = new System.Windows.Forms.Padding(4); + this.comboBox1.Name = "comboBox1"; + this.comboBox1.Size = new System.Drawing.Size(315, 24); + this.comboBox1.TabIndex = 2; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(148, 100); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(44, 16); + this.label1.TabIndex = 3; + this.label1.Text = "label1"; + // + // textBoxEx1 + // + this.textBoxEx1.Location = new System.Drawing.Point(157, 151); + this.textBoxEx1.Name = "textBoxEx1"; + this.textBoxEx1.Size = new System.Drawing.Size(377, 22); + this.textBoxEx1.TabIndex = 4; + // + // textBoxEx2 + // + this.textBoxEx2.Location = new System.Drawing.Point(228, 239); + this.textBoxEx2.Name = "textBoxEx2"; + this.textBoxEx2.Size = new System.Drawing.Size(100, 22); + this.textBoxEx2.TabIndex = 5; + // + // Form3 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(721, 331); + this.Controls.Add(this.textBoxEx2); + this.Controls.Add(this.textBoxEx1); + this.Controls.Add(this.label1); + this.Controls.Add(this.comboBox1); + this.Controls.Add(this.checkBox1); + this.Controls.Add(this.menuStrip1); + this.MainMenuStrip = this.menuStrip1; + this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.Name = "Form3"; + this.Text = "Form1"; + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private TextDataTextBoxComponent.TextDataTextBoxComponent textDataTextBoxComponent1; + private System.Windows.Forms.MenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem lkjlkjhlkfhjlkfToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem hkkjhkjhkjkjhkToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem kjhkjhkjhkToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem klkjlkjljToolStripMenuItem; + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.ComboBox comboBox1; + private System.Windows.Forms.Label label1; + private Controls.TextBoxEx textBoxEx1; + private Controls.TextBoxEx textBoxEx2; + } +} + diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Form1.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Form1.cs new file mode 100644 index 0000000..2512b68 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Form1.cs @@ -0,0 +1,12 @@ +using System.Windows.Forms; + +namespace RootDesignerDemo +{ + public partial class Form3 : Form + { + public Form3() + { + InitializeComponent(); + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Form1.resx b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Form1.resx new file mode 100644 index 0000000..25af2b6 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Form1.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 488, 17 + + \ No newline at end of file diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/HostingComponents/SimpleBoxComponent.Designer.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/HostingComponents/SimpleBoxComponent.Designer.cs new file mode 100644 index 0000000..54ca929 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/HostingComponents/SimpleBoxComponent.Designer.cs @@ -0,0 +1,36 @@ +namespace RootDesignerDemo.HostingComponents +{ + partial class SimpleBoxComponent + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/HostingComponents/SimpleBoxComponent.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/HostingComponents/SimpleBoxComponent.cs new file mode 100644 index 0000000..0ac538c --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/HostingComponents/SimpleBoxComponent.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RootDesignerDemo.HostingComponents +{ + public partial class SimpleBoxComponent : Component + { + public SimpleBoxComponent() + { + InitializeComponent(); + } + + public SimpleBoxComponent(IContainer container) + { + container.Add(this); + + InitializeComponent(); + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Program.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Program.cs new file mode 100644 index 0000000..e7e63f3 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace RootDesignerDemo +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form3()); + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/AssemblyInfo.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..3a87c32 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RootDesignerDemo")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RootDesignerDemo")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5887c1e1-5924-4a07-84f8-ba9c616f4baf")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Resources.Designer.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Resources.Designer.cs new file mode 100644 index 0000000..6a51467 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RootDesignerDemo.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RootDesignerDemo.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Resources.resx b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Settings.Designer.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Settings.Designer.cs new file mode 100644 index 0000000..42e0174 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RootDesignerDemo.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Settings.settings b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/RootDesignedComponent.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/RootDesignedComponent.cs new file mode 100644 index 0000000..e974530 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/RootDesignedComponent.cs @@ -0,0 +1,19 @@ +using System.ComponentModel; +using System.ComponentModel.Design; + +namespace SampleRootDesigner +{ + // The following attribute associates the SampleRootDesigner designer + // with the SampleComponent component. + [Designer(typeof(SampleRootDesigner), typeof(IRootDesigner))] + public class RootDesignedComponent : Component + { + public RootDesignedComponent() + { + } + + private void InitializeComponent() + { + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/RootDesignedComponent.resx b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/RootDesignedComponent.resx new file mode 100644 index 0000000..e5858cc --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/RootDesignedComponent.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + \ No newline at end of file diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/RootDesignerDemo.csproj b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/RootDesignerDemo.csproj new file mode 100644 index 0000000..45aa50b --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/RootDesignerDemo.csproj @@ -0,0 +1,93 @@ + + + + + Debug + AnyCPU + {5887C1E1-5924-4A07-84F8-BA9C616F4BAF} + WinExe + RootDesignerDemo + RootDesignerDemo + v4.8.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + Form + + + Form1.cs + + + + + Component + + + Form1.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + {548CC959-438A-4BCB-9AF3-D0F019F4C3BC} + RootDesignerSupportLib + + + + \ No newline at end of file diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/SampleRootDesigner.RootDesignerView.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/SampleRootDesigner.RootDesignerView.cs new file mode 100644 index 0000000..6334eaf --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/SampleRootDesigner.RootDesignerView.cs @@ -0,0 +1,30 @@ +using System.Drawing; +using System.Windows.Forms; + +namespace SampleRootDesigner +{ + public partial class SampleRootDesigner + { + // RootDesignerView is a simple control that will be displayed + // in the designer window. + private class RootDesignerView : Control + { + private SampleRootDesigner m_designer; + + public RootDesignerView(SampleRootDesigner designer) + { + m_designer = designer; + BackColor = Color.Blue; + Font = new Font(Font.FontFamily.Name, 24.0f); + } + + protected override void OnPaint(PaintEventArgs pe) + { + base.OnPaint(pe); + + // Draws the name of the component in large letters. + pe.Graphics.DrawString(m_designer.Component.Site.Name, Font, Brushes.Yellow, ClientRectangle); + } + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/SampleRootDesigner.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/SampleRootDesigner.cs new file mode 100644 index 0000000..a350521 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/SampleRootDesigner.cs @@ -0,0 +1,67 @@ +using System; +using System.ComponentModel.Design; +using System.Drawing.Design; +using System.Windows.Forms; + +namespace SampleRootDesigner +{ + public partial class SampleRootDesigner : ComponentDesigner, IRootDesigner, IToolboxUser, IToolboxItemProvider + { + // Member field of custom type RootDesignerView, a control that + // will be shown in the Forms designer view. This member is + // cached to reduce processing needed to recreate the + // view control on each call to GetView(). + private RootDesignerView m_view; + + // This method returns an instance of the view for this root + // designer. The "view" is the user interface that is presented + // in a document window for the user to manipulate. + object IRootDesigner.GetView(ViewTechnology technology) + { + if (technology != ViewTechnology.Default) + { + throw new ArgumentException("Not a supported view technology", "technology"); + } + + if (m_view == null) + { + // Some type of displayable Form or control is required + // for a root designer that overrides GetView(). In this + // example, a Control of type RootDesignerView is used. + // Any class that inherits from Control will work. + m_view = new RootDesignerView(this); + } + return m_view; + } + + public bool GetToolSupported(ToolboxItem tool) + { + MessageBox.Show("Reached GetToolSupported!"); + return false; + } + + public void ToolPicked(ToolboxItem tool) + { + MessageBox.Show("Reached ToolPicked!"); + } + + // IRootDesigner.SupportedTechnologies is a required override for an + // IRootDesigner. Default is the view technology used by this designer. + ViewTechnology[] IRootDesigner.SupportedTechnologies + { + get + { + return new ViewTechnology[] { ViewTechnology.Default }; + } + } + + public ToolboxItemCollection Items + { + get + { + MessageBox.Show("Reached Toolbox-Items requested!"); + return null; + } + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/ShapeDocument.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/ShapeDocument.cs new file mode 100644 index 0000000..a87e82b --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerDemo/ShapeDocument.cs @@ -0,0 +1,14 @@ +namespace SampleRootDesigner +{ + // This sample demonstrates how to provide the root designer view, or + // design mode background view, by overriding IRootDesigner.GetView(). + + // This sample component inherits from RootDesignedComponent which + // uses the SampleRootDesigner. + public class ShapeDocument : ShapeDocumentBase + { + public ShapeDocument() + { + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Controls/Controls.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Controls/Controls.cs new file mode 100644 index 0000000..01ef2bc --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Controls/Controls.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; +using System.Windows.Forms; + +namespace RootDesignerDemo.Controls +{ + [ToolboxItemFilter("System.Windows.Forms")] + public class TextBoxEx : TextBox + { + } + + public class ButtonEx : Button + { + } + + public class LabelEx : Label + { + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Properties/AssemblyInfo.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9e27f4a --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RootDesignerSupportLib")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RootDesignerSupportLib")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("548cc959-438a-4bcb-9af3-d0f019f4c3bc")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Properties/Resources.Designer.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Properties/Resources.Designer.cs new file mode 100644 index 0000000..062994b --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Properties/Resources.Designer.cs @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RootDesignerDemo.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RootDesignerDemo.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Line { + get { + object obj = ResourceManager.GetObject("Line", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Rectangle { + get { + object obj = ResourceManager.GetObject("Rectangle", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Text { + get { + object obj = ResourceManager.GetObject("Text", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Properties/Resources.resx b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Properties/Resources.resx new file mode 100644 index 0000000..f67fbfd --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Properties/Resources.resx @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\Rectangle.bmp;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Line.bmp;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Text.bmp;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Resources/Line.bmp b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Resources/Line.bmp new file mode 100644 index 0000000..89280eb Binary files /dev/null and b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Resources/Line.bmp differ diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Resources/Rectangle.bmp b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Resources/Rectangle.bmp new file mode 100644 index 0000000..9239ce9 Binary files /dev/null and b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Resources/Rectangle.bmp differ diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Resources/Text.bmp b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Resources/Text.bmp new file mode 100644 index 0000000..a8e028f Binary files /dev/null and b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/Resources/Text.bmp differ diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/SampleRootDesigner.RootDesignerView.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/SampleRootDesigner.RootDesignerView.cs new file mode 100644 index 0000000..7796444 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/SampleRootDesigner.RootDesignerView.cs @@ -0,0 +1,60 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace SampleRootDesigner +{ + public partial class ShapeRootDesigner + { + // RootDesignerView is a simple control that will be displayed + // in the designer window. + private class RootDesignerView : Control + { + private ShapeRootDesigner _designer; + private Cursor _savedCursor; + + protected override Cursor DefaultCursor => base.DefaultCursor; + + public RootDesignerView(ShapeRootDesigner designer) + { + _designer = designer; + BackColor = Color.LightGray; + Font = new Font(Font.FontFamily.Name, 24.0f); + } + + protected override void OnMouseEnter(EventArgs e) + { + base.OnMouseEnter(e); + + _savedCursor = Cursor; + _designer.VsOutputWindowLogger.WriteLine($"View: MouseEnter"); + object toolboxItem = _designer.ToolboxService.GetSelectedToolboxItem(); + + if (toolboxItem is null) + { + toolboxItem = "No toolbox item selected."; + } + + if (_designer.ToolboxService.SetCursor()) + { + Cursor = Cursor.Current; + } + } + + protected override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + _designer.VsOutputWindowLogger.WriteLine($"View: MouseLeave"); + Cursor = _savedCursor; + } + + protected override void OnPaint(PaintEventArgs pe) + { + base.OnPaint(pe); + + // Draws the name of the component in large letters. + pe.Graphics.DrawString(_designer.Component.Site.Name, Font, Brushes.Black, ClientRectangle); + } + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/SampleRootDesigner.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/SampleRootDesigner.cs new file mode 100644 index 0000000..6f3b8d0 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/SampleRootDesigner.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Drawing.Design; +using System.Windows.Forms; +using RootDesignerDemo; + +namespace SampleRootDesigner +{ + [ToolboxItemFilter(ToolboxCategory, ToolboxItemFilterType.Require)] + public partial class ShapeRootDesigner : ComponentDesigner, IRootDesigner, IToolboxUser + { + private const string ToolboxCategory = "ShapeRootDesigner"; + private const string VsOutputWindowPaneName = "Root Designer Demo"; + + private VsOutputWindowLogger _vsOutputWindowLogger = new VsOutputWindowLogger(VsOutputWindowPaneName); + + // Member field of custom type RootDesignerView, a control that + // will be shown in the Forms designer view. This member is + // cached to reduce processing needed to recreate the + // view control on each call to GetView(). + private RootDesignerView _designerSurface; + + private IToolboxService _toolboxService = null; + private ToolboxItemCollection _tools; + private ISelectionService _selectionService = null; + private IDesignerHost _designerHost = null; + + public override void Initialize(IComponent component) + { + base.Initialize(component); + _vsOutputWindowLogger.WriteLine($"ShapeRootDesigner.Initialize() for component {component.GetType().FullName} called."); + _selectionService = GetService(typeof(ISelectionService)) as ISelectionService; + if (!(_selectionService is null)) + { + _vsOutputWindowLogger.WriteLine($"ShapeRootDesigner.Initialize(): ISelectionService retrieved."); + } + + _designerHost = GetService(typeof(IDesignerHost)) as IDesignerHost; + if (!(_designerHost is null)) + { + _vsOutputWindowLogger.WriteLine($"ShapeRootDesigner.Initialize(): IDesignerHost retrieved."); + } + + _toolboxService = GetService(typeof(IToolboxService)) as IToolboxService; + if (!(_toolboxService is null)) + { + _vsOutputWindowLogger.WriteLine($"ShapeRootDesigner.Initialize(): IToolboxService retrieved."); + } + } + + public ViewTechnology[] SupportedTechnologies => new[] { ViewTechnology.Default }; + + public bool GetToolSupported(ToolboxItem tool) + { + return !(tool.Properties[ToolboxCategory] is null); + } + + public void ToolPicked(ToolboxItem tool) + { + _vsOutputWindowLogger.WriteLine($"ToolPicked: {tool}"); + } + + // This method returns an instance of the view for this root + // designer. The "view" is the user interface that is presented + // in a document window for the user to manipulate. + object IRootDesigner.GetView(ViewTechnology technology) + { + if (technology != ViewTechnology.Default) + { + throw new ArgumentException("Not a supported view technology", "technology"); + } + + if (_designerSurface == null) + { + // Some type of displayable Form or control is required + // for a root designer that overrides GetView(). In this + // example, a Control of type RootDesignerView is used. + // Any class that inherits from Control will work. + _designerSurface = new RootDesignerView(this); + + _toolboxService = (IToolboxService)this.GetService(typeof(IToolboxService)); + // If an IToolboxService was located, update the + // category list. + if (_toolboxService is null) + { + MessageBox.Show("Couldn't retrieve Toolbox Service!"); + } + else + { + SetupToolboxItems(); + } + } + + return _designerSurface; + } + + private void SetupToolboxItems() + { + CreateToolboxItem("Line Tool", "LineTool", "ShapeRootDesigner", + RootDesignerDemo.Properties.Resources.Line); + CreateToolboxItem("Rectangle Tool", "RectangleTool", "ShapeRootDesigner", + RootDesignerDemo.Properties.Resources.Rectangle); + CreateToolboxItem("Text Tool", "TextTool", "ShapeRootDesigner", + RootDesignerDemo.Properties.Resources.Text); + } + + private void CreateToolboxItem(string toolDisplayName, string toolTypeName, string toolboxFilterString, Bitmap toolboxBitmap) + { + ToolboxItem toolboxItem; + ToolboxItemFilterAttribute toolboxItemFilterAttribute; + ToolboxItemFilterAttribute[] toolboxItemFilterAttributeArray; + + string toolboxItemName =$"{ToolboxCategory}.{toolTypeName}"; + + foreach (ToolboxItem existingToolboxItem in _toolboxService.GetToolboxItems(ToolboxCategory)) + { + if (existingToolboxItem.TypeName != toolboxItemName) + { + continue; + } + + // We found the item, so remove it... + _toolboxService.RemoveToolboxItem(existingToolboxItem); + break; + } + + // ...and add it again. + toolboxItem = new ToolboxItem() + { + TypeName = toolboxItemName, + DisplayName = toolDisplayName, + Description = $"Description of {toolDisplayName}", + Bitmap = toolboxBitmap + }; + + toolboxItemFilterAttribute = new ToolboxItemFilterAttribute(toolboxFilterString, ToolboxItemFilterType.Require); + toolboxItemFilterAttributeArray = new ToolboxItemFilterAttribute[] { toolboxItemFilterAttribute }; + toolboxItem.Filter = (ICollection)toolboxItemFilterAttributeArray; + toolboxItem.Properties[ToolboxCategory] = toolTypeName; + _toolboxService.AddToolboxItem(toolboxItem, ToolboxCategory); + + return; + } + + internal IToolboxService ToolboxService => _toolboxService; + internal IDesignerHost DesignerHost => _designerHost; + internal ISelectionService SelectionService => _selectionService; + internal VsOutputWindowLogger VsOutputWindowLogger => _vsOutputWindowLogger; + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/ShapeDocumentBase.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/ShapeDocumentBase.cs new file mode 100644 index 0000000..02e28c0 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/ShapeDocumentBase.cs @@ -0,0 +1,47 @@ +using System.ComponentModel; +using System.ComponentModel.Design; + +namespace SampleRootDesigner +{ + // The following attribute associates the SampleRootDesigner designer + // with the SampleComponent component. + [Designer(typeof(ShapeRootDesigner), typeof(IRootDesigner)), + ToolboxItem(false)] + public class ShapeDocumentBase : Component + { + private IToolboxServiceExample.IToolboxServiceControl iToolboxServiceControl1; + private RootDesignerDemo.HostingComponents.SimpleBoxComponent simpleBoxComponent1; + private System.Windows.Forms.Button button1; + private IContainer components; + + public ShapeDocumentBase() + { + } + + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.iToolboxServiceControl1 = new IToolboxServiceExample.IToolboxServiceControl(); + this.simpleBoxComponent1 = new RootDesignerDemo.HostingComponents.SimpleBoxComponent(this.components); + this.button1 = new System.Windows.Forms.Button(); + // + // iToolboxServiceControl1 + // + this.iToolboxServiceControl1.BackColor = System.Drawing.Color.Beige; + this.iToolboxServiceControl1.Location = new System.Drawing.Point(500, 400); + this.iToolboxServiceControl1.Name = "iToolboxServiceControl1"; + this.iToolboxServiceControl1.Size = new System.Drawing.Size(771, 432); + this.iToolboxServiceControl1.TabIndex = 0; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(0, 0); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 0; + this.button1.Text = "button1"; + this.button1.UseVisualStyleBackColor = true; + + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/ShapeDocumentBase.resx b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/ShapeDocumentBase.resx new file mode 100644 index 0000000..8e8ff23 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesigner/ShapeDocumentBase.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 225, 291 + + + 849, 394 + + + 1434, 432 + + + True + + \ No newline at end of file diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesignerSupportLib.csproj b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesignerSupportLib.csproj new file mode 100644 index 0000000..5e545b5 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/RootDesignerSupportLib.csproj @@ -0,0 +1,270 @@ + + + + + + Debug + AnyCPU + {548CC959-438A-4BCB-9AF3-D0F019F4C3BC} + Library + Properties + RootDesignerDemo + RootDesignerSupportLib + v4.7.2 + 512 + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\MessagePack.2.4.59\lib\netstandard2.0\MessagePack.dll + + + ..\packages\MessagePack.Annotations.2.4.59\lib\netstandard2.0\MessagePack.Annotations.dll + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\packages\Microsoft.Build.Framework.17.3.1\lib\net472\Microsoft.Build.Framework.dll + + + ..\packages\Microsoft.IO.Redist.6.0.0\lib\net472\Microsoft.IO.Redist.dll + + + ..\packages\Microsoft.NET.StringTools.17.4.0\lib\net472\Microsoft.NET.StringTools.dll + + + ..\packages\Microsoft.ServiceHub.Framework.4.1.3102\lib\netstandard2.0\Microsoft.ServiceHub.Framework.dll + + + ..\packages\Microsoft.VisualStudio.GraphModel.17.5.33428.366\lib\net472\Microsoft.VisualStudio.GraphModel.dll + + + ..\packages\Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.17.5.33428.366\lib\net472\Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll + True + + + ..\packages\Microsoft.VisualStudio.Interop.17.5.33428.366\lib\net472\Microsoft.VisualStudio.Interop.dll + + + ..\packages\Microsoft.VisualStudio.RemoteControl.16.3.44\lib\net45\Microsoft.VisualStudio.RemoteControl.dll + + + ..\packages\Microsoft.VisualStudio.RpcContracts.17.5.21\lib\netstandard2.0\Microsoft.VisualStudio.RpcContracts.dll + + + ..\packages\Microsoft.VisualStudio.Shell.Framework.17.5.33428.388\lib\net472\Microsoft.VisualStudio.Shell.Framework.dll + + + ..\packages\Microsoft.VisualStudio.Shell.Interop.17.5.33428.366\lib\net472\Microsoft.VisualStudio.Shell.Interop.dll + + + ..\packages\Microsoft.VisualStudio.Telemetry.16.6.21\lib\net45\Microsoft.VisualStudio.Telemetry.dll + + + ..\packages\Microsoft.VisualStudio.Threading.17.5.21\lib\net472\Microsoft.VisualStudio.Threading.dll + + + ..\packages\Microsoft.VisualStudio.Utilities.17.5.33428.366\lib\net472\Microsoft.VisualStudio.Utilities.dll + + + ..\packages\Microsoft.VisualStudio.Utilities.Internal.16.3.38\lib\net45\Microsoft.VisualStudio.Utilities.Internal.dll + + + ..\packages\Microsoft.VisualStudio.Validation.17.0.65\lib\netstandard2.0\Microsoft.VisualStudio.Validation.dll + + + ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll + + + ..\packages\Nerdbank.Streams.2.9.112\lib\netstandard2.0\Nerdbank.Streams.dll + + + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + ..\packages\StreamJsonRpc.2.14.24\lib\netstandard2.0\StreamJsonRpc.dll + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\packages\System.Collections.Immutable.6.0.0\lib\net461\System.Collections.Immutable.dll + + + + ..\packages\System.Composition.AttributedModel.6.0.0\lib\net461\System.Composition.AttributedModel.dll + + + ..\packages\System.Composition.Convention.6.0.0\lib\net461\System.Composition.Convention.dll + + + ..\packages\System.Composition.Hosting.6.0.0\lib\net461\System.Composition.Hosting.dll + + + ..\packages\System.Composition.Runtime.6.0.0\lib\net461\System.Composition.Runtime.dll + + + ..\packages\System.Composition.TypedParts.6.0.0\lib\net461\System.Composition.TypedParts.dll + + + + + + ..\packages\System.Diagnostics.DiagnosticSource.6.0.0\lib\net461\System.Diagnostics.DiagnosticSource.dll + + + + + ..\packages\System.IO.Pipelines.6.0.3\lib\net461\System.IO.Pipelines.dll + + + + ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Security.AccessControl.6.0.0\lib\net461\System.Security.AccessControl.dll + + + ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll + + + ..\packages\System.Text.Encodings.Web.6.0.0\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.6.0.0\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.AccessControl.6.0.0\lib\net461\System.Threading.AccessControl.dll + + + ..\packages\System.Threading.Tasks.Dataflow.6.0.0\lib\net461\System.Threading.Tasks.Dataflow.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + Component + + + True + True + Resources.resx + + + Component + + + SimpleBoxComponent.cs + + + UserControl + + + + + + Component + + + Component + + + + + + + IToolboxServiceControl.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + ShapeDocumentBase.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ShapeComponents/SimpleBoxComponent.Designer.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ShapeComponents/SimpleBoxComponent.Designer.cs new file mode 100644 index 0000000..54ca929 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ShapeComponents/SimpleBoxComponent.Designer.cs @@ -0,0 +1,36 @@ +namespace RootDesignerDemo.HostingComponents +{ + partial class SimpleBoxComponent + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ShapeComponents/SimpleBoxComponent.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ShapeComponents/SimpleBoxComponent.cs new file mode 100644 index 0000000..0ac538c --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ShapeComponents/SimpleBoxComponent.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RootDesignerDemo.HostingComponents +{ + public partial class SimpleBoxComponent : Component + { + public SimpleBoxComponent() + { + InitializeComponent(); + } + + public SimpleBoxComponent(IContainer container) + { + container.Add(this); + + InitializeComponent(); + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ToolboxServiceDemo/IToolboxServiceControl.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ToolboxServiceDemo/IToolboxServiceControl.cs new file mode 100644 index 0000000..3f36d8c --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ToolboxServiceDemo/IToolboxServiceControl.cs @@ -0,0 +1,208 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Drawing.Design; +using System.Windows.Forms; + +namespace IToolboxServiceExample +{ + // Provides an example control that functions in design mode to + // demonstrate use of the IToolboxService to list and select toolbox + // categories and items, and to add components or controls + // to the parent form using code. + [DesignerAttribute(typeof(WindowMessageDesigner), typeof(IDesigner))] + public class IToolboxServiceControl : System.Windows.Forms.UserControl + { + private System.Windows.Forms.ListBox listBox1; + private System.Windows.Forms.ListBox listBox2; + private IToolboxService toolboxService = null; + private ToolboxItemCollection tools; + private int controlSpacingMultiplier; + + public IToolboxServiceControl() + { + InitializeComponent(); + listBox2.DoubleClick += new EventHandler(this.CreateComponent); + controlSpacingMultiplier = 0; + } + + // Obtain or reset IToolboxService reference on each siting of control. + public override System.ComponentModel.ISite Site + { + get + { + return base.Site; + } + set + { + base.Site = value; + + // If the component was sited, attempt to obtain + // an IToolboxService instance. + if (base.Site != null) + { + toolboxService = (IToolboxService)this.GetService(typeof(IToolboxService)); + // If an IToolboxService was located, update the + // category list. + if (toolboxService != null) + UpdateLists(); + } + else + { + toolboxService = null; + } + } + } + + // Updates the list of categories and the list of items in the + // selected category. + private void UpdateLists() + { + if (toolboxService != null) + { + this.listBox1.SelectedIndexChanged -= new System.EventHandler(this.UpdateSelectedCategory); + this.listBox2.SelectedIndexChanged -= new System.EventHandler(this.UpdateSelectedItem); + listBox1.Items.Clear(); + for (int i = 0; i < toolboxService.CategoryNames.Count; i++) + { + listBox1.Items.Add(toolboxService.CategoryNames[i]); + if (toolboxService.CategoryNames[i] == toolboxService.SelectedCategory) + { + listBox1.SelectedIndex = i; + tools = toolboxService.GetToolboxItems(toolboxService.SelectedCategory); + listBox2.Items.Clear(); + for (int j = 0; j < tools.Count; j++) + listBox2.Items.Add(tools[j].DisplayName); + } + } + this.listBox1.SelectedIndexChanged += new System.EventHandler(this.UpdateSelectedCategory); + this.listBox2.SelectedIndexChanged += new System.EventHandler(this.UpdateSelectedItem); + } + } + + // Sets the selected category when a category is clicked in the + // category list. + private void UpdateSelectedCategory(object sender, System.EventArgs e) + { + if (toolboxService != null) + { + toolboxService.SelectedCategory = (string)listBox1.SelectedItem; + UpdateLists(); + } + } + + // Sets the selected item when an item is clicked in the item list. + private void UpdateSelectedItem(object sender, System.EventArgs e) + { + if (toolboxService != null) + { + if (listBox1.SelectedIndex != -1) + { + if ((string)listBox1.SelectedItem == toolboxService.SelectedCategory) + toolboxService.SetSelectedToolboxItem(tools[listBox2.SelectedIndex]); + else + UpdateLists(); + } + } + } + + // Creates a control from a double-clicked toolbox item and adds + // it to the parent form. + private void CreateComponent(object sender, EventArgs e) + { + // Obtains an IDesignerHost service from design environment. + IDesignerHost host = (IDesignerHost)this.GetService(typeof(IDesignerHost)); + + // Get the project components container (Windows Forms control + // containment depends on controls collections). + IContainer container = host.Container; + + // Identifies the parent Form. + System.Windows.Forms.Form parentForm = this.FindForm(); + + // Retrieves the parent Form's designer host. + IDesignerHost parentHost = (IDesignerHost)parentForm.Site.GetService(typeof(IDesignerHost)); + + // Create the components. + IComponent[] comps = null; + try + { + comps = toolboxService.GetSelectedToolboxItem().CreateComponents(parentHost); + } + catch (Exception ex) + { + // Catch and show any exceptions to prevent disabling + // the control's UI. + MessageBox.Show(ex.ToString(), "Exception message"); + } + if (comps == null) + return; + + // Add any created controls to the parent form's controls + // collection. Note: components are added from the + // ToolboxItem.CreateComponents(IDesignerHost) method. + for (int i = 0; i < comps.Length; i++) + { + if (parentForm != null && comps[i].GetType().IsSubclassOf(typeof(System.Windows.Forms.Control))) + { + ((System.Windows.Forms.Control)comps[i]).Location = new Point(20 * controlSpacingMultiplier, 20 * controlSpacingMultiplier); + if (controlSpacingMultiplier > 10) + controlSpacingMultiplier = 0; + else + controlSpacingMultiplier++; + parentForm.Controls.Add((System.Windows.Forms.Control)comps[i]); + } + } + } + + // Displays labels. + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) + { + e.Graphics.DrawString("IToolboxService Control", new Font("Arial", 14), new SolidBrush(Color.Black), 6, 4); + e.Graphics.DrawString("Category List", new Font("Arial", 8), new SolidBrush(Color.Black), 8, 26); + e.Graphics.DrawString("Items in Category", new Font("Arial", 8), new SolidBrush(Color.Black), 208, 26); + e.Graphics.DrawString("(Double-click item to add to parent form)", new Font("Arial", 7), new SolidBrush(Color.Black), 232, 12); + } + + private void InitializeComponent() + { + this.listBox1 = new System.Windows.Forms.ListBox(); + this.listBox2 = new System.Windows.Forms.ListBox(); + this.SuspendLayout(); + // + // listBox1 + // + this.listBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.listBox1.ItemHeight = 20; + this.listBox1.Location = new System.Drawing.Point(8, 41); + this.listBox1.Name = "listBox1"; + this.listBox1.Size = new System.Drawing.Size(192, 364); + this.listBox1.TabIndex = 0; + this.listBox1.SelectedIndexChanged += new System.EventHandler(this.UpdateSelectedCategory); + // + // listBox2 + // + this.listBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listBox2.ItemHeight = 20; + this.listBox2.Location = new System.Drawing.Point(208, 41); + this.listBox2.Name = "listBox2"; + this.listBox2.Size = new System.Drawing.Size(557, 364); + this.listBox2.TabIndex = 3; + // + // IToolboxServiceControl + // + this.BackColor = System.Drawing.Color.Beige; + this.Controls.Add(this.listBox2); + this.Controls.Add(this.listBox1); + this.Location = new System.Drawing.Point(500, 400); + this.Name = "IToolboxServiceControl"; + this.Size = new System.Drawing.Size(771, 432); + this.ResumeLayout(false); + + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ToolboxServiceDemo/IToolboxServiceControl.resx b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ToolboxServiceDemo/IToolboxServiceControl.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ToolboxServiceDemo/IToolboxServiceControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ToolboxServiceDemo/TextDataTextBoxComponent.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ToolboxServiceDemo/TextDataTextBoxComponent.cs new file mode 100644 index 0000000..8b21447 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/ToolboxServiceDemo/TextDataTextBoxComponent.cs @@ -0,0 +1,157 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing.Design; +using System.Windows.Forms; + +namespace TextDataTextBoxComponent +{ + // Component that adds a "Text" data format ToolboxItemCreatorCallback + // to the Toolbox. This component uses a custom ToolboxItem that + // creates a TextBox containing the text data. + public class TextDataTextBoxComponent : Component + { + private bool creatorAdded = false; + private IToolboxService ts; + + public TextDataTextBoxComponent() + { + } + + // ISite override to register TextBox creator + public override System.ComponentModel.ISite Site + { + get + { + return base.Site; + } + set + { + if (value != null) + { + base.Site = value; + + if (!creatorAdded) + { + AddTextTextBoxCreator(); + } + } + else + { + if (creatorAdded) + { + RemoveTextTextBoxCreator(); + } + + base.Site = value; + } + } + } + + // Adds a "Text" data format creator to the toolbox that creates + // a textbox from a text fragment pasted to the toolbox. + private void AddTextTextBoxCreator() + { + ts = (IToolboxService)GetService(typeof(IToolboxService)); + + if (ts != null) + { + ToolboxItemCreatorCallback textCreator = + new ToolboxItemCreatorCallback(this.CreateTextBoxForText); + + try + { + ts.AddCreator( + textCreator, + "Text", + (IDesignerHost)GetService(typeof(IDesignerHost))); + + creatorAdded = true; + } + catch (Exception ex) + { + MessageBox.Show( + ex.ToString(), + "Exception Information"); + } + } + } + + // Removes any "Text" data format creator from the toolbox. + private void RemoveTextTextBoxCreator() + { + if (ts != null) + { + ts.RemoveCreator( + "Text", + (IDesignerHost)GetService(typeof(IDesignerHost))); + + creatorAdded = false; + } + } + + // ToolboxItemCreatorCallback delegate format method to create + // the toolbox item. + private ToolboxItem CreateTextBoxForText( + object serializedObject, + string format) + { + DataObject o = new DataObject((IDataObject)serializedObject); + + string[] formats = o.GetFormats(); + + if (o.GetDataPresent("System.String", true)) + { + string toolboxText = (string)(o.GetData("System.String", true)); + return (new TextToolboxItem(toolboxText)); + } + + return null; + } + + protected override void Dispose(bool disposing) + { + if (creatorAdded) + { + RemoveTextTextBoxCreator(); + } + } + } + + // Custom toolbox item creates a TextBox and sets its Text property + // to the constructor-specified text. + public class TextToolboxItem : ToolboxItem + { + private string text; + private delegate void SetTextMethodHandler(Control c, string text); + + public TextToolboxItem(string text) : base() + { + this.text = text; + } + + // ToolboxItem.CreateComponentsCore override to create the TextBox + // and link a method to set its Text property. + protected override IComponent[] CreateComponentsCore(IDesignerHost host) + { + System.Windows.Forms.TextBox textbox = + (TextBox)host.CreateComponent(typeof(TextBox)); + + // Because the designer resets the text of the textbox, use + // a SetTextMethodHandler to set the text to the value of + // the text data. + Control c = host.RootComponent as Control; + c.BeginInvoke( + new SetTextMethodHandler(OnSetText), + new object[] { textbox, text }); + + return new System.ComponentModel.IComponent[] { textbox }; + } + + // Method to set the text property of a TextBox after it is initialized. + private void OnSetText(Control c, string text) + { + c.Text = text; + } + } +} \ No newline at end of file diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/VsOutputWindowLogger.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/VsOutputWindowLogger.cs new file mode 100644 index 0000000..3b2face --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/VsOutputWindowLogger.cs @@ -0,0 +1,30 @@ +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace RootDesignerDemo +{ + internal class VsOutputWindowLogger + { + IVsOutputWindowPane _generalPane; + + public VsOutputWindowLogger(string outputPaneName) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + // Get the output window + IVsOutputWindow outputWindow = ServiceProvider.GlobalProvider.GetService(typeof(SVsOutputWindow)) as IVsOutputWindow; + + var generalPaneGuid = VSConstants.GUID_OutWindowGeneralPane; + outputWindow.CreatePane(ref generalPaneGuid, outputPaneName, 1, 1); + outputWindow.GetPane(ref generalPaneGuid, out _generalPane); + _generalPane.Activate(); + } + + public void WriteLine(string message) + { + ThreadHelper.ThrowIfNotOnUIThread(); + _ = _generalPane.OutputStringThreadSafe($"{message}\r\n"); + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/WindowMessageDesigner.cs b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/WindowMessageDesigner.cs new file mode 100644 index 0000000..3fb87cf --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/WindowMessageDesigner.cs @@ -0,0 +1,21 @@ +using System.Windows.Forms.Design; + +namespace IToolboxServiceExample +{ + // This designer passes window messages to the controls at design time. + public class WindowMessageDesigner : ControlDesigner + { + public WindowMessageDesigner() + { + } + + // Window procedure override passes events to control. + protected override void WndProc(ref System.Windows.Forms.Message m) + { + if (m.HWnd == this.Control.Handle) + base.WndProc(ref m); + else + this.DefWndProc(ref m); + } + } +} diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/app.config b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/app.config new file mode 100644 index 0000000..c324813 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/app.config @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/packages.config b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/packages.config new file mode 100644 index 0000000..0f3e704 --- /dev/null +++ b/Samples/RootDesigner/Framework/RootDesignerDemo/RootDesignerSupportLib/packages.config @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/Directory.Build.targets b/Samples/RootDesigner/Net/ClientServerSample/Directory.Build.targets new file mode 100644 index 0000000..afe6de0 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/Directory.Build.targets @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Samples/RootDesigner/Net/ClientServerSample/NuGet.config b/Samples/RootDesigner/Net/ClientServerSample/NuGet.config new file mode 100644 index 0000000..68668ff --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/NuGet.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Properties/Resources.Designer.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Properties/Resources.Designer.cs new file mode 100644 index 0000000..f440fe7 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Properties/Resources.Designer.cs @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RootDesignerDemo.Client.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RootDesignerDemo.Client.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Line { + get { + object obj = ResourceManager.GetObject("Line", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Rectangle { + get { + object obj = ResourceManager.GetObject("Rectangle", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Text { + get { + object obj = ResourceManager.GetObject("Text", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Properties/Resources.resx b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Properties/Resources.resx new file mode 100644 index 0000000..dc7dbfa --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Properties/Resources.resx @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\Rectangle.bmp;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Line.bmp;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Text.bmp;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Resources/Line.bmp b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Resources/Line.bmp new file mode 100644 index 0000000..89280eb Binary files /dev/null and b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Resources/Line.bmp differ diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Resources/Rectangle.bmp b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Resources/Rectangle.bmp new file mode 100644 index 0000000..9239ce9 Binary files /dev/null and b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Resources/Rectangle.bmp differ diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Resources/Text.bmp b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Resources/Text.bmp new file mode 100644 index 0000000..a8e028f Binary files /dev/null and b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/Resources/Text.bmp differ diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/RootDesignerDemo.Client.csproj b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/RootDesignerDemo.Client.csproj new file mode 100644 index 0000000..087badf --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/RootDesignerDemo.Client.csproj @@ -0,0 +1,34 @@ + + + + net472 + true + 9.0 + enable + true + + + + + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/RootDesignerProxy/ShapeRootProxyDesigner.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/RootDesignerProxy/ShapeRootProxyDesigner.cs new file mode 100644 index 0000000..d6adf44 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/RootDesignerProxy/ShapeRootProxyDesigner.cs @@ -0,0 +1,84 @@ +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Design; +using Microsoft.DotNet.DesignTools.Client.Designers; + +namespace RootDesignerDemo.Client.RootDesignerProxy +{ + [ToolboxItemFilter(ToolboxCategory, ToolboxItemFilterType.Require)] + public class ShapeRootProxyDesigner : ComponentProxyDesigner, IToolboxUser + { + private const string ToolboxCategory = "SdkShapeRootDesigner"; + + private IToolboxService? _toolboxService = null; + private ToolboxItemCollection? _tools; + + public override void Initialize(IComponent component) + { + base.Initialize(component); + _toolboxService = (IToolboxService?) GetService(typeof(IToolboxService)); + } + + private void SetupToolboxItems() + { + CreateToolboxItem("Line Tool", "LineTool", ToolboxCategory, + RootDesignerDemo.Client.Properties.Resources.Line); + CreateToolboxItem("Rectangle Tool", "RectangleTool", ToolboxCategory, + RootDesignerDemo.Client.Properties.Resources.Rectangle); + CreateToolboxItem("Text Tool", "TextTool", ToolboxCategory, + RootDesignerDemo.Client.Properties.Resources.Text); + } + + private void CreateToolboxItem(string toolDisplayName, string toolTypeName, string toolboxFilterString, Bitmap toolboxBitmap) + { + ToolboxItem toolboxItem; + ToolboxItemFilterAttribute toolboxItemFilterAttribute; + ToolboxItemFilterAttribute[] toolboxItemFilterAttributeArray; + + string toolboxItemName = $"{ToolboxCategory}.{toolTypeName}"; + + foreach (ToolboxItem existingToolboxItem in _toolboxService!.GetToolboxItems(ToolboxCategory)) + { + if (existingToolboxItem.TypeName != toolboxItemName) + { + continue; + } + + // We found the item, so remove it... + _toolboxService.RemoveToolboxItem(existingToolboxItem); + break; + } + + // ...and add it again. + toolboxItem = new ToolboxItem() + { + TypeName = toolboxItemName, + DisplayName = toolDisplayName, + Description = $"Description of {toolDisplayName}", + Bitmap = toolboxBitmap + }; + + toolboxItemFilterAttribute = new ToolboxItemFilterAttribute(toolboxFilterString, ToolboxItemFilterType.Require); + toolboxItemFilterAttributeArray = new ToolboxItemFilterAttribute[] { toolboxItemFilterAttribute }; + toolboxItem.Filter = (ICollection)toolboxItemFilterAttributeArray; + toolboxItem.Properties[ToolboxCategory] = toolTypeName; + _toolboxService.AddToolboxItem(toolboxItem, ToolboxCategory); + + return; + } + + protected override RootDesignerSupport EnableRootDesigner() => RootDesignerSupport.Default; + + protected override void PopulateToolbox() => SetupToolboxItems(); + + bool IToolboxUser.GetToolSupported(ToolboxItem tool) + { + return !(tool.Properties[ToolboxCategory] is null); + } + + void IToolboxUser.ToolPicked(ToolboxItem tool) + { + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/CustomEnumClientVersion.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/CustomEnumClientVersion.cs new file mode 100644 index 0000000..ec590ce --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/CustomEnumClientVersion.cs @@ -0,0 +1,14 @@ +namespace RootDesignerDemo.Designer.Client +{ + /// + /// Mirror of the server-defined enum for the class + /// which makes up the RdControl's RdPropertyStore property. + /// + public enum CustomEnumClientVersion + { + FirstValue, + SecondValue, + ThirdValue, + FourthValue + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/ErrorProviderExtension.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/ErrorProviderExtension.cs new file mode 100644 index 0000000..a70308d --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/ErrorProviderExtension.cs @@ -0,0 +1,28 @@ +using System; +using System.Windows.Forms; + +namespace RootDesignerDemo.Designer.Client +{ + internal static class ErrorProviderExtension + { + /// + /// Marks the provided control as errored if the is not met. + /// + /// ErrorProvider component instance. + /// Control, on which the Error to set on if errorCondition is true. + /// Function delegate which checks the error condition. + /// Error text to be assigned in the error case. + /// true, if an error occured. + public static bool SetErrorOrNull(this ErrorProvider errorProvider, Control control, Func errorCondition, string errorText) + { + if (errorCondition()) + { + errorProvider.SetError(control, errorText); + return true; + } + + errorProvider.SetError(control, null); + return false; + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditor.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditor.cs new file mode 100644 index 0000000..8d1db63 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditor.cs @@ -0,0 +1,57 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing.Design; +using System.Windows.Forms; +using System.Windows.Forms.Design; + +namespace RootDesignerDemo.Designer.Client +{ + /// + /// The actual client-side implementation of the RdTypeEditor, which is called by + /// Visual Studio's Property Browser. + /// + public class RdTypeEditor : UITypeEditor + { + RdTypeEditorDialog? _customTypeEditorDialog; + + public override object? EditValue( + ITypeDescriptorContext context, + IServiceProvider provider, + object? value) + { + if (provider is null) + { + return value; + } + + var editorService = provider.GetRequiredService(); + var designerHost = provider.GetRequiredService(); + + // Value now holds the proxy of the RdPropertyStore object the user wants to edit. + var viewModelClient = RdTypeEditorVMClient.Create(provider, value); + + _customTypeEditorDialog ??= new RdTypeEditorDialog(provider, viewModelClient); + _customTypeEditorDialog.Context = context; + _customTypeEditorDialog.Host = designerHost; + + var dialogResult = editorService.ShowDialog(_customTypeEditorDialog); + if (dialogResult == DialogResult.OK) + { + // By now, the UI of the Editor has asked its (client-side) ViewModel + // to run the code which updates the property value. It passes the data to + // the server, which in turn updates the server-side ViewModel. + // When it's time to return the value from the client-side ViewModel back to the + // Property Browser (which has called the TypeEditor in the first place), the client-side + // ViewModel accesses its PropertyStore property, which in turn gets the required PropertyStore + // proxy object directly from the server-side ViewModel. + value = viewModelClient.PropertyStore; + } + + return value; + } + + public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) + => UITypeEditorEditStyle.Modal; + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorDialog.Designer.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorDialog.Designer.cs new file mode 100644 index 0000000..5f766c5 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorDialog.Designer.cs @@ -0,0 +1,240 @@ +namespace RootDesignerDemo.Designer.Client +{ + partial class RdTypeEditorDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this._mainTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this._customEnumValueLabel = new System.Windows.Forms.Label(); + this._listOfStringsLabel = new System.Windows.Forms.Label(); + this._dateCreatedLabel = new System.Windows.Forms.Label(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this._okButton = new System.Windows.Forms.Button(); + this._cancelButton = new System.Windows.Forms.Button(); + this._dateCreated = new System.Windows.Forms.DateTimePicker(); + this._customEnumValueListBox = new System.Windows.Forms.ComboBox(); + this._requiredIdLabel = new System.Windows.Forms.Label(); + this._requiredIdTextBox = new System.Windows.Forms.TextBox(); + this._listOfStringTextBox = new System.Windows.Forms.TextBox(); + this._errorProvider = new System.Windows.Forms.ErrorProvider(this.components); + this._mainTableLayoutPanel.SuspendLayout(); + this.flowLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this._errorProvider)).BeginInit(); + this.SuspendLayout(); + // + // _mainTableLayoutPanel + // + this._mainTableLayoutPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this._mainTableLayoutPanel.ColumnCount = 3; + this._mainTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this._mainTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this._mainTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this._mainTableLayoutPanel.Controls.Add(this._customEnumValueLabel, 0, 3); + this._mainTableLayoutPanel.Controls.Add(this._listOfStringsLabel, 0, 2); + this._mainTableLayoutPanel.Controls.Add(this._dateCreatedLabel, 0, 1); + this._mainTableLayoutPanel.Controls.Add(this.flowLayoutPanel1, 2, 0); + this._mainTableLayoutPanel.Controls.Add(this._dateCreated, 1, 1); + this._mainTableLayoutPanel.Controls.Add(this._customEnumValueListBox, 1, 3); + this._mainTableLayoutPanel.Controls.Add(this._requiredIdLabel, 0, 0); + this._mainTableLayoutPanel.Controls.Add(this._requiredIdTextBox, 1, 0); + this._mainTableLayoutPanel.Controls.Add(this._listOfStringTextBox, 1, 2); + this._mainTableLayoutPanel.Location = new System.Drawing.Point(7, 6); + this._mainTableLayoutPanel.Margin = new System.Windows.Forms.Padding(2); + this._mainTableLayoutPanel.Name = "_mainTableLayoutPanel"; + this._mainTableLayoutPanel.RowCount = 2; + this._mainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this._mainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this._mainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this._mainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this._mainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this._mainTableLayoutPanel.Size = new System.Drawing.Size(511, 264); + this._mainTableLayoutPanel.TabIndex = 0; + // + // _customEnumValueLabel + // + this._customEnumValueLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this._customEnumValueLabel.AutoSize = true; + this._customEnumValueLabel.Location = new System.Drawing.Point(4, 243); + this._customEnumValueLabel.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this._customEnumValueLabel.Name = "_customEnumValueLabel"; + this._customEnumValueLabel.Size = new System.Drawing.Size(104, 13); + this._customEnumValueLabel.TabIndex = 6; + this._customEnumValueLabel.Text = "Custom Enum value:"; + // + // _listOfStringsLabel + // + this._listOfStringsLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this._listOfStringsLabel.AutoSize = true; + this._listOfStringsLabel.Location = new System.Drawing.Point(4, 139); + this._listOfStringsLabel.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this._listOfStringsLabel.Name = "_listOfStringsLabel"; + this._listOfStringsLabel.Size = new System.Drawing.Size(73, 13); + this._listOfStringsLabel.TabIndex = 4; + this._listOfStringsLabel.Text = "List of Strings:"; + // + // _dateCreatedLabel + // + this._dateCreatedLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this._dateCreatedLabel.AutoSize = true; + this._dateCreatedLabel.Location = new System.Drawing.Point(4, 35); + this._dateCreatedLabel.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this._dateCreatedLabel.Name = "_dateCreatedLabel"; + this._dateCreatedLabel.Size = new System.Drawing.Size(72, 13); + this._dateCreatedLabel.TabIndex = 2; + this._dateCreatedLabel.Text = "Date created:"; + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.flowLayoutPanel1.AutoSize = true; + this.flowLayoutPanel1.Controls.Add(this._okButton); + this.flowLayoutPanel1.Controls.Add(this._cancelButton); + this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel1.Location = new System.Drawing.Point(424, 2); + this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(11, 2, 2, 2); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this._mainTableLayoutPanel.SetRowSpan(this.flowLayoutPanel1, 5); + this.flowLayoutPanel1.Size = new System.Drawing.Size(85, 70); + this.flowLayoutPanel1.TabIndex = 0; + // + // _okButton + // + this._okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this._okButton.Location = new System.Drawing.Point(2, 2); + this._okButton.Margin = new System.Windows.Forms.Padding(2); + this._okButton.Name = "_okButton"; + this._okButton.Size = new System.Drawing.Size(81, 28); + this._okButton.TabIndex = 0; + this._okButton.Text = "OK"; + this._okButton.UseVisualStyleBackColor = true; + // + // _cancelButton + // + this._cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this._cancelButton.Location = new System.Drawing.Point(2, 40); + this._cancelButton.Margin = new System.Windows.Forms.Padding(2, 8, 2, 2); + this._cancelButton.Name = "_cancelButton"; + this._cancelButton.Size = new System.Drawing.Size(81, 28); + this._cancelButton.TabIndex = 1; + this._cancelButton.Text = "Cancel"; + this._cancelButton.UseVisualStyleBackColor = true; + // + // _dateCreated + // + this._dateCreated.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this._dateCreated.Location = new System.Drawing.Point(116, 32); + this._dateCreated.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this._dateCreated.Name = "_dateCreated"; + this._dateCreated.Size = new System.Drawing.Size(293, 20); + this._dateCreated.TabIndex = 3; + // + // _customEnumValueListBox + // + this._customEnumValueListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this._customEnumValueListBox.FormattingEnabled = true; + this._customEnumValueListBox.Location = new System.Drawing.Point(116, 239); + this._customEnumValueListBox.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this._customEnumValueListBox.Name = "_customEnumValueListBox"; + this._customEnumValueListBox.Size = new System.Drawing.Size(293, 21); + this._customEnumValueListBox.TabIndex = 7; + // + // _requiredIdLabel + // + this._requiredIdLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this._requiredIdLabel.AutoSize = true; + this._requiredIdLabel.Location = new System.Drawing.Point(4, 7); + this._requiredIdLabel.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this._requiredIdLabel.Name = "_requiredIdLabel"; + this._requiredIdLabel.Size = new System.Drawing.Size(72, 13); + this._requiredIdLabel.TabIndex = 0; + this._requiredIdLabel.Text = "A required ID:"; + // + // _requiredIdTextBox + // + this._requiredIdTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this._requiredIdTextBox.Location = new System.Drawing.Point(116, 4); + this._requiredIdTextBox.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this._requiredIdTextBox.Name = "_requiredIdTextBox"; + this._requiredIdTextBox.Size = new System.Drawing.Size(293, 20); + this._requiredIdTextBox.TabIndex = 1; + // + // _listOfStringTextBox + // + this._listOfStringTextBox.AcceptsReturn = true; + this._listOfStringTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this._listOfStringTextBox.Location = new System.Drawing.Point(114, 58); + this._listOfStringTextBox.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this._listOfStringTextBox.Multiline = true; + this._listOfStringTextBox.Name = "_listOfStringTextBox"; + this._listOfStringTextBox.Size = new System.Drawing.Size(297, 175); + this._listOfStringTextBox.TabIndex = 5; + // + // _errorProvider + // + this._errorProvider.ContainerControl = this; + // + // RdTypeEditorDialog + // + this.AcceptButton = this._okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this._cancelButton; + this.ClientSize = new System.Drawing.Size(524, 273); + this.Controls.Add(this._mainTableLayoutPanel); + this.Margin = new System.Windows.Forms.Padding(2); + this.Name = "RdTypeEditorDialog"; + this.Text = "RdTypeEditorDialog"; + this._mainTableLayoutPanel.ResumeLayout(false); + this._mainTableLayoutPanel.PerformLayout(); + this.flowLayoutPanel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this._errorProvider)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel _mainTableLayoutPanel; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Button _okButton; + private System.Windows.Forms.Button _cancelButton; + private System.Windows.Forms.Label _dateCreatedLabel; + private System.Windows.Forms.DateTimePicker _dateCreated; + private System.Windows.Forms.Label _listOfStringsLabel; + private System.Windows.Forms.Label _requiredIdLabel; + private System.Windows.Forms.Label _customEnumValueLabel; + private System.Windows.Forms.ComboBox _customEnumValueListBox; + private System.Windows.Forms.TextBox _requiredIdTextBox; + private System.Windows.Forms.TextBox _listOfStringTextBox; + private System.Windows.Forms.ErrorProvider _errorProvider; + } +} \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorDialog.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorDialog.cs new file mode 100644 index 0000000..f6d82ff --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorDialog.cs @@ -0,0 +1,109 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Linq; +using System.Windows.Forms; +using RootDesignerDemo.Protocol.DataTransport; + +namespace RootDesignerDemo.Designer.Client +{ + internal partial class RdTypeEditorDialog : Form + { + RdPropertyStoreData? _propertyStore; + + public RdTypeEditorDialog(IServiceProvider provider, RdTypeEditorVMClient viewModelClient) + { + InitializeComponent(); + + Provider = provider; + ViewModelClient = viewModelClient; + + // Fill the Enum Combobox with the enum values. + _customEnumValueListBox.Items.AddRange( + Enum.GetValues(typeof(CustomEnumClientVersion)) + .Cast() + .Select(enumValue => enumValue.ToString()) + .ToArray()); + + _customEnumValueListBox.SelectedIndex = 0; + + // Fill the Form with the values. + PropertyStoreData = ViewModelClient.PropertyStoreData; + } + + public IServiceProvider? Provider { get; } + public RdTypeEditorVMClient ViewModelClient { get; set; } + public ITypeDescriptorContext? Context { get; set; } + public IDesignerHost? Host { get; set; } + + public RdPropertyStoreData? PropertyStoreData + { + get => _propertyStore; + + set + { + _requiredIdTextBox.Text = value?.SomeMustHaveId; + _dateCreated.Value = value?.DateCreated ?? DateTime.Now.Date; + _listOfStringTextBox.Lines = value?.ListOfStrings; + _customEnumValueListBox.SelectedIndex = value?.CustomEnumValue ?? 0; + } + } + + // Question: "How does this dialog ever get closed?" + // In this dialog, the OK button is set as the Form's accept button, + // the Cancel button is set as the Form's cancel button. + // The OK button's DialogResult is set to OK. + // Now, assigning a DialogResult value to a modal Form's DialogResult property + // automatically also causes OnValidating to run, and if its CancelEventArgs + // are not actually cancelled, the modal Form closes automatically. + // There is no need to write any code for that, expect for validating the user input. + // So, we neither need to handle the OK click event, nor Cancel click. + protected override void OnFormClosed(FormClosedEventArgs e) + { + base.OnFormClosed(e); + + if (DialogResult == DialogResult.OK) + { + ViewModelClient.ExecuteOkCommand(); + } + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + base.OnFormClosing(e); + e.Cancel = FormValidating(); + } + + private bool FormValidating() + { + // If the user doesn't click OK but cancels then we dont validate but return false. + // In this case, the existing content of _propertyStore stays current. + if (DialogResult != DialogResult.OK) + return false; + + bool validationFailed = false; + + validationFailed |= _errorProvider.SetErrorOrNull( + control: _requiredIdTextBox, + errorCondition: () => string.IsNullOrWhiteSpace(_requiredIdTextBox.Text), + errorText: "Please enter some valid ID value (alphanumerical)."); + + validationFailed |= _errorProvider.SetErrorOrNull( + control: _dateCreated, + errorCondition: () => _dateCreated.Value > DateTime.Now, + errorText: "Date can't be in the future."); + + _propertyStore = validationFailed + ? null + : (new( + _requiredIdTextBox.Text, + _dateCreated.Value, + _listOfStringTextBox.Lines, + (byte)_customEnumValueListBox.SelectedIndex)); + + ViewModelClient.PropertyStoreData = _propertyStore; + + return validationFailed; + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorDialog.resx b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorDialog.resx new file mode 100644 index 0000000..91f6789 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorDialog.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorVMClient.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorVMClient.cs new file mode 100644 index 0000000..50aad03 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeEditor/RdTypeEditorVMClient.cs @@ -0,0 +1,92 @@ +using System; +using RootDesignerDemo.Protocol; +using RootDesignerDemo.Protocol.DataTransport; +using RootDesignerDemo.Protocol.Endpoints; +using Microsoft.DotNet.DesignTools.Client; +using Microsoft.DotNet.DesignTools.Client.Proxies; +using Microsoft.DotNet.DesignTools.Client.Views; + +namespace RootDesignerDemo.Designer.Client +{ + /// + /// Client-side implementation of the ViewModel to control the TypeEditor UI. + /// + internal class RdTypeEditorVMClient : ViewModelClient + { + [ExportViewModelClientFactory(ViewModelNames.RdTypeEditorVM)] + private class Factory : ViewModelClientFactory + { + protected override RdTypeEditorVMClient CreateViewModelClient(ObjectProxy? viewModel) + => new(viewModel); + } + + public RdTypeEditorVMClient(ObjectProxy? viewModel) : base(viewModel) + { + if (viewModel is null) + { + throw new ArgumentNullException(nameof(viewModel)); + } + } + + /// + /// Creates an instance of this VMClient and initializes it with the server types + /// from which the data sources can be generated. + /// + /// + /// The designer session to create the VMClient server side. + /// + /// + /// The VMClient for controlling the TypeEditor dialog. + /// + public static RdTypeEditorVMClient Create( + IServiceProvider provider, + object? customPropertyStoreProxy) + { + var session = provider.GetRequiredService(); + var client = provider.GetRequiredService(); + + var response = client.SendRequest( + new CreateRdTypeEditorVMRequest( + session.Id, + customPropertyStoreProxy)); + + var viewModel = (ObjectProxy)response.ViewModel!; + + var clientViewModel = provider.CreateViewModelClient(viewModel); + clientViewModel.Initialize(response.PropertyStoreData); + + return clientViewModel; + } + + private void Initialize(RdPropertyStoreData? propertyStoreData) + { + PropertyStoreData = propertyStoreData; + } + + // Executes the OK Command when the user has clicked the OK button: + // It takes the _propertyStoreData, sends it to the Server along with the ViewModelProxy, + // so the server process can access it. The server process then creates the actual PropertyStore object + // from the passed data (remember: the client cannot do that, since it doesn't know about the + // RdPropertyStore type which only exists server-side!) and stores it in its PropertyStore property. + // Now, when the TypeEditor continues with the codeflow in EditValue, it gets the updated value to return to + // the property grid from this client-side ViewModel, namely from its PropertyStore property. This property + // in turn uses the ViewModelProxy to call the server and get the Proxy of the value from the server-side + // ViewModel, which our OKClickHandler has _just_ updated server-side and is therefore up-to-date. + internal void ExecuteOkCommand() + { + Client!.SendRequest(new RdTypeEditorOKClickRequest(ViewModelProxy, PropertyStoreData)); + } + + // Get, when the TypeEditor's UI need to be update its controls to show the content of the custom property. + // Set, when the validation of the data passed, which was just entered by the User. + internal RdPropertyStoreData? PropertyStoreData { get; set; } + + /// + /// Returns the Proxy of the server-side ViewModel's PropertyStore property. + /// + public Object? PropertyStore + + // See also comment on ExecuteOKCommand. + => ViewModelProxy!.GetPropertyValue(nameof(PropertyStore)); + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeRoutingProvider.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeRoutingProvider.cs new file mode 100644 index 0000000..3367624 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Client/TypeRoutingProvider.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Microsoft.DotNet.DesignTools.Client.TypeRouting; +using RootDesignerDemo.Client.RootDesignerProxy; +using RootDesignerDemo.Designer.Client; +using RootDesignerDemo.Protocol; + +namespace RootDesignerDemo.Designer.Server +{ + /// + /// Class holding the TypeRoutings for resolving the control designer type on the client. + /// + [ExportTypeRoutingDefinitionProvider] + internal class TypeRoutingProvider : TypeRoutingDefinitionProvider + { + public override IEnumerable GetDefinitions() + { + return new[] + { + new TypeRoutingDefinition( + TypeRoutingKinds.Editor, + nameof(EditorNames.RdTypeEditor), + typeof(RdTypeEditor)), + + new TypeRoutingDefinition( + TypeRoutingKinds.Designer, + nameof(DesignerNames.ShapeRootDesigner), + typeof(ShapeRootProxyDesigner)), + }; + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/EllipseShape.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/EllipseShape.cs new file mode 100644 index 0000000..63722e3 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/EllipseShape.cs @@ -0,0 +1,7 @@ +namespace RootDesignerDemo.Designer.Server; + +public class EllipseShape : ShapeBase +{ + public override void Render(Graphics g) + => g.DrawEllipse(CurrentPen, new Rectangle(Location, Size)); +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/LineShape.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/LineShape.cs new file mode 100644 index 0000000..8658852 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/LineShape.cs @@ -0,0 +1,7 @@ +namespace RootDesignerDemo.Designer.Server; + +public class LineShape : ShapeBase +{ + public override void Render(Graphics g) + => g.DrawLine(CurrentPen, Location, Location + Size); +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdControl.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdControl.cs new file mode 100644 index 0000000..9b7fdd8 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdControl.cs @@ -0,0 +1,128 @@ +using System.ComponentModel; +using System.Text; + +namespace RootDesignerDemo.Controls +{ + /// + /// Custom Control sample implementation. + /// + /// + /// This sample custom control implements one custom property named + /// of type . Its sole purpose is to demonstrate how to implement + /// a custom TypeEditor for editing this property's content at design time with the out-of-process + /// WinForms Designer. + /// + [Designer("RdControlDesigner")] + public class RdControl : Control + { + // Backing field for CustomProperty. + private RdPropertyStore? _customPropertyStoreProperty; + + /// + /// Occurs when the RdPropertyStoreProperty changes. + /// + public event EventHandler? RdPropertyStorePropertyChanged; + + public RdControl() + { + DoubleBuffered = true; + ResizeRedraw = true; + } + + /// + /// Gets or sets a value of type which is composed of different value types, + /// a custom enum and a string array. + /// + [Description("A custom property composed of different value types, a custom enum and a string array."), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public RdPropertyStore? RdPropertyStoreProperty + { + get => _customPropertyStoreProperty; + + set + { + if (!Equals(value, _customPropertyStoreProperty)) + { + _customPropertyStoreProperty = value; + OnRdPropertyStoreProperty(EventArgs.Empty); + + // We update this property only at design-time, not at runtime. + if (IsHandleCreated && IsAncestorSiteInDesignMode) + { + Invalidate(); + } + } + } + } + + /// + /// Raises the event. + /// + protected virtual void OnRdPropertyStoreProperty(EventArgs e) + => RdPropertyStorePropertyChanged?.Invoke(this, e); + + /// + /// Resets the . + /// + private void ResetRdPropertyStoreProperty() + => RdPropertyStoreProperty = null; + + /// + /// Indicates whether the property should be persisted. + /// + /// + /// if the CodeDOM serializer should emit code for + /// assigning a valid content to the property in InitializeComponent. + /// + private bool ShouldSerializeRdPropertyStoreProperty() + => RdPropertyStoreProperty is not null; + + // The only function of this control is to draw a visual + // representation of the RdPropertyStoreProperty at Design and Runtime. + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + // We show this only at Design time, not at runtime. + if (IsAncestorSiteInDesignMode) + { + + // Drawing a frame around the control's borders: + var pen = new Pen(ForeColor); + var brush = new SolidBrush(ForeColor); + + // Drawing the contents of the CustomProperty. + e.Graphics.DrawString( + BuildContentString(RdPropertyStoreProperty), + Font, + brush, + point: new(10, 10)); + + // Builds a long string with the text representation of + // the RdPropertyStoreProperty property of this control. + string BuildContentString(RdPropertyStore? propertyData) + { + StringBuilder stringBuilder = new(); + + if (propertyData is null) + { + stringBuilder.Append("No CustomProperty Data defined."); + return stringBuilder.ToString(); + } + + stringBuilder.AppendLine($"{nameof(propertyData.SomeMustHaveId)}: {propertyData.SomeMustHaveId}"); + stringBuilder.AppendLine($"{nameof(propertyData.DateCreated)}: {propertyData.DateCreated:yyyy-mm-dd (ddd)}"); + stringBuilder.AppendLine($"{nameof(propertyData.CustomEnumValue)}: '{propertyData.CustomEnumValue}'"); + stringBuilder.AppendLine(); + stringBuilder.AppendLine("List of Strings:"); + stringBuilder.AppendLine("============================="); + + propertyData.ListOfStrings?.ForEach( + stringValue => stringBuilder.AppendLine($"* {stringValue}")); + + return stringBuilder.ToString(); + } + } + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdControl.resx b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdControl.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdPropertyStore.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdPropertyStore.cs new file mode 100644 index 0000000..a35b14c --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdPropertyStore.cs @@ -0,0 +1,55 @@ +using System.ComponentModel; +using System.ComponentModel.Design.Serialization; +using System.Drawing.Design; + +namespace RootDesignerDemo.Controls +{ + /// + /// An example for a custom type used by a property of a custom control. + /// + /// + /// Since this type is composed from different sub types, there is no editor out of the box to enter data in a + /// meaningful way when we are editing the value at design-time from within the property grid. That is the + /// reason we need a dedicated editor (RdTypeEditor) deriving from which does + /// that job. + /// Note for CodeDom serialization: since this is a complex type, serializing a property of this type in the + /// custom control won't produce the correct code in the InitializeComponent method. Therefore, we need a + /// custom serializer. Since we cannot refer to the types directly, we need to refer to them as full qualified names + /// and pass them to the as strings. + /// The custom CodeDom serializer should by default be placed in the same assembly as the custom control's designer. + /// + [DesignerSerializer("RootDesignerDemo.Designer.Server.Serialization.RdPropertyStoreCodeDomSerializer", + "Microsoft.DotNet.DesignTools.Serialization.CodeDomSerializer")] + [Editor("RdTypeEditor", typeof(UITypeEditor))] + public class RdPropertyStore + { + // We need the default constructor for the CodeDom serializer. + public RdPropertyStore() + { + SomeMustHaveId = Guid.NewGuid().ToString(); + } + + public RdPropertyStore( + string someMustHaveId, + DateTime dateCreated, + List? listOfStrings, + RdPropertyStoreEnum customEnumValue) + { + SomeMustHaveId = someMustHaveId; + DateCreated = dateCreated; + ListOfStrings = listOfStrings; + CustomEnumValue = customEnumValue; + } + + public string SomeMustHaveId { get; set; } + public DateTime DateCreated { get; set; } + public List? ListOfStrings { get; set; } + public RdPropertyStoreEnum CustomEnumValue { get; set; } + + public override string ToString() + => $"{nameof(SomeMustHaveId)}: {SomeMustHaveId}" + + $"{nameof(DateCreated)}: {DateCreated:yyyy-MM-dd HH:mm}" + + $"{nameof(CustomEnumValue)}: {CustomEnumValue}" + + $"{(ListOfStrings is null ? "No" : ListOfStrings?.Count ?? 0)} strings in list defined."; + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdPropertyStoreEnum.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdPropertyStoreEnum.cs new file mode 100644 index 0000000..146fba7 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RdPropertyStoreEnum.cs @@ -0,0 +1,13 @@ +namespace RootDesignerDemo.Controls +{ + /// + /// Custom enum used in . + /// + public enum RdPropertyStoreEnum + { + FirstValue, + SecondValue, + ThirdValue, + FourthValue + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RectangleShape.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RectangleShape.cs new file mode 100644 index 0000000..05fa8db --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RectangleShape.cs @@ -0,0 +1,7 @@ +namespace RootDesignerDemo.Designer.Server; + +public class RectangleShape : ShapeBase +{ + public override void Render(Graphics g) + => g.DrawRectangle(CurrentPen, new Rectangle(Location, Size)); +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RectangleShape.resx b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RectangleShape.resx new file mode 100644 index 0000000..d28be67 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RectangleShape.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 33, 33 + + + False + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RootDesignerDemo.Controls.csproj b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RootDesignerDemo.Controls.csproj new file mode 100644 index 0000000..879defe --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/RootDesignerDemo.Controls.csproj @@ -0,0 +1,16 @@ + + + + net6.0-windows + enable + true + enable + + + + + Component + + + + diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeBase.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeBase.cs new file mode 100644 index 0000000..23163b5 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeBase.cs @@ -0,0 +1,54 @@ +using System.ComponentModel; + +namespace RootDesignerDemo.Designer.Server; + +public class ShapeBase : Component +{ + public ShapeBase() + { + CurrentPen = Pens.DarkBlue; + CurrentBrush = (SolidBrush)Brushes.DarkBlue; + Location = DefaultLocation; + } + + public static Point DefaultLocation => new Point(20, 20); + public static Size DefaultSize => new Size(40, 40); + + protected Pen CurrentPen { get; } + protected SolidBrush CurrentBrush { get; } + + public Point Location { get; set; } + public Size Size { get; set; } + + public Color PenColor + { + get => CurrentPen.Color; + + set + { + if (CurrentPen.Color == value) + { + return; + } + + CurrentPen.Color = value; + } + } + + public Color FillColor + { + get => CurrentBrush.Color; + + set + { + if (CurrentBrush.Color == value) + { + return; + } + + CurrentBrush.Color = value; + } + } + + public virtual void Render(Graphics g) { } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeBase.resx b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeBase.resx new file mode 100644 index 0000000..0ab4d62 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeBase.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 33, 33 + + + False + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeDocumentBase.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeDocumentBase.cs new file mode 100644 index 0000000..6d33c35 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeDocumentBase.cs @@ -0,0 +1,19 @@ +using System.ComponentModel; +using System.ComponentModel.Design; + +namespace RootDesignerDemo.Designer.Server; + +// The following attribute associates the SampleRootDesigner designer +// with the SampleComponent component. +[Designer("ShapeRootDesigner", typeof(IRootDesigner)), + ToolboxItem(false)] +public class ShapeDocumentBase : Component +{ + public ShapeDocumentBase() + { + } + + private void InitializeComponent() + { + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeDocumentBase.resx b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeDocumentBase.resx new file mode 100644 index 0000000..e5858cc --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Controls/ShapeDocumentBase.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Package/RootDesignerDemo.Package.csproj b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Package/RootDesignerDemo.Package.csproj new file mode 100644 index 0000000..c272472 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Package/RootDesignerDemo.Package.csproj @@ -0,0 +1,39 @@ + + + + net6.0 + false + false + true + https://github.com/dotnet/winforms-designer + git + $(TargetsForTfmSpecificContentInPackage);_GetFilesToPackage + Always + $([System.DateTime]::Now.ToString("1.yM.dHmm")) + $([System.DateTime]::Now.ToString("yyyy.MM.dd.HHmm")) + + + + + <_File Include="$(SolutionDir)\RootDesignerDemo.Controls\bin\$(Configuration)\net6.0-windows\RootDesignerDemo.Controls.dll" /> + + + <_File Include="$(SolutionDir)\RootDesignerDemo.Controls\bin\$(Configuration)\net6.0-windows\RootDesignerDemo.Controls.pdb" /> + + <_File Include="$(SolutionDir)\RootDesignerDemo.Client\bin\$(Configuration)\net472\RootDesignerDemo.Client.dll" TargetDir="Design/WinForms" /> + <_File Include="$(SolutionDir)\RootDesignerDemo.Protocol\bin\$(Configuration)\net472\RootDesignerDemo.Protocol.dll" TargetDir="Design/WinForms" /> + <_File Include="$(SolutionDir)\RootDesignerDemo.Server\bin\$(Configuration)\net6.0-windows\RootDesignerDemo.Server.dll" TargetDir="Design/WinForms/Server" /> + <_File Include="$(SolutionDir)\RootDesignerDemo.Protocol\bin\$(Configuration)\net6.0-windows\RootDesignerDemo.Protocol.dll" TargetDir="Design/WinForms/Server" /> + + + + + + + + + + + + diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/DataTransport/RdPropertyStoreData.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/DataTransport/RdPropertyStoreData.cs new file mode 100644 index 0000000..54bf475 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/DataTransport/RdPropertyStoreData.cs @@ -0,0 +1,69 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.DotNet.DesignTools.Protocol.DataPipe; + +namespace RootDesignerDemo.Protocol.DataTransport +{ + /// + /// Transport class to carry the content of the RdPropertyStore + /// from the DesignToolServer's server process to the client (Visual Studio) + /// and back. + /// + [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")] + public partial class RdPropertyStoreData : IDataPipeObject + { + [AllowNull] + public string SomeMustHaveId { get; private set; } + + public DateTime DateCreated { get; private set; } + public string[]? ListOfStrings { get; private set; } + public byte CustomEnumValue { get; private set; } + + public RdPropertyStoreData() + { + } + + public RdPropertyStoreData( + string someMustHaveId, + DateTime dateCreated, + string[]? listOfStrings, + byte customEnumValue) + { + // We use this extension method here, which works also for .NET Framework and + // lower than .NET 7 versions; it's defined in GlobalUtilities.cs. + SomeMustHaveId = someMustHaveId.OrThrowIfArgumentIsNullOrEmpty(); + DateCreated = dateCreated; + ListOfStrings = listOfStrings; + CustomEnumValue = customEnumValue; + } + + public void ReadProperties(IDataPipeReader reader) + { + SomeMustHaveId = reader.ReadString(nameof(SomeMustHaveId)); + DateCreated = reader.ReadDateTimeOrDefault(nameof(DateCreated)); + + ListOfStrings = reader.ReadArrayOrNull( + nameof(ListOfStrings), + (reader) => reader.ReadString()!); + + CustomEnumValue = reader.ReadByte(nameof(CustomEnumValue)); + } + + public void WriteProperties(IDataPipeWriter writer) + { + writer.Write(nameof(SomeMustHaveId), SomeMustHaveId); + writer.WriteIfNotDefault(nameof(DateCreated), DateCreated); + + writer.WriteArrayIfNotNull( + nameof(ListOfStrings), + ListOfStrings, + (writer, value) => writer.Write(value)); + + writer.Write(nameof(CustomEnumValue), CustomEnumValue); + } + + private string GetDebuggerDisplay() + => $"ID: {SomeMustHaveId}; {nameof(DateCreated)}: {DateCreated}"; + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/EditorNames.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/EditorNames.cs new file mode 100644 index 0000000..7c0b346 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/EditorNames.cs @@ -0,0 +1,15 @@ +namespace RootDesignerDemo.Protocol +{ + /// + /// Static class for holding the names of all custom type editors to provide IntelliSense support. + /// + public static class EditorNames + { + public static readonly string RdTypeEditor = nameof(RdTypeEditor); + } + + public static class DesignerNames + { + public static readonly string ShapeRootDesigner = "RootDesignerDemo.Designer.Server.ShapeRootDesigner"; + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/EndpointNames.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/EndpointNames.cs new file mode 100644 index 0000000..70b6450 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/EndpointNames.cs @@ -0,0 +1,11 @@ +namespace RootDesignerDemo.Protocol +{ + /// + /// Static class for holding the names of all endpoints to provide IntelliSense support. + /// + public static class EndpointNames + { + public const string CreateRdTypeEditorVM = nameof(CreateRdTypeEditorVM); + public const string RdTypeEditorEditorOKClick = nameof(RdTypeEditorEditorOKClick); + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/CreateRdTypeEditorVMEndpoint.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/CreateRdTypeEditorVMEndpoint.cs new file mode 100644 index 0000000..d05c8b1 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/CreateRdTypeEditorVMEndpoint.cs @@ -0,0 +1,35 @@ +using System.Composition; +using Microsoft.DotNet.DesignTools.Protocol.DataPipe; +using Microsoft.DotNet.DesignTools.Protocol.Endpoints; + +namespace RootDesignerDemo.Protocol.Endpoints +{ + /// + /// Endpoint for the method to create the RdTypeEditor's ViewModel. + /// + /// + /// 'ViewModel' in this context is a class which holds the logic/properties to control the UI. There is a + /// server-side and a client-side part of that view model. The server-side provides the logic based on the actual + /// types of RdTypeEditor, running in the context of the custom control's TFM. It communicates to the + /// client-side view model, which then controls the client-side hosted UI, running in the context of + /// Visual Studio. + /// For every endpoint there is a request class and a response class for transporting the necessary data + /// to the respective context. There is also an additional endpoint handler, which holds the code that executes the actual + /// functionality of that endpoint. Request and response are located in the communications assembly; the actual handler is + /// part of the server-side implementation of the RdControl, along with the actual control designer and the + /// server-side view model. + /// + [Shared] + [ExportEndpoint] + public class CreateRdTypeEditorVMEndpoint + : Endpoint + { + public override string Name => EndpointNames.CreateRdTypeEditorVM; + + protected override CreateRdTypeEditorVMRequest CreateRequest(IDataPipeReader reader) + => new(reader); + + protected override CreateRdTypeEditorVMResponse CreateResponse(IDataPipeReader reader) + => new(reader); + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/CreateRdTypeEditorVMRequest.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/CreateRdTypeEditorVMRequest.cs new file mode 100644 index 0000000..662beea --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/CreateRdTypeEditorVMRequest.cs @@ -0,0 +1,38 @@ +using Microsoft.DotNet.DesignTools.Protocol; +using Microsoft.DotNet.DesignTools.Protocol.DataPipe; +using Microsoft.DotNet.DesignTools.Protocol.Endpoints; + +namespace RootDesignerDemo.Protocol.Endpoints +{ + /// + /// Request class for the . This passes the necessary + /// context (SessionId, proxy of the RdControl) from the client to the server. + /// + public class CreateRdTypeEditorVMRequest : Request + { + public SessionId SessionId { get; private set; } + public object? RdControlProxy { get; private set; } + + public CreateRdTypeEditorVMRequest() { } + + public CreateRdTypeEditorVMRequest(SessionId sessionId, object? customControlProxy) + { + SessionId = sessionId.OrThrowIfArgumentIsNull(); + RdControlProxy = customControlProxy; + } + + public CreateRdTypeEditorVMRequest(IDataPipeReader reader) : base(reader) { } + + protected override void ReadProperties(IDataPipeReader reader) + { + SessionId = reader.ReadSessionId(nameof(SessionId)); + RdControlProxy = reader.ReadObject(nameof(RdControlProxy)); + } + + protected override void WriteProperties(IDataPipeWriter writer) + { + writer.Write(nameof(SessionId), SessionId); + writer.WriteObject(nameof(RdControlProxy), RdControlProxy); + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/CreateRdTypeEditorVMResponse.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/CreateRdTypeEditorVMResponse.cs new file mode 100644 index 0000000..9b3e978 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/CreateRdTypeEditorVMResponse.cs @@ -0,0 +1,44 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using RootDesignerDemo.Protocol.DataTransport; +using Microsoft.DotNet.DesignTools.Protocol.DataPipe; +using Microsoft.DotNet.DesignTools.Protocol.Endpoints; + +namespace RootDesignerDemo.Protocol.Endpoints +{ + /// + /// Response class, answering the request for that endpoint. This transports the requested data (Proxy of + /// the server-side ViewModel and the data of the custom property type PropertyStore) back to the client. + /// + public class CreateRdTypeEditorVMResponse : Response + { + [AllowNull] + public object ViewModel { get; private set; } + + public RdPropertyStoreData? PropertyStoreData { get; set; } + + public CreateRdTypeEditorVMResponse() { } + + public CreateRdTypeEditorVMResponse( + object viewModel, + RdPropertyStoreData? propertyStoreData) + { + ViewModel = viewModel ?? throw new ArgumentNullException(nameof(viewModel)); + PropertyStoreData = propertyStoreData; + } + + public CreateRdTypeEditorVMResponse(IDataPipeReader reader) : base(reader) { } + + protected override void ReadProperties(IDataPipeReader reader) + { + ViewModel = reader.ReadObject(nameof(ViewModel)); + PropertyStoreData = reader.ReadDataPipeObjectOrNull(nameof(PropertyStoreData)); + } + + protected override void WriteProperties(IDataPipeWriter writer) + { + writer.WriteObject(nameof(ViewModel), ViewModel); + writer.WriteDataPipeObjectIfNotNull(nameof(PropertyStoreData), PropertyStoreData!); + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/RdTypeEditorOKClickEndpoint.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/RdTypeEditorOKClickEndpoint.cs new file mode 100644 index 0000000..9bd8883 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/RdTypeEditorOKClickEndpoint.cs @@ -0,0 +1,22 @@ +using System.Composition; +using Microsoft.DotNet.DesignTools.Protocol.DataPipe; +using Microsoft.DotNet.DesignTools.Protocol.Endpoints; + +namespace RootDesignerDemo.Protocol.Endpoints +{ + /// + /// Endpoint to handle the event when the user clicks the OK button of the custom type editor. + /// + [Shared] + [ExportEndpoint] + public class RdTypeEditorOKClickEndpoint : Endpoint + { + public override string Name => EndpointNames.RdTypeEditorEditorOKClick; + + protected override RdTypeEditorOKClickRequest CreateRequest(IDataPipeReader reader) + => new(reader); + + protected override RdTypeEditorOKClickResponse CreateResponse(IDataPipeReader reader) + => new(reader); + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/RdTypeEditorOKClickRequest.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/RdTypeEditorOKClickRequest.cs new file mode 100644 index 0000000..6f6b253 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/RdTypeEditorOKClickRequest.cs @@ -0,0 +1,44 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using RootDesignerDemo.Protocol.DataTransport; +using Microsoft.DotNet.DesignTools.Protocol.DataPipe; +using Microsoft.DotNet.DesignTools.Protocol.Endpoints; + +namespace RootDesignerDemo.Protocol.Endpoints +{ + /// + /// Request class for the endpoint. This passes the necessary + /// context (ViewModel, content of custom property) from the Client to the Server. + /// + public class RdTypeEditorOKClickRequest : Request + { + [AllowNull] + public object ViewModel { get; private set; } + + [AllowNull] + public RdPropertyStoreData PropertyStoreData { get; private set; } + + public RdTypeEditorOKClickRequest(object? viewModel, RdPropertyStoreData? propertyStoreData) + { + ViewModel = viewModel ?? throw new ArgumentNullException(nameof(viewModel)); + PropertyStoreData = propertyStoreData; + } + + public RdTypeEditorOKClickRequest(IDataPipeReader reader) + : base(reader) + { + } + + protected override void ReadProperties(IDataPipeReader reader) + { + ViewModel = reader.ReadObject(nameof(ViewModel)); + PropertyStoreData = reader.ReadDataPipeObjectOrNull(nameof(PropertyStoreData)); + } + + protected override void WriteProperties(IDataPipeWriter writer) + { + writer.WriteObject(nameof(ViewModel), ViewModel); + writer.WriteDataPipeObjectIfNotNull(nameof(PropertyStoreData), PropertyStoreData); + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/RdTypeEditorOKClickResponse.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/RdTypeEditorOKClickResponse.cs new file mode 100644 index 0000000..88f971d --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/Endpoints/RdTypeEditorOKClickResponse.cs @@ -0,0 +1,23 @@ +using Microsoft.DotNet.DesignTools.Protocol.DataPipe; +using Microsoft.DotNet.DesignTools.Protocol.Endpoints; + +namespace RootDesignerDemo.Protocol.Endpoints +{ + /// + /// Response class for this endpoint. This is not returning any relevant data, but this class is still needed + /// to meet the infrastructure conventions. + /// + public class RdTypeEditorOKClickResponse : Response.Empty + { + public static new RdTypeEditorOKClickResponse Empty { get; } = new RdTypeEditorOKClickResponse(); + + private RdTypeEditorOKClickResponse() + { + } + + public RdTypeEditorOKClickResponse(IDataPipeReader reader) + : base(reader) + { + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/CallerArgumentExpressionAttribute.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/CallerArgumentExpressionAttribute.cs new file mode 100644 index 0000000..f1acd5f --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/CallerArgumentExpressionAttribute.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Copied from https://github/dotnet/runtime + +// Note: This attribute was introduced in .NET Core 3.0. + + +namespace System.Runtime.CompilerServices; + +#if NETFRAMEWORK + +[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] +internal sealed class CallerArgumentExpressionAttribute : Attribute +{ + public CallerArgumentExpressionAttribute(string parameterName) + { + ParameterName = parameterName; + } + + public string ParameterName { get; } +} + +#endif diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/GlobalUtilities.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/GlobalUtilities.cs new file mode 100644 index 0000000..30e80e0 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/GlobalUtilities.cs @@ -0,0 +1,168 @@ +// ------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// ------------------------------------------------------------------- + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Microsoft.DotNet.DesignTools.Protocol; +using Microsoft.WinForms.Utilities.Shared; + +#pragma warning disable VSTHRD003 // Avoid awaiting foreign Tasks + +internal static class GlobalUtilities +{ + private const string ValueCannotBeAnEmptyString = "Value cannot be an empty string!"; + private const string ValueCannotBeNull = "Value cannot be null!"; + + /// + /// If the specified is , throw an + /// . + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ThrowIfNull([NotNull] T? value) + { + if (value is null) + { + ThrowHelper.ThrowInvalidOperationException(ValueCannotBeNull); + } + } + + /// + /// If the specified is , throw an + /// . Otherwise, return the value passed. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T OrThrowIfNull([NotNull] this T? value) + { + ThrowIfNull(value); + return value; + } + + /// + /// Awaits the specified . If the result is , + /// throw an . Otherwise, return the result. + /// + /// + public static async Task OrThrowIfNullAsync(this Task task) + => (await task).OrThrowIfNull(); + + /// + /// If is or an empty + /// . + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ThrowIfNullOrEmpty([NotNull] string? value) + { + if (value.IsNullOrEmpty()) + { + ThrowIfNull(value); + ThrowHelper.ThrowInvalidOperationException(ValueCannotBeAnEmptyString); + } + } + + /// + /// If is or an empty + /// . Otherwise, return the value passed. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string OrThrowIfNullOrEmpty([NotNull] string? value) + { + ThrowIfNullOrEmpty(value); + return value; + } + + /// + /// If is , throw an . + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ThrowIfArgumentIsNull( + [NotNull] object? argument, + [CallerArgumentExpression("argument")] string? paramName = null) + { + if (argument is null) + { + ThrowHelper.ThrowArgumentNullException(paramName); + } + } + + /// + /// If is , throw an . + /// Otherwise, return the value of . + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T OrThrowIfArgumentIsNull( + [NotNull] this T? argument, + [CallerArgumentExpression("argument")] string? paramName = null) + { + ThrowIfArgumentIsNull(argument, paramName); + + return argument; + } + + /// + /// If is or an empty , + /// throw an . + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ThrowIfArgumentIsNullOrEmpty( + [NotNull] string? argument, + [CallerArgumentExpression("argument")] string? paramName = null) + { + if (argument.IsNullOrEmpty()) + { + ThrowIfArgumentIsNull(argument, paramName); + ThrowHelper.ThrowArgumentException(ValueCannotBeAnEmptyString, paramName); + } + } + + /// + /// If is , + /// throw an . + /// + /// + public static SessionId OrThrowIfArgumentIsNull( + this SessionId sessionId, + [CallerArgumentExpression("sessionId")] string? paramName = null) + { + if (sessionId.IsNull) + { + throw new ArgumentNullException(nameof(sessionId)); + } + + return sessionId; + } + + /// + /// If is or an empty , + /// throw an . Otherwise, return the value of . + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string OrThrowIfArgumentIsNullOrEmpty( + [NotNull] this string? argument, + [CallerArgumentExpression("argument")] string? paramName = null) + { + ThrowIfArgumentIsNullOrEmpty(argument, paramName); + return argument; + } + + /// + /// Indicates whether the specified string is or an empty . + /// + /// + /// This method can be used to workaround the fact that string.IsNullOrEmpty doesn't have nullable + /// annotations on .NET Framework. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value) + => string.IsNullOrEmpty(value); +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/NullableAttributes.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/NullableAttributes.cs new file mode 100644 index 0000000..bfa5d93 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/NullableAttributes.cs @@ -0,0 +1,154 @@ +// ------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// ------------------------------------------------------------------- +// Copied from https://github/dotnet/runtime +// Note: These attributes were introduced in .NET Core 3.0 and .NET 5. +#nullable disable + +#if NETFRAMEWORK + +namespace System.Diagnostics.CodeAnalysis +{ + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute + { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class DisallowNullAttribute : Attribute + { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute + { } + + /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class NotNullAttribute : Attribute + { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute + { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } + + + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullAttribute : Attribute + { + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new[] { member }; + } + + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } + } +} + +#endif diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/ThrowHelper.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/ThrowHelper.cs new file mode 100644 index 0000000..fcd5c5a --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/GlobalUtilities/ThrowHelper.cs @@ -0,0 +1,44 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Microsoft.WinForms.Utilities.Shared; + +internal static class ThrowHelper +{ + /// + /// Throws an . + /// + /// + /// This is marked with NoInlining to ensure that the JIT can better inline calling code. + /// + /// + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowArgumentNullException(string? paramName) + => throw new ArgumentNullException(paramName); + + /// + /// Throws an . + /// + /// + /// This is marked with NoInlining to ensure that the JIT can better inline calling code. + /// + /// + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowArgumentException(string message, string? paramName) + => throw new ArgumentException(message, paramName); + + /// + /// Throws an . + /// + /// + /// This is marked with NoInlining to ensure that the JIT can better inline calling code. + /// + /// + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowInvalidOperationException(string message) + => throw new InvalidOperationException(message); +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/RootDesignerDemo.Protocol.csproj b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/RootDesignerDemo.Protocol.csproj new file mode 100644 index 0000000..64bd0ba --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/RootDesignerDemo.Protocol.csproj @@ -0,0 +1,10 @@ + + + + net6.0-windows;net472 + true + latest + enable + true + + diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/ViewModelNames.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/ViewModelNames.cs new file mode 100644 index 0000000..18daf6f --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Protocol/ViewModelNames.cs @@ -0,0 +1,10 @@ +namespace RootDesignerDemo.Protocol +{ + /// + /// Static class for holding the names of all ViewModels to provide IntelliSense support. + /// + public static class ViewModelNames + { + public const string RdTypeEditorVM = nameof(RdTypeEditorVM); + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/CodeDomSerializer/RdPropertyStoreCodeDomSerializer.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/CodeDomSerializer/RdPropertyStoreCodeDomSerializer.cs new file mode 100644 index 0000000..e48a132 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/CodeDomSerializer/RdPropertyStoreCodeDomSerializer.cs @@ -0,0 +1,95 @@ +using System.CodeDom; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics; +using Microsoft.DotNet.DesignTools.Serialization; +using RootDesignerDemo.Controls; + +namespace RootDesignerDemo.Designer.Server.Serialization +{ + /// + /// Provides a generic content serializer for reference types, which - in contrast to + /// DesignerSerializationVisibility.Content - also generates code which instantiates the property's + /// custom type. + /// + internal class RdPropertyStoreCodeDomSerializer : CodeDomSerializer + { + public override object Serialize( + IDesignerSerializationManager manager, + object value) + { + if (Debugger.IsAttached) + Debugger.Break(); + + if (manager.Context.Current is ExpressionContext expressionContext) + { + RdPropertyStore propertyStore = (RdPropertyStore)value; + + // This is the left-side assignment target, we want to generate. + // And it describes the current context, for which we need the + // object generation. + var contextExpression = expressionContext.Expression; + CodeStatementCollection statements = new(); + + // Now, we want to generate: + // this.customControl1.CustomProperty = new RootDesignerDemo.RdPropertyStore(); + // this.customControl1.CustomProperty.CustomEnumValue = RootDesignerDemo.CustomEnum.FourthValue; + // this.customControl1.CustomProperty.DateCreated = new System.DateTime(2022, 7, 13, 0, 0, 0, 0); + // this.customControl1.CustomProperty.ListOfStrings = ((System.Collections.Generic.List)(resources.GetObject("resource.ListOfStrings"))); + // this.customControl1.CustomProperty.SomeMustHaveId = "{C0E03E00-EFDA-47AA-9BA9-B69671F7A565}"; + + // We start with 'new RdPropertyStore()'; + CodeObjectCreateExpression customPropertyCreateExpression = new( + new CodeTypeReference(typeof(RdPropertyStore))); + + // Then we do the assignment '{codeExpression} = new RdPropertyStore()'; + CodeAssignStatement contextAssignmentStatement = new( + contextExpression, customPropertyCreateExpression); + + // And from here on we're doing exactly that what the default serializer would be doing, + // had it detected the DesignerSerializationVisibilityAttribute set to 'Content'. + // It just traverses the property's object graph and creates the code for generating + // the respective value types, enums, arrays, etc. + + // To that end, we're _not_ getting this type's serializer, because then we would ending up with running + // this exact serializer. This is not what we want, we would end up in a recusion. Instead, we want to serialize + // the graph of property type, so we're getting object's default serializer... + var serializer = (CodeDomSerializer)manager.GetSerializer(typeof(object), typeof(CodeDomSerializer)); + + /// ...indicate, that we also want to generate code for setting the default values ... + var absolute = manager.Context[typeof(SerializeAbsoluteContext)] as SerializeAbsoluteContext; + + /// ...and we finally make sure, we're not serializing things that have been serialized before and could just + /// get from the stack. + var result = IsSerialized(manager, value, absolute != null) + ? GetExpression(manager, value) + : serializer.Serialize(manager, value); + + /// Now: Here is the difference to DesignerSerializationVisibility.Content: We are adding the instantiation code for our + /// custom (complex) property. And now ... + statements.Add(contextAssignmentStatement); + + /// ...we're adding all the statements, which the default (object) serializer generated, and which are at this point + /// supposed to be all the statements for assigning our custom control's property's custom type's properties. + if (result is CodeStatementCollection statementCollection) + { + foreach (CodeStatement statement in statementCollection) + { + statements.Add(statement); + } + } + else + { + if (result is CodeStatement statement) + { + statements.Add(statement); + } + } + + return statements; + }; + + var baseResult = base.Serialize(manager, value); + return baseResult; + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/ControlDesigner/RdControlDesigner.ActionList.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/ControlDesigner/RdControlDesigner.ActionList.cs new file mode 100644 index 0000000..aed6c98 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/ControlDesigner/RdControlDesigner.ActionList.cs @@ -0,0 +1,78 @@ +using System.ComponentModel; +using Microsoft.DotNet.DesignTools.Designers; +using Microsoft.DotNet.DesignTools.Designers.Actions; +using RootDesignerDemo.Controls; + +namespace RootDesignerDemo.Designer.Server +{ + internal partial class RdControlDesigner + { + /// + /// Action lists implementation for the . + /// + /// + /// Note: Action lists for the out-of-process Designer can be implemented exactly as they would be for the in-process + /// Designer, except: The control designer has to be compiled against the Winforms Designer Extensibility SDK, and ActionList + /// related classes must come from the namespace. + /// + private class ActionList : DesignerActionList + { + private const string Behavior = nameof(Behavior); + + private readonly ComponentDesigner _designer; + + public ActionList(RdControlDesigner designer) + : base(designer.Component) + { + _designer = designer; + } + + public RdPropertyStore? CustomProperty + { + get => ((RdControl?)Component)?.RdPropertyStoreProperty; + + // Note: This code would not work, since the PropertyBrowser wouldn't get updated. + //set + //{ + // if (Component is { } component) + // { + // ((RdControl)component).CustomProperty = value; + // } + //} + + // Do this instead: + set + { + if (Component is not null) + { + TypeDescriptor.GetProperties(Component)[nameof(CustomProperty)]?.SetValue(Component, value); + } + } + } + + public void InvokeRdTypeEditor() + => _designer.InvokePropertyEditor(nameof(RdControl.RdPropertyStoreProperty)); + + public override DesignerActionItemCollection GetSortedActionItems() + { + DesignerActionItemCollection actionItems = new(); + + actionItems.Add(new DesignerActionHeaderItem(Behavior)); + + actionItems.Add(new DesignerActionPropertyItem( + nameof(CustomProperty), + "CustomProperty definition", + Behavior, + "Controls the values of the CustomProperty Definition.")); + + actionItems.Add(new DesignerActionMethodItem( + this, + nameof(InvokeRdTypeEditor), + "Invokes the custom TypeEditor...", + true)); + + return actionItems; + } + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/ControlDesigner/RdControlDesigner.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/ControlDesigner/RdControlDesigner.cs new file mode 100644 index 0000000..8ef8ac1 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/ControlDesigner/RdControlDesigner.cs @@ -0,0 +1,51 @@ +using System.Drawing; +using System.Windows.Forms; +using Microsoft.DotNet.DesignTools.Designers; +using Microsoft.DotNet.DesignTools.Designers.Actions; + +namespace RootDesignerDemo.Designer.Server +{ + /// + /// The control designer of the RdControl. + /// + internal partial class RdControlDesigner : ControlDesigner + { + /// + /// Attaches the action lists to the control designer. + /// + /// + /// Note: Action lists for the out-of-process Designer can be implemented exactly as they would be for the in-process + /// designer, except: The control designer has to be compiled against the Winforms Designer Extensibility SDK, and ActionList + /// related classes must come from the namespace. + /// + public override DesignerActionListCollection ActionLists + => new() + { + new ActionList(this) + }; + + protected override void OnPaintAdornments(PaintEventArgs paintEventArgs) + { + base.OnPaintAdornments(paintEventArgs); + + // If you want to paint custom adorner or other GDI+ based content, + // use the paintEventArgs' Graphics methods to render it. + + // Drawing the frame around the ClientRectangle with a dotted brush... + if (!(SelectionService?.GetComponentSelected(Control) ?? false)) + { + using var pen = new Pen(Control.ForeColor); + + // ...if the control is not currently selected. + pen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot; + using var brush = new SolidBrush(Control.ForeColor); + + var clientRect = Control.ClientRectangle; + clientRect.Inflate(-1, -1); + + paintEventArgs.Graphics.DrawRectangle(pen, clientRect); + } + } + } +} + diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/Handler/CreateRdTypeEditorVMHandler.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/Handler/CreateRdTypeEditorVMHandler.cs new file mode 100644 index 0000000..08ee276 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/Handler/CreateRdTypeEditorVMHandler.cs @@ -0,0 +1,28 @@ +using RootDesignerDemo.Protocol; +using RootDesignerDemo.Protocol.Endpoints; +using Microsoft.DotNet.DesignTools.Protocol.Endpoints; + +namespace RootDesignerDemo.Designer.Server.Handlers +{ + /// + /// The handler for . + /// This actually creates the ViewModel and returns it via its request class. + /// + [ExportRequestHandler(EndpointNames.CreateRdTypeEditorVM)] + internal class CreateRdTypeEditorVMHandler + : RequestHandler + { + public override CreateRdTypeEditorVMResponse HandleRequest(CreateRdTypeEditorVMRequest request) + { + // Gets the respective DesignerHost of the sessionId, which has been passed by the client. + var designerHost = GetDesignerHost(request.SessionId); + + // Creates the ViewModel and passes the DesignerHost. + var viewModel = CreateViewModel(designerHost); + + // The ViewModel then initializes and wraps itself into the response class + // so it can be returned to the client. + return viewModel.Initialize(request.RdControlProxy!); + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/Handler/RdTypeEditorOkClickHandler.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/Handler/RdTypeEditorOkClickHandler.cs new file mode 100644 index 0000000..3b3ad1c --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/Handler/RdTypeEditorOkClickHandler.cs @@ -0,0 +1,28 @@ +using RootDesignerDemo.Protocol; +using RootDesignerDemo.Protocol.Endpoints; +using Microsoft.DotNet.DesignTools.Protocol.Endpoints; + +namespace RootDesignerDemo.Designer.Server.Handlers +{ + [ExportRequestHandler(EndpointNames.RdTypeEditorEditorOKClick)] + internal class RdTypeEditorOkClickHandler : RequestHandler + { + /// + /// Handler for the TypeEditor Dialog's OK-Click event. + /// + /// Contains the ViewModel reference and the new PropertyStoreData, + /// which contain the new values which have been edited/entered in the TypeEditor dialog. + /// The response, which is empty in this case, but the conventions need to be honored. + public override RdTypeEditorOKClickResponse HandleRequest(RdTypeEditorOKClickRequest request) + { + // Getting the ViewModel passed via the endpoint's request class. + var viewModel = (RdTypeEditorVM)request.ViewModel; + + // Delegate the actual handling of the OKClick event to the server-side ViewModel. + viewModel.OKClick(request.PropertyStoreData); + + // Nothing really to return, just honoring the conventions. + return RdTypeEditorOKClickResponse.Empty; + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/RdTypeEditorVM.Factory.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/RdTypeEditorVM.Factory.cs new file mode 100644 index 0000000..fb93124 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/RdTypeEditorVM.Factory.cs @@ -0,0 +1,19 @@ +using System; +using RootDesignerDemo.Protocol; +using Microsoft.DotNet.DesignTools.ViewModels; + +namespace RootDesignerDemo.Designer.Server +{ + internal partial class RdTypeEditorVM + { + /// + /// Factory class which generates the RdTypeEditorViewModel. + /// + [ExportViewModelFactory(ViewModelNames.RdTypeEditorVM)] + private class Factory : ViewModelFactory + { + protected override RdTypeEditorVM CreateViewModel(IServiceProvider provider) + => new(provider); + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/RdTypeEditorVM.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/RdTypeEditorVM.cs new file mode 100644 index 0000000..3f64e8f --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RdTypeEditor/RdTypeEditorVM.cs @@ -0,0 +1,64 @@ +using System; +using System.Linq; +using RootDesignerDemo.Protocol.DataTransport; +using RootDesignerDemo.Protocol.Endpoints; +using Microsoft.DotNet.DesignTools.ViewModels; +using RootDesignerDemo.Controls; + +namespace RootDesignerDemo.Designer.Server +{ + /// + /// The server-side ViewModel for controlling the RdTypeEditor UI. + /// + /// + /// 'ViewModel' in this context is a class which holds the logic/properties to control the UI. This is the + /// server-side part, but there is also a client-side part of that ViewModel. The server-side provides the logic based + /// on the real types of the RdTypeEditor, running in the context of the TFM of the custom control. It communicates + /// to the client-side ViewModel part, which _then_ controls the client-side hosted UI, which in turn runs in the + /// TFM-context of Visual Studio. + /// + internal partial class RdTypeEditorVM : ViewModel + { + public RdTypeEditorVM(IServiceProvider provider) + : base(provider) + { + } + + public CreateRdTypeEditorVMResponse Initialize(object propertyStoreObject) + { + var propertyStore = (RdPropertyStore)propertyStoreObject; + + return new CreateRdTypeEditorVMResponse( + this, + propertyStore is null + ? null + : new RdPropertyStoreData( + propertyStore.SomeMustHaveId, + propertyStore.DateCreated, + propertyStore.ListOfStrings?.ToArray(), + (byte)propertyStore.CustomEnumValue)); + } + + internal void OKClick(RdPropertyStoreData propertyStoreData) + { + // We're constructing the actual PropertyStore content based + // on the data that the user edited and the View sent to the server. + + PropertyStore = new( + propertyStoreData.SomeMustHaveId, + propertyStoreData.DateCreated, + propertyStoreData.ListOfStrings?.ToList(), + (RdPropertyStoreEnum)propertyStoreData.CustomEnumValue); + + // So, the server-side ViewModel now holds the edited, commited result. + // The question now is: How does the ViewModel property find the way back + // to the control? + // That's been done client-side: On the client, the client-side ViewModel holds the reference to the this + // PropertyStore property over a ProxyObject. When the User clicks OK in the editor, that codeflow is + // returned to the client-side part of the TypeEditor. That is, where the assignment from this ViewModel + // to the actual Property of the Control happens. + } + + public RdPropertyStore? PropertyStore { get; set; } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesigner/BufferedRenderer.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesigner/BufferedRenderer.cs new file mode 100644 index 0000000..866927f --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesigner/BufferedRenderer.cs @@ -0,0 +1,161 @@ +using System; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using Microsoft.DotNet.DesignTools.Designers; + +namespace RootDesignerDemo.Designer.Server; + +public class BufferedRenderer : IDisposable +{ + [DllImport("user32.dll")] + public static extern IntPtr WindowFromDC(IntPtr hdc); + + public event PaintEventHandler? Paint; + + private Bitmap? _activeBitmap; + private Bitmap? _upcomingBitmap; + private DesignerScrollableControl _parentControl; + private bool _disposedValue; + private Graphics? _adornerGraphics; + + public BufferedRenderer(DesignerScrollableControl parentControl) + { + _parentControl = parentControl ?? throw new ArgumentNullException(nameof(parentControl)); + _parentControl.Resize += OnParentControlResize; + _parentControl.PostPaint += OnParentPostPaint; + + InitializeBitmaps(); + _parentControl.Invalidate(); + } + + private void InitializeBitmaps() + { + if (_disposedValue) + { + throw new ObjectDisposedException(nameof(BufferedRenderer)); + } + + _activeBitmap?.Dispose(); + _upcomingBitmap?.Dispose(); + + if (_parentControl.ClientSize.Width == 0 || _parentControl.ClientSize.Height == 0) + { + return; + } + + _activeBitmap = new Bitmap(_parentControl.ClientSize.Width, _parentControl.ClientSize.Height, PixelFormat.Format32bppArgb); + _upcomingBitmap = new Bitmap(_parentControl.ClientSize.Width, _parentControl.ClientSize.Height, PixelFormat.Format32bppArgb); + + _activeBitmap.MakeTransparent(); + _upcomingBitmap.MakeTransparent(); + + _activeBitmap.Tag = "activeBitmap"; + _upcomingBitmap.Tag = "upcomingBitmap"; + } + + private void OnParentControlResize(object? sender, EventArgs e) + { + InitializeBitmaps(); + _parentControl.Invalidate(); + } + + private void OnParentPostPaint(object? sender, PaintEventArgs e) + { + if (_adornerGraphics is null) + { + _adornerGraphics = Graphics.FromHdc(e.Graphics.GetHdc()); + } + + if (_activeBitmap is null) + { + return; + } + + // Draw something on the screen + e.Graphics.DrawLine(Pens.Blue, 10, 10, 100, 100); + + OnPaintInternal(); + } + + protected virtual void OnPaint(PaintEventArgs e) + => Paint?.Invoke(this, e); + + public void Invalidate() + { + OnPaintInternal(); + } + + private void OnPaintInternal() + { + if (_upcomingBitmap is null + || _activeBitmap is null + || _adornerGraphics is null) + { + return; + } + + using (Graphics g = Graphics.FromImage(_activeBitmap)) + { + g.Clear(Color.Transparent); + OnPaint(new PaintEventArgs(g, _parentControl.ClientRectangle)); + //SwapBitmaps(); + } + + var bitmapRectangle = new Rectangle(0, 0, _activeBitmap.Width, _activeBitmap.Height); + + ImageAttributes imageAttributes = new ImageAttributes(); + imageAttributes.SetColorKey(Color.Transparent, Color.Transparent); + + _adornerGraphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; + + try + { + //_adornerGraphics.DrawImage( + // image: _activeBitmap!, + // destRect: bitmapRectangle, + // srcX: 0, + // srcY: 0, + // srcWidth: bitmapRectangle.Width, + // srcHeight: bitmapRectangle.Height, + // srcUnit: GraphicsUnit.Pixel, + // imageAttributes); + } + catch (Exception ex) + { + MessageBox.Show($"{ex.Message}\n{ex.StackTrace}"); + } + } + + private void SwapBitmaps() + { + var temp = _activeBitmap; + _activeBitmap = _upcomingBitmap; + _upcomingBitmap = temp; + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _parentControl.Resize -= OnParentControlResize; + _parentControl.PostPaint -= OnParentPostPaint; + _activeBitmap?.Dispose(); + _upcomingBitmap?.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesigner/ShapeRootDesigner.ShapeDesignerView.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesigner/ShapeRootDesigner.ShapeDesignerView.cs new file mode 100644 index 0000000..b344e98 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesigner/ShapeRootDesigner.ShapeDesignerView.cs @@ -0,0 +1,176 @@ +using System; +using System.ComponentModel.Design; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Threading; +using System.Windows.Forms; +using Microsoft.DotNet.DesignTools.Designers; +using Timer = System.Threading.Timer; + +namespace RootDesignerDemo.Designer.Server; + +public partial class ShapeRootDesigner +{ + // RootDesignerView is a simple control that will be displayed + // in the designer window. + private class ShapeDesignerView : DesignerScrollableControl + { + private Timer? _timer; + private bool _guard; + private int _counter = 0; + private bool _drawControlInProgress; + private Point _initialPosition; + private Rectangle _screenRectangle; + private BufferedRenderer? _surfaceRenderer; + private GraphicsPath? _currentSelectionRenderer; + + public ShapeDesignerView(IRootDesigner rootDesigner) : base(rootDesigner) + { + BackColor = Color.LightGray; + ForeColor = Color.Black; + + Font = new Font(Font.FontFamily.Name, 24.0f); + + _timer = new System.Threading.Timer(new TimerCallback(TimerProc), null, 0, 2000); + + // Add a Label and a Button to the Controls Collection: + Controls.Add(new Label() + { + Text = "Hello World", + Location = new Point(10, 200), + Size= new Size(200, 50) + }); + + Controls.Add(new Button() + { + Text = "Click Me", + Location = new Point(10, 250), + Size = new Size(200, 50) + }); + } + + // We want a transparent overlay window automatically created to draw adorners on. + // To that end, we return true here. The paint event for that transparent adorner window + // will then surface in the PostPaint event, passing the Graphics object of the adorner window. + protected override bool SupportPostPaint => true; + + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + + // If we had recreate the handle, we need to re-register the SurfaceRenderer. + if (_surfaceRenderer is not null) + { + _surfaceRenderer.Dispose(); + } + + _surfaceRenderer = new BufferedRenderer(this); + _surfaceRenderer.Paint += SurfaceRendererPaint; + Invalidate(); + } + + protected override void Dispose(bool disposing) + { + if (_surfaceRenderer is not null) + { + _surfaceRenderer.Paint -= SurfaceRendererPaint; + } + base.Dispose(disposing); + } + + private void TimerProc(object? state) + { + // We are requesting an Invalidation of the internal adorner windows, which we inserted in the stack, + // since this component overwrites SupportPostPaint, and therefore requests a transparent overlay window + // to be created. Its paint event can then be handled in OnPostPaint. + if (!this.IsHandleCreated) + { + return; + } + + this.BeginInvoke(new Action(() => + { + try + { + if (_guard) + return; + + _guard = true; + _surfaceRenderer?.Invalidate(); + + _counter++; + _guard = false; + + } + catch (Exception) + { + } + })); + } + + // When SupportPostPaint returns true, we create a transparent overlay, and route the paint event + // to the Surface Renderer, which is a BufferedRenderer, which will do the adorner painting. + private void SurfaceRendererPaint(object? sender, PaintEventArgs e) + { + // Draws the name of the component in large letters. + e.Graphics.DrawString($"{RootDesigner?.Component?.Site?.Name}: {_counter}", Font, Brushes.Blue, ClientRectangle); + e.Graphics.DrawPath(Pens.Blue, CurrentSelectionRenderer); + } + + // Note: This is NOT the original mouse down, but rather the mouse down, which is + // triggered by the input dispatcher (and originates from the input shield in the client). + protected override void OnMouseDown(MouseButtonDispatchEventArgs e) + { + base.OnMouseDown(e); + _drawControlInProgress= true; + } + + // Note: See note above. + protected override void OnMouseUp(MouseButtonDispatchEventArgs e) + { + if (!_drawControlInProgress) + return; + + CurrentSelectionRenderer.Reset(); + _drawControlInProgress = false; + _initialPosition = Point.Empty; + _screenRectangle = Rectangle.Empty; + _surfaceRenderer?.Invalidate(); + } + + internal GraphicsPath CurrentSelectionRenderer + { + get + { + if (_currentSelectionRenderer is null) + { + _currentSelectionRenderer = new GraphicsPath(); + } + + return _currentSelectionRenderer; + } + } + + // Note: See note above. + protected override void OnMouseMove(InputDispatchEventArgs e) + { + base.OnMouseMove(e); + + if (!_drawControlInProgress) + return; + + if (_initialPosition.IsEmpty) + { + _initialPosition = e.Location; + } + + Size rectangleSize = new Size(Math.Abs(_initialPosition.X - e.Location.X), Math.Abs(_initialPosition.Y - e.Location.Y)); + Rectangle currentSelectionArea = new(_initialPosition, rectangleSize); + + _screenRectangle = currentSelectionArea; + CurrentSelectionRenderer.Reset(); + CurrentSelectionRenderer.AddRectangle(_screenRectangle); + _surfaceRenderer?.Invalidate(); + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesigner/ShapeRootDesigner.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesigner/ShapeRootDesigner.cs new file mode 100644 index 0000000..4248ad6 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesigner/ShapeRootDesigner.cs @@ -0,0 +1,35 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using Microsoft.DotNet.DesignTools.Designers; + +namespace RootDesignerDemo.Designer.Server; + +[ToolboxItemFilter(ToolboxCategory, ToolboxItemFilterType.Require)] +public partial class ShapeRootDesigner : ComponentDesigner, IRootDesigner +{ + private const string ToolboxCategory = "SdkShapeRootDesigner"; + + // Member field of custom type RootDesignerView, a control that + // will be shown in the Forms designer view. This member is + // cached to reduce processing needed to recreate the + // view control on each call to GetView(). + private ShapeDesignerView? _designerView; + + public ViewTechnology[] SupportedTechnologies => new[] { ViewTechnology.Default }; + + // This method returns an instance of the view for this root + // designer. The "view" is the user interface that is presented + // in a document window for the user to manipulate. + object IRootDesigner.GetView(ViewTechnology technology) + { + if (technology != ViewTechnology.Default) + { + throw new ArgumentException("Not a supported view technology", "technology"); + } + + // Important: This method must return the same instance every time it is called! + _designerView ??= new ShapeDesignerView(this); + return _designerView; + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesignerDemo.Server.csproj b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesignerDemo.Server.csproj new file mode 100644 index 0000000..1291e95 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/RootDesignerDemo.Server.csproj @@ -0,0 +1,14 @@ + + + + net6.0-windows + true + enable + true + + + + + + + diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/TypeRoutingProvider.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/TypeRoutingProvider.cs new file mode 100644 index 0000000..547fc91 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.Server/TypeRoutingProvider.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Microsoft.DotNet.DesignTools.TypeRouting; + +namespace RootDesignerDemo.Designer.Server +{ + /// + /// Class holding the TypeRoutings for resolving the control designer type on the server. + /// + [ExportTypeRoutingDefinitionProvider] + internal class TypeRoutingProvider : TypeRoutingDefinitionProvider + { + public override IEnumerable GetDefinitions() + { + return new[] + { + new TypeRoutingDefinition( + TypeRoutingKinds.Designer, + nameof(RdControlDesigner), + typeof(RdControlDesigner)), + + new TypeRoutingDefinition( + TypeRoutingKinds.Designer, + nameof(ShapeRootDesigner), + typeof(ShapeRootDesigner)) + }; + + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.sln b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.sln new file mode 100644 index 0000000..bfa4f58 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerDemo.sln @@ -0,0 +1,77 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32407.343 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RootDesignerDemo.Protocol", "RootDesignerDemo.Protocol\RootDesignerDemo.Protocol.csproj", "{855DB9BD-6C22-416B-A708-549D746149DE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ControlDesigners", "ControlDesigners", "{D247B836-28E7-4022-9FEF-12AA0A6B1184}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RootDesignerDemo.Server", "RootDesignerDemo.Server\RootDesignerDemo.Server.csproj", "{A6E65470-05AA-4DAA-92AF-0EC47A5CB140}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Controls", "Controls", "{89AEA44E-519A-4200-8467-17F5524C6E51}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RootDesignerDemo.Controls", "RootDesignerDemo.Controls\RootDesignerDemo.Controls.csproj", "{A1AFE219-25B5-4B8F-B9C2-0514A71731B5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RootDesignerDemo.Package", "RootDesignerDemo.Package\RootDesignerDemo.Package.csproj", "{119E53BE-06D5-40FA-87B8-FED0F8D2A750}" + ProjectSection(ProjectDependencies) = postProject + {58EFB943-5A3B-427A-A6C2-0ADE29F5A4A8} = {58EFB943-5A3B-427A-A6C2-0ADE29F5A4A8} + {855DB9BD-6C22-416B-A708-549D746149DE} = {855DB9BD-6C22-416B-A708-549D746149DE} + {A1AFE219-25B5-4B8F-B9C2-0514A71731B5} = {A1AFE219-25B5-4B8F-B9C2-0514A71731B5} + {A6E65470-05AA-4DAA-92AF-0EC47A5CB140} = {A6E65470-05AA-4DAA-92AF-0EC47A5CB140} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution Items", "_Solution Items", "{80223DA1-D034-43FE-A3D9-B9FCE6B30CE4}" + ProjectSection(SolutionItems) = preProject + Directory.Build.targets = Directory.Build.targets + NuGet.config = NuGet.config + readme.md = readme.md + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RootDesignerTestApp", "RootDesignerTestApp\RootDesignerTestApp.csproj", "{A2522661-E23A-4ADF-8B1F-2306ADB0CA26}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RootDesignerDemo.Client", "RootDesignerDemo.Client\RootDesignerDemo.Client.csproj", "{58EFB943-5A3B-427A-A6C2-0ADE29F5A4A8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {855DB9BD-6C22-416B-A708-549D746149DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {855DB9BD-6C22-416B-A708-549D746149DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {855DB9BD-6C22-416B-A708-549D746149DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {855DB9BD-6C22-416B-A708-549D746149DE}.Release|Any CPU.Build.0 = Release|Any CPU + {A6E65470-05AA-4DAA-92AF-0EC47A5CB140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6E65470-05AA-4DAA-92AF-0EC47A5CB140}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6E65470-05AA-4DAA-92AF-0EC47A5CB140}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6E65470-05AA-4DAA-92AF-0EC47A5CB140}.Release|Any CPU.Build.0 = Release|Any CPU + {A1AFE219-25B5-4B8F-B9C2-0514A71731B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1AFE219-25B5-4B8F-B9C2-0514A71731B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1AFE219-25B5-4B8F-B9C2-0514A71731B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1AFE219-25B5-4B8F-B9C2-0514A71731B5}.Release|Any CPU.Build.0 = Release|Any CPU + {119E53BE-06D5-40FA-87B8-FED0F8D2A750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {119E53BE-06D5-40FA-87B8-FED0F8D2A750}.Debug|Any CPU.Build.0 = Debug|Any CPU + {119E53BE-06D5-40FA-87B8-FED0F8D2A750}.Release|Any CPU.ActiveCfg = Release|Any CPU + {119E53BE-06D5-40FA-87B8-FED0F8D2A750}.Release|Any CPU.Build.0 = Release|Any CPU + {A2522661-E23A-4ADF-8B1F-2306ADB0CA26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2522661-E23A-4ADF-8B1F-2306ADB0CA26}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2522661-E23A-4ADF-8B1F-2306ADB0CA26}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2522661-E23A-4ADF-8B1F-2306ADB0CA26}.Release|Any CPU.Build.0 = Release|Any CPU + {58EFB943-5A3B-427A-A6C2-0ADE29F5A4A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58EFB943-5A3B-427A-A6C2-0ADE29F5A4A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58EFB943-5A3B-427A-A6C2-0ADE29F5A4A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58EFB943-5A3B-427A-A6C2-0ADE29F5A4A8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {855DB9BD-6C22-416B-A708-549D746149DE} = {D247B836-28E7-4022-9FEF-12AA0A6B1184} + {A6E65470-05AA-4DAA-92AF-0EC47A5CB140} = {D247B836-28E7-4022-9FEF-12AA0A6B1184} + {A1AFE219-25B5-4B8F-B9C2-0514A71731B5} = {89AEA44E-519A-4200-8467-17F5524C6E51} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D80B6075-E0F0-4F58-9558-C41445569497} + EndGlobalSection +EndGlobal diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Form1.Designer.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Form1.Designer.cs new file mode 100644 index 0000000..2f63fb8 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Form1.Designer.cs @@ -0,0 +1,95 @@ +namespace RootDesignerTestApp +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + button1 = new Button(); + checkBox1 = new CheckBox(); + checkBox2 = new CheckBox(); + printDialog1 = new PrintDialog(); + SuspendLayout(); + // + // button1 + // + button1.Location = new Point(142, 32); + button1.Margin = new Padding(2); + button1.Name = "button1"; + button1.Size = new Size(112, 34); + button1.TabIndex = 1; + button1.Text = "button1"; + button1.UseVisualStyleBackColor = true; + // + // checkBox1 + // + checkBox1.AutoSize = true; + checkBox1.Location = new Point(540, 385); + checkBox1.Margin = new Padding(2); + checkBox1.Name = "checkBox1"; + checkBox1.Size = new Size(121, 29); + checkBox1.TabIndex = 2; + checkBox1.Text = "checkBox1"; + checkBox1.UseVisualStyleBackColor = true; + // + // checkBox2 + // + checkBox2.AutoSize = true; + checkBox2.Location = new Point(290, 212); + checkBox2.Margin = new Padding(4, 4, 4, 4); + checkBox2.Name = "checkBox2"; + checkBox2.Size = new Size(121, 29); + checkBox2.TabIndex = 3; + checkBox2.Text = "checkBox2"; + checkBox2.UseVisualStyleBackColor = true; + // + // printDialog1 + // + printDialog1.UseEXDialog = true; + // + // Form1 + // + AutoScaleDimensions = new SizeF(10F, 25F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(800, 450); + Controls.Add(checkBox2); + Controls.Add(checkBox1); + Controls.Add(button1); + Margin = new Padding(2); + Name = "Form1"; + Text = "Form1"; + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private Button button1; + private CheckBox checkBox1; + private CheckBox checkBox2; + private PrintDialog printDialog1; + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Form1.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Form1.cs new file mode 100644 index 0000000..386892c --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Form1.cs @@ -0,0 +1,10 @@ +namespace RootDesignerTestApp +{ + public partial class Form1 : Form + { + public Form1() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Form1.resx b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Form1.resx new file mode 100644 index 0000000..a9d7fb8 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Form1.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Program.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Program.cs new file mode 100644 index 0000000..d0d9384 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/Program.cs @@ -0,0 +1,17 @@ +namespace RootDesignerTestApp +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new Form1()); + } + } +} \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/RootDesignerTestApp.csproj b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/RootDesignerTestApp.csproj new file mode 100644 index 0000000..a25f70f --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/RootDesignerTestApp.csproj @@ -0,0 +1,15 @@ + + + + WinExe + net6.0-windows + enable + true + enable + + + + + + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/ShapeDocument.cs b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/ShapeDocument.cs new file mode 100644 index 0000000..a3695ec --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/ShapeDocument.cs @@ -0,0 +1,13 @@ +using RootDesignerDemo.Designer.Server; + +namespace RootDesignerDemo +{ + // This sample component inherits from RootDesignedComponent which + // uses the SampleRootDesigner. + public class ShapeDocument : ShapeDocumentBase + { + public ShapeDocument() + { + } + } +} diff --git a/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/ShapeDocument.resx b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/ShapeDocument.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/RootDesignerTestApp/ShapeDocument.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/ClientServerSample/readme.md b/Samples/RootDesigner/Net/ClientServerSample/readme.md new file mode 100644 index 0000000..d7eb454 --- /dev/null +++ b/Samples/RootDesigner/Net/ClientServerSample/readme.md @@ -0,0 +1,320 @@ +# Introduction to the Template Solution + +Since .NET 3.1 started to support the WinForms Runtime, a new WinForms designer +was needed to support .NET applications. This work required a near-complete +re-architect of the designer, as we responded to the differences between .NET +and the .NET Framework based WinForms designer everyone knows and loves. + +Until we added support for .NET Core applications there was only a single +process, *devenv.exe*, that both the Visual Studio environment and the application +being designed ran within. But .NET Framework and .NET Core can’t both run +together within *devenv.exe*, and as a result we had to take the designer out of +process. We call the existing process – the process that Visual Studio runs in – +the client process or the Visual Studio process, and the process which shows the +actual Form at design time, the server process or short *DesignToolsServer*. + +## Enter the DesignToolsServer + +While simple control designer scenarios like type converters, action lists or +CodeDom serializers don’t need any substantial rewrites, type editors are a +different beast altogether. + +To illustrate the additional requirements which arose by introducing different +processes, let’s look at a typical type editor for any property of type +`Image` like the Button’s *BackgroundImage* property: While the actual image +that you picked will be rendered on a button in the server process, the dialog +you picked the Image *with* runs in the context of Visual Studio. That in turn +means there is communication between the two processes necessary, which custom +type editors for the modern WinForms Designer need to take care of. In addition, +type editor solutions also need to provide a NuGet package whose individual +assemblies gets partly loaded into the Visual Studio process and partly into the +DesignToolsServer. And to that end, this NuGet needs to have a special +structure, which a dedicated Package project takes care of. If you want to learn +more about the concept of the modern WinForms Designer: [This blog +post](https://devblogs.microsoft.com/dotnet/state-of-the-windows-forms-designer-for-net-applications/) +describes the concept of the different processes in greater detail. + +## Projects which the templates create + +Setting all these things up manually means coordinating a lot of moving parts, +and there is a huge potential that things go wrong. The individual projects +created by this template help to prevent falling into those traps. The templates +create a series of projects and important solution folders, depending on your +needs for both C\# and Visual Basic. Let’s look at the projects which are part +of the template solution in detail: + +* **\_Solution Items:** This is not really a project, but rather a solution + folder, which holds the readme, the *Directory.Build* Target which determines + the NuGet package version for the used Designer SDK version and the + *NuGet.config* setting. If at any point you would need to change the Designer + SDK version which is used throughout the solution, you would only need to + change them in one spot: here. +* **RootDesignerDemo.Client** This is a project of the same target framework + version as Visual Studio, and it holds the actual type editor UI running in + the context of Visual Studio. It also contains the so-called client *view + model*, which is a UI controller class. It communicates on the one side with + its pendant in the server process and controls the client-based UI (a modal + WinForms Dialog) on the other side based on the server-provided data. +* **RootDesignerDemo.Server:** This project holds every aspect of the + control designer, which needs to be executed in the context of the server + process. Those are + + * The server-side view model, which provides the necessary data to the + client-side view model. + * The factory class, which generates the server-side view model. + * A custom CodeDom serializer for the custom property type the type editor is + handling, should one be needed. + * A custom designer action list which can be accessed at design time through + the designer action glyph of the control. Please note, that although these + classes are hosted purely in the server-side designer assembly, the UI for + the respective action list is still shown in the context of Visual Studio. + The necessary communication with the client is done completely behind the + scenes by the Designer SDK. So, even if it looks like the designer action + lists are handled exclusively by the server process, they are not. + * The actual control designer, which – as one example – paints the adornments + for the controls. This is the only part of the UI which is actually rendered + server-side, and although it looks like this rendering is done in the + context of Visual Studio, it is not. The rendering of the Form and all its + components at design time is done by the DesignToolsServer and just + projected on the client-area of Visual Studio Design surface. But! Although + rendered on the server-side, there is no direct interaction with the message + queue of the server. Every keyboard- and mouse-input is still received in the + Visual Studio-based client process, and the results of that are communicated + to the server. That is the way the WinForms Designer ensures that no + deadlocks will occur due to competing message queues of different processes. + +* **RootDesignerDemo.Protocol:** This project contains all the classes + which are necessary for the communication between the client and the server + process via [JSON-RPC](https://www.jsonrpc.org/). The Designer SDK provides + a series of base classes which are optimized for transferring + WinForms-typical data types between the client- and the server-process. A + typical protocol library for a control designer builds on those classes. + +* **RootDesignerDemo:** This is the project, which contains your actual + custom control(s). + +* **RootDesignerDemo.Package:** This is the project which creates the NuGet + package. This NuGet package organizes the individual control designer + components for the DesignToolsServer server process and the Visual Studio + client process in respective folders, so that the required parts are + available for the processes at design time. + +## Invoking Type Editors, In Process vs. Out-Of-Process + +In the classic framework, invoking of a type editor is a straightforward +procedure. Here is what happens, when the user starts to edit a value of a +property by opening a type editor via the Visual Studio’s property browser: + +* The user wants to set a value for a property of a control which either + doesn’t have a default string representation (like an image or a sound file) + or is a composite property type, which demands a more complex user + interaction. A type editor for that property type is defined by the + `EditorAttribute` (see class `RdPropertyStore` in the template + project as an example). + +* The custom type editor class, which is usually provided along with the type + the custom control provides for that special property, is instantiated when + the user clicks on the …-Button in the property’s cell of Visual Studio’s + property browser. + +* The property browser now calls the `EditValue` method of the type editor + and passes the value of the property to set. In other words: The type editor + receives the instance of the custom property. In the example of the + `BackgroundImage` property of the Button control, the instance would be + the actual image. In our template example, that instance would be of type + `RdPropertyStore`. + +* The type editor now gets the `UIDialogService`, which enables the type + editor to display a modal (WinForms) dialog in the context of Visual Studio. + It is important to show the dialog in the context of Visual Studio, because + otherwise Windows message processing queues of different processes would run + concurrently, compete and quickly dead-lock each other, so + that Visual Studio would freeze. + +* The UI converts the value in an editable format, gets the updates from the + users, and then converts the edits back to the type of that control’s custom + property. The value, which the type editor returns, is now assigned by the + property browser to the property. + +And here now is the all-important difference compared to the out-of-process +Designer scenario: When the property browser asks the UITypeEditor to display +the visual representation of the value, that type’s value is not available in +the context of Visual Studio. The reason: The property browser runs in a process +targeting a different .NET version than the process that defines the type. +Visual Studio runs, for example, against .NET Framework 4.7.2 while the custom +control library you are developing is e. g. targeting .NET 7. There is simply no +supported way that .NET Framework can deal with types defined in or based on +types defined .NET 7. So, instead of giving the UITypeEditor the control’s +custom/special property’s value directly, it’s handing it via a so-called *proxy +object*. + +The concept of proxy objects in the client (Visual Studio) process does require +a special infrastructure for handling user inputs in custom type editors. Let’s +look at what infrastructure components of the Designer we need to understand, +before we talk about the workflow for setting the value in the OOP scenario: + +* **Using View Models:** View models are for controlling aspects of a UI + without having a direct reference to the UI specific components. Don’t + confuse view models in the context of the WinForms Designer with view models + you might know from XAML languages, though! They are only remote relatives. + Yes, they are controlling the UI in a UI-technology independent way – and + that’s the main aspect of a view model. But no, in contrast to XAML, they + are not doing this by direct data binding. View models in the context of the + Designer are rather used to sync certain conditions of the UI between the + client and the server process. The class `RdTypeEditorVMClient` + provides a static method `Create`, which is the dedicated way to create a + view model. You pass it the service provider and also the proxy object of + the instance of the property value to edit, which the client-side type + editor just got from the property browser. + +* **Sessions and the DesignToolsClient:** For the communication with the + DesignToolsServer server process, the Designer not only need a communication + endpoint on the server, but also on the client side. The + `DesignToolsClient` class represents that client-side endpoint and + provides the basic mechanisms for communication with the server. To separate + the concerns of each open designable WinForms document within Visual Studio, + each open designer is associated with a session. The `Create` method in + the sample shows how to retrieve a session along with and the + `DesignToolsClient` through the service provider, and can now, with both + objects, talk to the server – in this case to create the respective + *server-side* view model. + +* **Proxy classes:** These classes solve the basic challenge: Representing + objects of server-side .NET version types which are not known to the client. + If you select a component in the Designer, what the property browser “sees” + is a proxy object which kind of points to the real object in the server + process. A value of a property of a complex type is also represented by a + proxy object, since – again – its type only exists on the server, because + it’s targeting a different .NET version. And yet again: Also, the view model + returned from the server is not the actual server-side view model instance + (it can’t, because, again, it might contain or be based on types that are + not existing in the client-side target framework). The client-side view + model will need this view model-proxy to synchronize necessary data across + the process boundaries. + +* **Data transport and remote procedure calls:** The communication between + client and server is always synchronous, so blocking: You define endpoints + in the server-process, which the client calls. The client waits, until the + server has finished processing those remote procedure calls. Basically, each + endpoint needs three dedicated classes: A *Request* class, defined in the + Protocol project (see below), which transports necessary data to the + DesignToolsServer. A *Response* class, which transports result data back to + the client process – also defined in the Protocol project. And lastly a + *Handler* class, which is the server-side remote-procedure to call, if you + will. In this template, two endpoints are already predefined: + `CreateRdTypeEditorVM` creates the server-side view model, whose + instance is then hosted as a proxy-object in the client-side view model, so + the communication and data exchange can be simplified over those two + instances. And then there is also the `TypeEditorOKClick` endpoint: This + method is called when the user clicked the OK button of the type editor + during design time to indicate that they changed the value passed by the + property browser. Since the custom property type only exists in the + DesignToolsServer, the client can only pass over the individual data + fragments from what the user entered in the dialog to the server process. + But it is the server which then creates the actual instance of the value of + what it got passed from the client. And it eventually assigns that value to + the property of the user control. + +Now, with these important basics in mind, here is the workflow for setting a +property value via a type editor in the out-of-process Designer scenario in +detail: + +* As in the classic in-process-Scenario, the user wants to set a value for a +custom property. And again, a type editor for that property type is defined by +the `EditorAttribute` (see class `RdPropertyStore` in the template +project). The first important difference: Since the type in question might not +be available in the client process’ target framework, the type can only be +defined as a string. Also as before, the custom type editor class is +instantiated, when the user clicks on the …-Button in the property’s cell of the +property browser. Now, here is a first exciting challenge that the modern +designer faces: When the custom control lives only in the server process, and +the actual type editor lives only in the client, how does the WinForms Designer +finds the type editor on the client side? This is where an important component +in the client designer project comes into play: the `TypeRoutingProvider`. It +holds a table of `TypeRoutingDefinition` objects and assigns the names of the +editors to the actual types. That means, if you were ever to add additional type +editors for other property types or controls to your control library solution, +this table must be ament and maintained accordingly. It’s best practice to use +the `EditorNames`definitions in the Protocol project to that end, since it +minimizes typos by providing IntelliSense support. + +* Now, yet again, the property browser calls the `EditValue` method of the + type editor and passes the value of the property to set. But the value now is + not the actual value of the property. It’s rather the proxy object, which + points to the actual instance of the value in the server process. This also + means, processing the value must be happening in the server-process. To this + end, the two view model types to control the edit procedure need now to be + used: one on the client side (`RdTypeEditorVMClient`), and one on the + server side (`RdTypeEditorVM`). The template creates both classes for + you, along with the infrastructure methods to set them up. + +* The static `Create` method of the client-side view model has now all the + information to create the actual client-side view model by calling the + `CreateViewModelClient` method of the Designer service provider, and for + that it passes the server-side proxy to the server view model. + +* The type editor’s main task is to edit the value of type + `RdPropertyStore`. To keep the example simple, this is just a composite + type, composed of a `string`, a `DateTime`, a list of `string` elements and a + custom Enum. As a reminder: since this type only exists server-side, the UI + (being in the context of Visual Studio) cannot use this type. This is where + the Protocol project/assembly comes into play. The Protocol project defines + all the transport classes, which can be used in either process. It is defined + as a .NET standard library, so all its types can be projected and used in both + .NET and .NET Framework projects equally. So, to solve this requirement, we + mirror the `RdPropertyStore` type with a special data class we define in + the Protocol project named `RdPropertyStoreData`. This type also provides + the necessary methods to convert the data its hosting into the JSON format and + back from it, which is needed to transport it across the process’s boundaries. + With that, the response class for the endpoint to create the server-side view + model not only takes the proxy of the server-side view model, but also the + original values of the types, the custom property type is composed of. And + this data we now use to populate the type editor’s dialog client side. + +* The user now edits the values. + +* When the user clicks *OK*, we validate the data on the client inside the + `RdTypeEditorDialog`. And if the validation passes, the dialog returns + `DialogResult.OK`, and we call the `ExecuteOKCommand` method of the client + view model to kick of the data transfer to the server. This method now sends + the `RdTypeEditorOKClickRequest` to the server and passes the individual + retrieved data from the user’s input of the dialog along. The endpoint’s + handler gets those data and passes - in turn - that data to the server-side + view model, which then again calls its `OnClick` method, composes the actual + instance of the custom control’s property type, and stores it in the + `PropertyStore` property of the server-side view model. And with that the call + chain seems to end here. So, the server-side view model now holds the edited + and committed result. But wait! How does the view model property find the way + back to the control’s property? That last step is done client-side, and it’s + kind of subtle: Remember? When the client-side view model got created, it not + only triggered the creation of the server-side view model. It also requested + the proxy of that view model to be returned to the client side. On the client, + the client-side view model holds the reference to the server-side view model’s + `PropertyStore` property over a proxy object. When the user clicks *OK* in the + editor, that code flow is returned to the type editor (running in the context + of Visual Studio), which had opened the modal dialog to begin with. Now, back in + the actual type editor class, it is where the assignment from this view model + to the actual property of the control happens: + +```cs + var dialogResult = editorService.ShowDialog(_customTypeEditorDialog); + if (dialogResult == DialogResult.OK) + { + // By now, the UI of the Editor has asked its (client-side) ViewModel + // to run the code which updates the property value. It passes the data to + // the server, which in turn updates the server-side ViewModel. + // When it's time to return the value from the client-side ViewModel back to the + // Property Browser (which has called the type editor in the first place), the client-side + // ViewModel accesses its PropertyStore property, which in turn gets the required PropertyStore + // proxy object directly from the server-side ViewModel. + value = viewModelClient.PropertyStore; + } +``` + +The `PropertyStore` property of the `ViewModelClient` doesn’t have a dedicated +backing field to hold the value. Rather, it uses the infrastructure of the proxy +to communicate with the server-side view model to get the just created proxy of +the server-side view model’s `PropertyStore` content directly. And the proxy +object is what we need here: Again, since the client doesn’t know the type, it +can only deal with the proxy objects which point and represent the server types +instead. diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/CustomControl/SimpleCustomControl.cs b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/CustomControl/SimpleCustomControl.cs new file mode 100644 index 0000000..d6b7f1f --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/CustomControl/SimpleCustomControl.cs @@ -0,0 +1,10 @@ +using System.ComponentModel; +using RootDesignerDemo.Design; + +namespace RootDesignerDemo; + +[Designer(typeof(SimpleCustomControlDesigner))] +public class SimpleCustomControl : Panel +{ + public bool SampleProperty { get; set; } +} diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/CustomControl/SimpleCustomControlDesigner.cs b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/CustomControl/SimpleCustomControlDesigner.cs new file mode 100644 index 0000000..314c182 --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/CustomControl/SimpleCustomControlDesigner.cs @@ -0,0 +1,80 @@ +using Microsoft.DotNet.DesignTools.Designers.Actions; +using Microsoft.DotNet.DesignTools.Designers; + +using System.ComponentModel; + +namespace RootDesignerDemo.Design +{ + /// + /// Control Designer for the + /// + internal partial class SimpleCustomControlDesigner : ControlDesigner + { + /// + /// Hooks up the Action lists. + /// + /// + /// Action lists for the OOP-Designer can be implemented exactly like for the in-process Designer. The Designer + /// has to be compiled though against the Designer SDK, and ActionList related classes must come from the + /// namespace. + /// + public override DesignerActionListCollection ActionLists + => new() + { + new ActionList(this) + }; + + protected override void OnPaintAdornments(PaintEventArgs pe) + { + base.OnPaintAdornments(pe); + } + + /// + /// Action lists implementation for the . + /// + /// + /// Note: Action lists for the OOP-Designer can be implemented exactly like for the in-process Designer. The Designer + /// has to be compiled though against the Designer SDK, and ActionList related classes must come from the + /// namespace. + /// + private class ActionList : DesignerActionList + { + private const string Behavior = nameof(Behavior); + private const string Data = nameof(Data); + + private readonly ComponentDesigner _designer; + + public ActionList(SimpleCustomControlDesigner designer) + : base(designer.Component) + { + _designer = designer; + } + + public bool SampleProperty + { + get => ((SimpleCustomControl)Component!).SampleProperty; + + // Do this instead: + set => + TypeDescriptor.GetProperties(Component!)[nameof(SampleProperty)]! + .SetValue(Component, value); + } + + public override DesignerActionItemCollection GetSortedActionItems() + { + DesignerActionItemCollection actionItems = new(); + + actionItems.Add(new DesignerActionHeaderItem(Behavior)); + actionItems.Add(new DesignerActionHeaderItem(Data)); + + actionItems.Add(new DesignerActionPropertyItem( + nameof(SampleProperty), + "Some Sample Property", + Behavior, + "A demo property to have something to show for the Action List.")); + + return actionItems; + } + } + } +} diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner.Lib.csproj b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner.Lib.csproj new file mode 100644 index 0000000..88c43e2 --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner.Lib.csproj @@ -0,0 +1,14 @@ + + + + net7.0-windows + enable + true + enable + + + + + + + diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/SampleRootDesigner.SampleRootDesignerView.cs b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/SampleRootDesigner.SampleRootDesignerView.cs new file mode 100644 index 0000000..9733d8b --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/SampleRootDesigner.SampleRootDesignerView.cs @@ -0,0 +1,58 @@ +using System.Diagnostics; +using Microsoft.DotNet.DesignTools.AsyncToolbox; +using Microsoft.DotNet.DesignTools.Designers; + +namespace RootDesignerDemo; + +public partial class ShapeRootDesigner +{ + // RootDesignerView is a simple control that will be displayed + // in the designer window. + private class SampleRootDesignerView : RootDesignerView + { + private ShapeRootDesigner _designer; + private IAsyncToolboxService _asyncToolboxService; + private System.Threading.Timer _timer; + private bool _guard; + private int _counter = 0; + + public SampleRootDesignerView(ShapeRootDesigner designer) + { + _designer = designer; + BackColor = Color.LightGray; + Font = new Font(Font.FontFamily.Name, 24.0f); + + _asyncToolboxService = designer.GetRequiredService(); + _timer = new System.Threading.Timer(new TimerCallback(TimerProc), null, 0, 2000); + } + + private async void TimerProc(object? state) + { + if (_guard) + return; + + _guard = true; + + if (Debugger.IsAttached) + Debugger.Break(); + + if (_asyncToolboxService != null) + { + await _asyncToolboxService.SetCursorAsync(); + } + + Invalidate(); + + _counter++; + _guard = false; + } + + protected override void OnPaint(PaintEventArgs pe) + { + base.OnPaint(pe); + + // Draws the name of the component in large letters. + pe.Graphics.DrawString($"{_designer.Component.Site.Name}: {_counter}", Font, Brushes.Black, ClientRectangle); + } + } +} diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/SampleRootDesigner.cs b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/SampleRootDesigner.cs new file mode 100644 index 0000000..293363c --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/SampleRootDesigner.cs @@ -0,0 +1,114 @@ +using System.ComponentModel; +using System.ComponentModel.Design; +using Microsoft.DotNet.DesignTools.Designers; + +namespace RootDesignerDemo; + +[ToolboxItemFilter(ToolboxCategory, ToolboxItemFilterType.Require)] +public partial class ShapeRootDesigner : ComponentDesigner, IRootDesigner //, IToolboxUser +{ + private const string ToolboxCategory = "ShapeRootDesigner"; + + // Member field of custom type RootDesignerView, a control that + // will be shown in the Forms designer view. This member is + // cached to reduce processing needed to recreate the + // view control on each call to GetView(). + private RootDesignerView? _designerSurface; + + //private IToolboxService? _toolboxService = null; + //private ToolboxItemCollection? _tools; + + public ViewTechnology[] SupportedTechnologies => new[] { ViewTechnology.Default }; + + //public bool GetToolSupported(ToolboxItem tool) + //{ + // return !(tool.Properties[ToolboxCategory] is null); + //} + + //public void ToolPicked(ToolboxItem tool) + //{ + // // TODO: Add the code, which adds a new shape to the designer surface. + //} + + // This method returns an instance of the view for this root + // designer. The "view" is the user interface that is presented + // in a document window for the user to manipulate. + object IRootDesigner.GetView(ViewTechnology technology) + { + if (technology != ViewTechnology.Default) + { + throw new ArgumentException("Not a supported view technology", "technology"); + } + + if (_designerSurface == null) + { + // Some type of displayable Form or control is required + // for a root designer that overrides GetView(). In this + // example, a Control of type RootDesignerView is used. + // Any class that inherits from Control will work. + _designerSurface = new SampleRootDesignerView(this); + + //_toolboxService = (IToolboxService)this.GetService(typeof(IToolboxService)); + //// If an IToolboxService was located, update the + //// category list. + //if (_toolboxService is null) + //{ + // MessageBox.Show("Couldn't retrieve Toolbox Service!"); + //} + //else + //{ + // SetupToolboxItems(); + //} + } + + return _designerSurface; + } + + //private void SetupToolboxItems() + //{ + // CreateToolboxItem("Line Tool", "LineTool", "ShapeRootDesigner", + // RootDesignerDemo.Properties.Resources.Rectangle); + // CreateToolboxItem("Rectangle Tool", "RectangleTool", "ShapeRootDesigner", + // RootDesignerDemo.Properties.Resources.Rectangle); + // CreateToolboxItem("Text Tool", "TextTool", "ShapeRootDesigner", + // RootDesignerDemo.Properties.Resources.Rectangle); + //} + + private void CreateToolboxItem(string toolDisplayName, string toolTypeName, string toolboxFilterString, Bitmap toolboxBitmap) + { + //ToolboxItem toolboxItem; + //ToolboxItemFilterAttribute toolboxItemFilterAttribute; + //ToolboxItemFilterAttribute[] toolboxItemFilterAttributeArray; + + //string toolboxItemName =$"{ToolboxCategory}.{toolTypeName}"; + + //foreach (ToolboxItem existingToolboxItem in _toolboxService.GetToolboxItems(ToolboxCategory)) + //{ + // if (existingToolboxItem.TypeName != toolboxItemName) + // { + // continue; + // } + + // // We found the item, so remove it... + // _toolboxService.RemoveToolboxItem(existingToolboxItem); + // break; + //} + + //// ...and add it again. + //toolboxItem = new ToolboxItem() + //{ + // TypeName = toolboxItemName, + // DisplayName = toolDisplayName, + // Description = $"Description of {toolDisplayName}", + // Bitmap = toolboxBitmap + //}; + + //toolboxItemFilterAttribute = new ToolboxItemFilterAttribute(toolboxFilterString, ToolboxItemFilterType.Require); + //toolboxItemFilterAttributeArray = new ToolboxItemFilterAttribute[] { toolboxItemFilterAttribute }; + //toolboxItem.Filter = (ICollection)toolboxItemFilterAttributeArray; + //toolboxItem.Properties[ToolboxCategory] = toolTypeName; + //_toolboxService.AddToolboxItem(toolboxItem, ToolboxCategory); + + return; + } +} diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/ShapeDocumentBase.cs b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/ShapeDocumentBase.cs new file mode 100644 index 0000000..e400362 --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/ShapeDocumentBase.cs @@ -0,0 +1,19 @@ +using System.ComponentModel; +using System.ComponentModel.Design; + +namespace RootDesignerDemo; + +// The following attribute associates the SampleRootDesigner designer +// with the SampleComponent component. +[Designer(typeof(ShapeRootDesigner), typeof(IRootDesigner)), + ToolboxItem(false)] +public class ShapeDocumentBase : Component +{ + public ShapeDocumentBase() + { + } + + private void InitializeComponent() + { + } +} diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/ShapeDocumentBase.resx b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/ShapeDocumentBase.resx new file mode 100644 index 0000000..e5858cc --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/RootDesigner/ShapeDocumentBase.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/ShapeComponents/SimpleBoxComponent.Designer.cs b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/ShapeComponents/SimpleBoxComponent.Designer.cs new file mode 100644 index 0000000..de7415a --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/ShapeComponents/SimpleBoxComponent.Designer.cs @@ -0,0 +1,35 @@ +namespace RootDesignerDemo.ShapeComponents; + +partial class SimpleBoxComponent +{ + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion +} diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/ShapeComponents/SimpleBoxComponent.cs b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/ShapeComponents/SimpleBoxComponent.cs new file mode 100644 index 0000000..239f16c --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/ShapeComponents/SimpleBoxComponent.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; + +namespace RootDesignerDemo.ShapeComponents; + +public partial class SimpleBoxComponent : Component +{ + public SimpleBoxComponent() + { + InitializeComponent(); + } + + public SimpleBoxComponent(IContainer container) + { + container.Add(this); + + InitializeComponent(); + } +} diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/ShapeComponents/SimpleBoxComponent.resx b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/ShapeComponents/SimpleBoxComponent.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesigner.Lib/ShapeComponents/SimpleBoxComponent.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo.sln b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo.sln new file mode 100644 index 0000000..4b497da --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32130.36 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RootDesigner.Lib", "RootDesigner.Lib\RootDesigner.Lib.csproj", "{9B043351-707C-4875-8465-457824F07EC3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RootDesignerDemo", "RootDesignerDemo\RootDesignerDemo.csproj", "{D6ED5824-17EC-4304-A763-5D72DF2483FE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9B043351-707C-4875-8465-457824F07EC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B043351-707C-4875-8465-457824F07EC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B043351-707C-4875-8465-457824F07EC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B043351-707C-4875-8465-457824F07EC3}.Release|Any CPU.Build.0 = Release|Any CPU + {D6ED5824-17EC-4304-A763-5D72DF2483FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6ED5824-17EC-4304-A763-5D72DF2483FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6ED5824-17EC-4304-A763-5D72DF2483FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6ED5824-17EC-4304-A763-5D72DF2483FE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {ED24AAF5-3FB6-43A3-991E-9E10A798B727} + EndGlobalSection +EndGlobal diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Form1.Designer.cs b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Form1.Designer.cs new file mode 100644 index 0000000..e7576bd --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Form1.Designer.cs @@ -0,0 +1,57 @@ +namespace RootDesignerDemo +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + simpleCustomControl1 = new SimpleCustomControl(); + SuspendLayout(); + // + // simpleCustomControl1 + // + simpleCustomControl1.Location = new Point(90, 64); + simpleCustomControl1.Name = "simpleCustomControl1"; + simpleCustomControl1.SampleProperty = true; + simpleCustomControl1.Size = new Size(462, 283); + simpleCustomControl1.TabIndex = 0; + // + // Form1 + // + AutoScaleDimensions = new SizeF(10F, 25F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(800, 450); + Controls.Add(simpleCustomControl1); + Name = "Form1"; + Text = "Form1"; + ResumeLayout(false); + } + + #endregion + + private SimpleCustomControl simpleCustomControl1; + } +} \ No newline at end of file diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Form1.cs b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Form1.cs new file mode 100644 index 0000000..48c7d28 --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Form1.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace RootDesignerDemo +{ + public partial class Form1 : Form + { + public Form1() + { + InitializeComponent(); + } + } +} diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Form1.resx b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Form1.resx new file mode 100644 index 0000000..a395bff --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Form1.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Program.cs b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Program.cs new file mode 100644 index 0000000..ea0542a --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/Program.cs @@ -0,0 +1,17 @@ +namespace RootDesignerDemo; + +internal static class Program +{ + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + + Application.Run(new Form1()); + } +} diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/RootDesignerDemo.csproj b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/RootDesignerDemo.csproj new file mode 100644 index 0000000..2bc9ad9 --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/RootDesignerDemo.csproj @@ -0,0 +1,15 @@ + + + + WinExe + net7.0-windows + enable + true + enable + + + + + + + diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/ShapeDocument.cs b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/ShapeDocument.cs new file mode 100644 index 0000000..0774242 --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/ShapeDocument.cs @@ -0,0 +1,14 @@ +namespace RootDesignerDemo +{ + // This sample demonstrates how to provide the root designer view, or + // design mode background view, by overriding IRootDesigner.GetView(). + + // This sample component inherits from RootDesignedComponent which + // uses the SampleRootDesigner. + public class ShapeDocument : ShapeDocumentBase + { + public ShapeDocument() + { + } + } +} diff --git a/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/ShapeDocument.resx b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/ShapeDocument.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Samples/RootDesigner/Net/Simple Sample/RootDesignerDemo/ShapeDocument.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557.zip b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557.zip new file mode 100644 index 0000000..49a49d2 Binary files /dev/null and b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557.zip differ diff --git a/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/TileRepeater.Package.nuspec b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/TileRepeater.Package.nuspec new file mode 100644 index 0000000..66df975 --- /dev/null +++ b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/TileRepeater.Package.nuspec @@ -0,0 +1,13 @@ + + + + TileRepeater.Package + 1.234.121557 + TileRepeater.Package + Package Description + + + + + + \ No newline at end of file diff --git a/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/[Content_Types].xml b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/[Content_Types].xml new file mode 100644 index 0000000..f30e5d8 --- /dev/null +++ b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/[Content_Types].xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/_rels/.rels b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/_rels/.rels new file mode 100644 index 0000000..69b24e5 --- /dev/null +++ b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/_rels/.rels @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/Server/TileRepeater.ClientServerProtocol.dll b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/Server/TileRepeater.ClientServerProtocol.dll new file mode 100644 index 0000000..6efc124 Binary files /dev/null and b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/Server/TileRepeater.ClientServerProtocol.dll differ diff --git a/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/Server/TileRepeater.Designer.Server.dll b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/Server/TileRepeater.Designer.Server.dll new file mode 100644 index 0000000..a3e46b3 Binary files /dev/null and b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/Server/TileRepeater.Designer.Server.dll differ diff --git a/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/TileRepeater.ClientServerProtocol.dll b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/TileRepeater.ClientServerProtocol.dll new file mode 100644 index 0000000..7c57c98 Binary files /dev/null and b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/TileRepeater.ClientServerProtocol.dll differ diff --git a/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/TileRepeater.Designer.Client.dll b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/TileRepeater.Designer.Client.dll new file mode 100644 index 0000000..19780f8 Binary files /dev/null and b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/Design/WinForms/TileRepeater.Designer.Client.dll differ diff --git a/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/TileRepeater.Controls.dll b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/TileRepeater.Controls.dll new file mode 100644 index 0000000..d20e734 Binary files /dev/null and b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/lib/net6.0/TileRepeater.Controls.dll differ diff --git a/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/package/services/metadata/core-properties/871454ca44c34012b8ddbcedc07e7c8e.psmdcp b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/package/services/metadata/core-properties/871454ca44c34012b8ddbcedc07e7c8e.psmdcp new file mode 100644 index 0000000..26f5db8 --- /dev/null +++ b/Samples/TypeEditor/Dotnet/TileRepeater_Medium/NuGet/BuildOut/TileRepeater.Package.1.234.121557/package/services/metadata/core-properties/871454ca44c34012b8ddbcedc07e7c8e.psmdcp @@ -0,0 +1,9 @@ + + + TileRepeater.Package + Package Description + TileRepeater.Package + 1.234.121557 + + NuGet.Build.Tasks.Pack, Version=6.6.0.57, Culture=neutral, PublicKeyToken=31bf3856ad364e35;Microsoft Windows NT 10.0.22621.0;.NET Framework 4.7.2 + \ No newline at end of file