First part:
1. Start new class library for .NET Framework (not core)
2. Name it MyControls.
3. Rename Class1 to MyButton.
4. Reference System.Windows.Forms in my case it was like: C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Windows.Forms.dll
5. MyButton inherit from Button.
6. Add property like:
public string MyProperty { get; set; }
7. Create new App Windows forms for .NET Framework (not core)
8. Add new tab in toolbox
9. Drag and drop MyControls.dll --- Second part - smart tag: 10. Add attribute [Designer(typeof(MyButtonDesigner))]:
using System.ComponentModel;
using System.Windows.Forms;
namespace MyControls
{
[Designer(typeof(MyButtonDesigner))]
public class MyButton: Button
{
public string MyProperty { get; set; }
}
}
11. Add class MyButtonDesigner 12. Add reference to System.Design C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Design.dll in usings add System.Windows.Forms.
13. Add class MyButtonDesigner and inherit from ControlDesigner:
using System.ComponentModel.Design;
using System.Windows.Forms.Design;
namespace MyControls
{
public class MyButtonDesigner: ControlDesigner
{
private DesignerActionListCollection _myButtonActionLists;
public override DesignerActionListCollection ActionLists
{
get
{
if (_myButtonActionLists is null)
{
_myButtonActionLists = new DesignerActionListCollection
{
new MyButtonActionList(Component)
};
}
return _myButtonActionLists;
}
}
}
}
14. Add class MyButtonActionList, inherit from DesignerActionList:
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Reflection;
namespace MyControls
{
public class MyButtonActionList: DesignerActionList
{
private readonly MyButton _myButton;
private readonly DesignerActionUIService _designerActionUiSvc = null;
public MyButtonActionList(IComponent component) : base(component)
{
_myButton = component as MyButton;
_designerActionUiSvc =
GetService(typeof(DesignerActionUIService))
as DesignerActionUIService;
}
public string MyProperty
{
get => _myButton.MyProperty;
set
{
GetPropertyByName("MyProperty").SetValue(_myButton, value);
_designerActionUiSvc.Refresh(Component);
}
}
public override DesignerActionItemCollection GetSortedActionItems()
{
DesignerActionItemCollection items = new DesignerActionItemCollection();
items.Add(new DesignerActionHeaderItem("My Smart tag"));
items.Add(new DesignerActionPropertyItem("MyProperty", "MyProperty"));
return items;
}
private PropertyDescriptor GetPropertyByName(string propName)
{
var prop = TypeDescriptor.GetProperties(_myButton)[propName];
if (null == prop)
throw new ArgumentException(
"Matching MyProperty property not found!",
propName);
return prop;
}
}
}
Example until now download from here.
---
Third part - UIEditor:
15. In MyButton change MyProperty to:
public MyPropertyType MyProperty { get; set; }
16. Add new class MyPropertyType, decorate with Editor and TypeConverter:
using System.ComponentModel;
using System.Drawing.Design;
namespace MyControls
{
[Editor(typeof(MyButtonEditor), typeof(UITypeEditor))]
[TypeConverter(typeof(MyPropertyTypeConverter))]
public class MyPropertyType
{
public string AnotherMyProperty { get; set; }
public MyPropertyType(string test)
{
AnotherMyProperty = test;
}
}
}
17. Add one more class MyPropertyTypeConverter:
using System;
using System.ComponentModel;
using System.Globalization;
namespace MyControls
{
public class MyPropertyTypeConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return true;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return true;
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is null)
{
return string.Empty;
}
return new MyPropertyType(value.ToString());
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
return ((MyPropertyType)value)?.AnotherMyProperty;
}
}
}
18. Again add class MyButtonEditor:
using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace MyControls
{
public class MyButtonEditor: UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
=> UITypeEditorEditStyle.Modal;
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider, object value)
{
IWindowsFormsEditorService svc = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
Form myForm = new Form();
svc?.ShowDialog(myForm);
return new MyPropertyType("test");
}
}
}
Example download from here.