Custom Controls Design Time Support Part 11: Designer Verbs

This post is simple and neat, the goal now is to add some designer verbs, so lets get straight.

What is a Designer Verb?

A designer can use the DesignerVerb class to add menu commands to the shortcut menu for the component it provides designer support for in design mode. Designer verbs associate a menu item with an event handler. Designer verbs are provided to the design-time environment by the Verbs property of the IDesigner interface. [more]

How to add Designer Verbs?v

I will use the sample I introduced in the last post, I have a TestUserControl that has a red dotted border , and a ShowBorder property to show or hide that border, now I will add on the UserControlDesigner class some verbs ( menu items ) to change the color of the Border ( in case if we had a from with red background so the border will be lost ).

[code:c#]

public override DesignerVerbCollection Verbs
{
    get
    {
        return new DesignerVerbCollection(
            new DesignerVerb[]
            {
                new DesignerVerb("Make Border Red",new EventHandler(MakeRedBorder)),
                new DesignerVerb("Make Border Green",new EventHandler(MakeGreenBorder)),
                new DesignerVerb("Make Border Blue",new EventHandler(MakeBlueBorder)),
            }
            );
    }
}

private void MakeRedBorder(object sender, EventArgs e)
{
    _pen.Color = Color.Red;
    Control.Refresh();
}
private void MakeGreenBorder(object sender, EventArgs e)
{
    _pen.Color = Color.Green;
    Control.Refresh();
}
private void MakeBlueBorder(object sender, EventArgs e)
{
    _pen.Color = Color.Blue;
    Control.Refresh();
}

[/code]

v2Where will they appear?

The verbs will have to places to appear now.

1. In the context menu on the control, on right click.

2. In the Property window when the control is selected, but make sure that the commands is enabled in the property window.

More from msdn: http://msdn2.microsoft.com/en-us/library/xfk1zfw9.aspx

Custom Controls Design Time Support Part 10: Filtering Control Members

As I  mentioned in introducing the Designer post that one of the designer powers is filtering the control members, filtering here means removing or adding members (Properties, Events or Attributes) to the DesignTime Environment in the PropertyWindow.

The IDesignerFilter interface has the methods that enable filtering the members that appear in the Design time PropertyWindow. 

The IDesignerFilter interface has 6 methods for filtering.

Why for each Properties , Events and Attributes there is a Pre and Post Filter method?
[more]
From msdn.

If you want to add an attribute or attributes, implement an override of the PreFilterAttributes method that adds the new System..::.Attribute to the IDictionary passed to the method. The keys in the dictionary are the type IDs of the attributes. To change or remove an attribute or attributes, implement an override of the PostFilterAttributes method.

If you want to add an event or events, implement an override of the PreFilterEvents method that adds the new EventDescriptor to the IDictionary passed to the method. The keys in the dictionary are the names of the events. To change or remove an event or events, implement an override of the PostFilterEvents method.

If you want to add a property or properties, implement an override of the PreFilterProperties method that adds the new PropertyDescriptor to the IDictionary passed to the method. The keys in the dictionary are the names of the properties. To change or remove a property or properties, implement an override of the PostFilterProperties method.

Ok now we know why and when we should use these methods, what about a sample. The sample I will introduce here is a designer for a simple UserControl that adds Design Time property to whether show a dotted border or not (to not get lost when not selected) , and will remove some properties too.

[code:c#]

public class UserControlDesigner : ControlDesigner
{
  Pen _pen;
  Control _control;
  bool _showBorder;

  public bool ShowBorder //Property to be added to the Property grid
  {
    get { return _showBorder; }
    set { _showBorder = value; }
  }

  public override void Initialize(System.ComponentModel.IComponent component)
  {
    base.Initialize(component);
    _control = this.Control;v _showBorder = true;
    _pen = new Pen(Color.Red);
    _pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
  }

  protected override void OnPaintAdornments(System.Windows.Forms.PaintEventArgs pe)
  {
    base.OnPaintAdornments(pe);

    if (_showBorder && _control != null)
    {
      pe.Graphics.DrawRectangle(_pen, new Rectangle(0, 0, _control.Width – 1, _control.Height – 1));
    }
  }
}

[/code]

The Above code is for a designer that adds a red dotted border to a control( useful when used with an empty usercontrol ). Now I will add the ShowBorder property to the set of properties associated with the TestUserControl.

[code:c#]

protected override void PreFilterProperties(IDictionary properties)
{
base.PreFilterProperties(properties);

//adding the property here
properties.Add("ShowBorder", TypeDescriptor.CreateProperty(typeof(UserControlDesigner), "ShowBorder", typeof(bool), new DescriptionAttribute("This property only appears in design time."), DesignOnlyAttribute.Yes));
}

[/code]

Just note that I added to the created property some attributes to give it too some design time support, AND don't forget to add the DesignOnlyAttribute.Yes to the attributes array of that property so that it doesn't be serialized and you find a compile time error trying to set that property.

[code:c#]

protected override void PostFilterProperties(IDictionary properties)
{
  properties.Remove("RightToLeft");

  base.PostFilterProperties(properties);
}

[/code]

And here I removed the RightToLeft property. Now all whats left is to associate this designer with the TestUserControl.

[code:c#]

[Designer(typeof(UserControlDesigner))]

public partial class TestUserControl : UserControl

[/code]

Msdn Note:

"When a class extends a designer that implements IDesignerFilter, each PostMethodName method should call the corresponding PostMethodName method of the base class after changing its own attributes, and each PreMethodName method should call the corresponding PreMethodName method of the base class before changing its own attributes."

More on this topic :

Metadata Filtering

Thats all see you next part with Verbs

UserControlDesigner.cs (1.93 kb)

Custom Controls Design Time Support Part 9: Introducing the Designer

What is a Designer?

Designers are objects that have the ability to modify a component’s design time behavior on a
design surface. A designer can display a component’s user interface as well as allow property
changes to the component. It can also provide other services and perform additional processing
specific to the component it is associated with. [more]

What is a Designer Capable of?

  1. Filtering Properties in a control.
  2. Add commands in the control context menu during design time.
  3. Adding a smart tag to the control.
  4. much more…

Designer Hierarchy

Desginer Hierarchy

Any .Net designer will have to implement the System.ComponentModel.Design.IDesigner interface, two other Interfaces that extend the IDesigner are the IRootDesigner and the ITreeDesigner.

IRootDesginer : It indicates the root designer. A root designer is simply
a top-level designer for other designers.

Ofcourse windows controls are totally different than web controls, as the first ones paints pixels and the other renders HTML, so the designers too are different. There are 3 main Root Designers, one for Web designer System.Web.UI.Design.WebFormsRootDesigner , one for a Windows designer System.Windows.Forms.Design.DocumentDesigner which is the base for Windows Forms and User Controls, and the last one appears when building other controls System.Windows.Forms.Design.ComponentDocumentDesigner.

ITreeDesigner : Provides support for building a set of related custom designers.

No one will ever need to implement this interface for your Designers, all designers can use the base System.ComponentModel.Design.ComponentDesigner which is the base Designer for all web and all windows designers.

The HtmlControlDesigner is the most suitable base class for any web control designer.

The System.Windows.Forms.Design.ControlDesigner is the most suitable base class for any windows control designer.

As always to attach a Designer to the control you need use an Attribute.

[code:c#]

[Designer(RoundButtonDesigner)]
public class RoundButton : Button

[/code]

So next parts I will show common designer features for both web and windows controls, then show web only and windows only designer features.

Custom Controls Design Time Support Part 8: Implementing UITypeEditor

In the previous post I gave a brief introduction on what is a UITypeEditor and what you can get from using it, this part I will show you how to implement one.

So here are the steps:

  1. Define a class that derives from System.Drawing.Design.UITypeEditor.
  2. Override GetEditStyle to return a supported UITypeEditorEditStyle.
  3. Override EditValue and pass any controls necessary to the IWindowsFormsEditorService.
  4. Override GetPaintValueSupported.
  5. Override PaintValue if the editor supports painting.
  6. Override IsDropDownResizable if the editor is resiazble.a


Now we will go through the steps one by one, the example I will introduce here will be another ColorEditor, I will use the ColorWheel introduced and explained in this MSDN magazine article.

The final editor that we will make will look like this

 

[more]

Step 1

[code:c#]

public class ColorWheelEditor : UITypeEditor

[/code]

Step 2

[code:c#]

public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
{
   return UITypeEditorEditStyle.DropDown;
}

[/code]

Step 3

[code:c#]

public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
{
   IWindowsFormsEditorService iwefs = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
   Color c;
   using (ColorWheelContainer cwc = new ColorWheelContainer(iwefs))
   {
     cwc.Color = (Color)value;
     iwefs.DropDownControl(cwc);
     if (cwc.Result == DialogResult.OK)
     {
       c = cwc.Color;
     }
     else
     {
       c = (Color)value;
     }
   }
  return c;
}

[/code]

Here, I need to introduce you to the IWindowsFormsEditorService .

Namespace System.Windows.Forms.Design
Assembly System.Windows.Forms
Methods 3
 
DropDownControl accepts a parameter of type control that will be shown the drop down, works when the edit style is UITypeEditorEditStyle.DropDown.
CloseDropDown when called closes the drop down, works when the edit style is UITypeEditorEditStyle.DropDown.
ShowDialog accepts a parameter of type Form, which represents the dialog that will be opened, works when the edit style is UITypeEditorEditStyle.Modal.

Step 4

[code:c#]

public override bool GetPaintValueSupported(System.ComponentModel.ITypeDescriptorContext context)
{
    return true; // we will use the picked color and fill the rectangle.
}

[/code]

Step 5

[code:c#]

public override void PaintValue(PaintValueEventArgs e)
{
   Color c = (Color)e.Value;
   e.Graphics.FillRectangle(new SolidBrush(c), e.Bounds);
}

[/code]

Step 6

[code:c#]

public override bool IsDropDownResizable
{
   get
   {
     return false;//we don't want it to be resizable
   }
}

[/code]

Now don't forget to associate the editor with the color property using the Editor attribute.

[code:c#]

[Editor(typeof(ColorWheelEditor),typeof(UITypeEditor))]
public Color ColorProperty

[/code]

In this part I showed how to implement a UITypeEditor that appears in a Drop Down. See you soon.

 

ColorWheelEditor.zip (13.99 kb)

Custom Controls Design Time Support Part 7: UITypeEditor Introduction

In the previous posts in this series I introduced how to use some basic and advanced Attributes and how to add an image for your control to appear in the toolbox and then I gave an introduction on TypeConverters and examples here and here on how to use the TypeConverters. Now I will give an introduction on how UITypeEditors used in design time.

First, what is a UITypeEditor anyway from MSDN :

The UITypeEditor class provides a base class that you can derive from and extend to implement a custom type editor for the design-time environment. Typically, your custom type editor interacts with the PropertyGrid control.

[more]

So the UITypeEditor interacts with the PropertyGrid where does it appear?? It appears in the left hand side of the property or just the value of the property in the PropertyWindow.

Any examples ?? yes there are multiple built in UITypeEditors for a lot types, one example is the FontEditor, when you edit any property of type Font you find by default a button ellipses like this .

Another example is the ColorEditor, while trying to get a screen shot for the color editor I found something interesting, in Visual Studio 2005 is different than that in Visual Studio 2008 more over in VS 2008 the color editor used with Web Controls is different than that used with windows controls.

Visual Studio 2005 Version

Visual Studio 2008 Windows Version

Visual Studio 2008 Web Version

 

Exploring the System.Drawing.Design.UITypeEditor methods.

GetEditStyle  Gets the editor style used by the EditValue method.
EditValue  Edits the value of the specified object using the editor style indicated by the GetEditStyle method.
GetPaintSupported  Indicates whether this editor supports painting a representation of an object's value.
PaintValue  Paints a representation of the value of an object using the specified PaintValueEventArgs.

The property.

 IsDropDownResizable  Gets a value indicating whether drop-down editors should be resizable by the user.

So, from the above pictures, the methods and the single property we can guess what a UITypeEditor can let us do.

  1. The editor can have one of three styles
    1. Modal -> a popup window as the FontEditor.
    2. DropDown -> a dropdown form as the VS2005 ColorEditor.
    3. None -> neither Modal nor DropDown just text as the Name property. (default Style for all Properties)
  2. You can draw anything within the small rectangle beside the property value (e.g. ColorEditor draws a solid rectangle with the chosen color beside it).
  3. When using the DropDown style you can make it resizable or not (e.g the VS2008 Windows ColorEditor is Resizable while the 2005 is not resizable).

Well thats enough for a fast introduction next part I'll give an example or two on how to implement an Editor from scratch, may be a new ColorEditor too.Smile

Custom Controls Design Time Support Part 6: Custom TypeConverter 2

The last post in the series I have showed you how to implement a custom TypeConverter and override some of the virtual methods from the base class.

In this post I'll show how to some more advanced examples on how to get the most of TypeConverters. First look at the strucure of the example.

[more] 

[code:c#]

public enum Processor
{
  TwoPointFour,
  TwoPointEight,
  ThreePointTwo,
  ThreePointSix
}

public class Computer
{
  private Processor _processorSpeed;
  public Processor ProcessorSpeed
  {
    get { return _processorSpeed; }
    set { _processorSpeed = value; }
  }

  private ComputerType _type;
  public ComputerType Type
  {
    get { return _type; }
    set { _type = value; }
  }
}

public abstract class ComputerType
{
  private int _hardDiskSize;
  public int HardDiskSize
  {
    get { return _hardDiskSize; }
    set { _hardDiskSize = value; }
  }
}

public class DesktopPC : ComputerType
{
  private bool _hasInternalUPS;
  public bool HasInternalUPS
  {
    get { return _hasInternalUPS; }
    set { _hasInternalUPS = value; }
  }
}

public class Laptop : ComputerType
{
  private bool _hasBluetooth;
  public bool HasBluetooth
  {
    get { return _hasBluetooth; }
    set { _hasBluetooth = value; }
  }
}

public class PocketPC : ComputerType
{
  private bool _canMakeCalls;
  public bool CanMakeCalls
  {
    get { return _canMakeCalls; }
    set { _canMakeCalls = value; }
  }
}

[/code]

Now to setup a test enivronment I made a new windows forms application added a Button and a PropertyGrid control, on button click.

 

[code:c#]

Computer toshiba = new Computer();
toshiba.ProcessorSpeed = Processor.TwoPointEight;
toshiba.Type = new Laptop();

propertyGrid1.SelectedObject = toshiba;

[/code]

And this is how it looks.

Now we need to enhance how that looks, first lets change how the Processor enum appears. The enum appears like that as the default behaviour of the EnumConverter which shows the enum values as is, to change that behaviour lets start by deriving from the EnumConverter and changing how it converts to and from a string.

[code:c#]

public class ProcessorConverter : EnumConverter
{
  private Type _enumType;
  private Dictionary _enumValues;

  public ProcessorConverter(Type enumType) : base(enumType)
  {
    _enumType = enumType;
    if (_enumValues == null)
    {
      _enumValues = new Dictionary();
      _enumValues.Add(Processor.TwoPointFour, "2.4 Ghz");
      _enumValues.Add(Processor.TwoPointEight, "2.8 Ghz");
      _enumValues.Add(Processor.ThreePointTwo, "3.2 Ghz");
      _enumValues.Add(Processor.ThreePointSix, "3.6 Ghz");
    }
  }

  public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object src)
  {
    if (src is string)
    {
      if (_enumValues.ContainsValue((string)src))
      {
        foreach (KeyValuePair item in _enumValues)
        {
          if(item.Value == (string)src)
            return item.Key;
        }
      }
      return null;
    }
    return base.ConvertFrom(context, culture, src);
  }
  public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object src, Type destinationType)
  {
    if (destinationType == typeof(string) && src is Processor)
    {
      return _enumValues[(Processor)src];
    }
    return base.ConvertTo(context, culture, src, destinationType);
  }
}

[/code]

As you can notice when deriving from the EnumConverter you have to implement the constructor having enumtype parameter. Now don't forget to associate the ProcessorConverter with the Processor enum.

[code:c#]

[TypeConverter(typeof(ProcessorConverter))]
public enum Processor

[/code]

And here is the result.

Now lets implement a ComputerTypeConverter, I will derive from the ExpandableObjectConverter which is already useful for listing the inner properties.

[code:c#]

public class ComputerTypeConverter : ExpandableObjectConverter

[/code]

By applying this converter as it is without implementing any custom code gives the following result. Now lets first override the CanConvert methods to allow our Converter to work with strings.

[code:c#]

public class ComputerTypeConverter : ExpandableObjectConverter
{
  public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  {
    if (sourceType == typeof(string))
    {
      return true;
    }
    return base.CanConvertFrom(context, sourceType);
  }
  public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
  {
    if (destinationType == typeof(string))
    {
      return true;
    }
    return base.CanConvertTo(context, destinationType);
  }

  public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object src)
  {
    if (src is string)
    {
      switch ((string)src)
      {
        case "Desktop PC": return new DesktopPC();
        case "Laptop": return new Laptop();
        case "Pocket PC": return new PocketPC();
        case "(None)":
        default:
          return null;
      }
    }
    return base.ConvertFrom(context, culture, src);
  }
  public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object src, Type destinationType)
  {
    if (destinationType == typeof(string))
    {
      if(src == null) return "(None)";
      if(src is DesktopPC) return "Desktop PC";
      if(src is Laptop) return "Laptop";
      if(src is PocketPC) return "Pocket PC";
    }
    return base.ConvertTo(context, culture, src, destinationType);
  }
}

[/code]

What we have done here is kind of nice, now we can write the string representing the computer type in the property grid and the TypeConverter automatically converts it for us. But the only problem here is that if the user/developer might not memorize all computer types and with the exact letter case, so we need a way to make it easy on them. Yes, its time to override the GetStandardValues method.

[code:c#]

private ComputerType[] _standardValues = new ComputerType[] { null, new DesktopPC(), new Laptop(), new PocketPC() };
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
  return true;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
  return new StandardValuesCollection(_standardValues);
}

[/code]

The final looks like this.

 

So, this is the end of TypeConverters I hope it become useful to someone someday. Next parts I will talk about UITypeEditors.

Computer.cs (5.67 kb)        

Custom Controls Design Time Support Part 5: Custom TypeConverters

In the previous post I gave an introduction on common TypeConverters and how to use them, in this post I will show you how to implement custom TypeConverters for your own custom data types.

First, lets explore the TypeConverter base type virtual methods …

Method Description
CanConvertFrom Returns a Boolean value indicating whether the converter can convert an object of the specified type to the type that this converter represents.
CanConvertTo Returns a Boolean value indicating whether the converter can convert an object to the specified type.
ConvertFrom Converts the specified value to the type represented by this converter.
ConvertFromInvariantString Converts the string representation of a value to a type that this converter represents, using the invariant culture, which is English.
ConvertFromString Converts the string representation of a value to a type that this converter represents, using the given culture.
ConvertTo Converts the given object to the specified type.
ConvertToInvariantString Converts the given object to a string, using the invariant culture.
ConvertToString Converts the given object to a string, using the specified culture.
CreateInstance Creates or recreates an object given a dictionary of property values. The dictionary contains property name-value pairs.
GetCreateInstanceSupported Returns a Boolean value indicating whether CreateInstance has been implemented.
GetProperties Returns a collection of PropertyDescriptor objects for the given object.
GetPropertiesSupported Returns a Boolean value indicating whether the given object supports properties.
GetStandardValues Returns a collection of standard values for the type that this converter represents.
GetStandardValuesExclusive Returns a Boolean value indicating whether the standard values are mutually exclusive.
GetStandardValuesSupported Returns a Boolean value indicating whether GetStandardValues is implemented.
IsValid Returns a Boolean value indicating whether the specified value is valid for the type that this converter represents.

[more] 

Now lets implement a simple one, considering a type Point3D which simply has 3 int properties representing a point in the 3D (x,y,z).

[code:c#]

public struct Point3D
{
    private int _x;
    private int _y;
    private int _z;

    public int X { get { return _x; } set { _x = value; } }
    public int Y { get { return _y; } set { _y = value; } }
    public int Z { get { return _z; } set { _z = value; } }
}

[/code]

When adding a Point3D property to our testing ButtonEx Control the Point would look like this in the PropertyWindow

[code:c#]

public class Point3DConverter : TypeConverter
{
   public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
   {
   if (sourceType == typeof(string))
      return true;
   else
      return base.CanConvertFrom(context, sourceType);
   }

   public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
   {
      if (destinationType == typeof(Point3D))
         return true;
      else
         return base.CanConvertTo(context, destinationType);
   }

   public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object src, Type destinationType)
   {
       //convert to a string
       if (destinationType == typeof(string))
      {
         Point3D p3 = (Point3D)src;
         string result = p3.X + "," + p3.Y + "," + p3.Z;
         return result;
       }
       return base.ConvertTo(context, culture, src, destinationType);
  }
   public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object src) {
  {
      //convert from a string
      if (src is string)
     {
         Point3D p3 = new Point3D();
         string[] temp = ((string)src).Split(',');
         p3.X = int.Parse(temp[0]);
         p3.Y = int.Parse(temp[1]);
         p3.Z = int.Parse(temp[2]);
        return p3;
       }
     return base.ConvertFrom(context, culture, src);
   }
}

[/code]

Now apply the Point3DConverter to the Point3D struct, so anytime the Point3D is used then the Point3DConverter is used by default as when you use the Size type it is always associated with the SizeConverter, same to FontConverter used default with Font type.

[code:c#]

[TypeConverter(typeof(Point3DConverter))]
public struct Point3D

[/code]

After applying the Converter the property would look like this:

 

This would be enough to use the Point3D smoothly in the desing time, but we can make it more fancy by overriding GetPropertiesSupported and GetProperties methods.

[code:c#]

public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
   return true;
}

public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object src, Attribute[] attributes)
{
   return TypeDescriptor.GetProperties(src, attributes);
}

[/code]

Now it will be expanded in the PropertyWindow

 

To show how the GetStandardValuesSupported and GetStandardValues work take a look on the following properties.

As you are expecting the values appearing in the drop down list are the StandardValues, accordingly the EnumConverter overrides those two methods in order to return the values of the Enum.

Next part I will show how to use the StandardValues and the rest of the methods.

Point3D.cs (2.58 kb)

Custom Controls Design Time Support Part 4: TypeConverters Introduction

 I mentioned in my Introduction post of this series the TypeConverters

Overview: For those who don't know what is a TypeConverter you can expect that it is something responsible to convert between types, there are times when you need to convert from one data type to another. Type converters are classes that describe how a particular object converts to and from other data types. For example, to display a date on the screen(console, windows form, web ..) it will need to be converted to a string representation. Vice Versa is true, if there is a string value of a date that needs to be stored for example in database. Usually casting is enough when the types are simple. But with complex types, a better technique is used.

TypeConverters are classes that define how an object converts to and from other types. Which are used during design time for sting conversion ( used by the PropertyGrid ), also in runtime in validations and conversions.

So, first lets take a look on some of the Common .NET Type Converters that are already built for us to use.

All the following classes are in the System.ComponentModel and the System.Drawing namespaces.

  1. StringConverter
  2. BooleanConverter
  3. CharConverter
  4. CollectionConverter
  5. CultureInfoConverter
  6. DateTimeConverter
  7. EnumConverter
  8. ExpandableObjectConverter
  9. GuidConverter
  10. TimeSpanConverter
  11. ColorConverter
  12. FontConverter
  13. PointConverter
  14. RectangleConverter
  15. SizeConverter

And much more. [more]

From the most Converters that I personally like is the ExpandableObjectConverter you can apply it using the following syntax.

Example:

[code:c#]

public class Info
{
int i;
string s;
public int IntProperty
{
get { return i;}
set { i = value; }
}
public int StringProperty
{
get { return s; }
set { s = value; }
}
}

public class ButtonEx : Button
{

private Info i;
[TypeConverter(typeof(ExpandableObjectConverter))]
public Info ComplexObject
{
get { return i; }
set { i = value; }
}
}

[/code]

Without the attribute, the property would look like this
After applying the converter it is like this

Next part I will implement a custom converter.

Custom Controls Design Time Support Part 3: Adding Image in the Toolbox

Attributes Applied To Description
ToolboxBitmap Controls           Allows you to specify an icon to represent a control in a container, such as the Microsoft Visual Studio Form Designer.

» ToolboxBitmapAttribute if you want to redistribute your custom control you should use this attribute to add an icon to it.

[code:c#]

[ToolboxBitmap(typeof(Button))]
public class ButtonEx : Button

[/code]

This will show the default icon of the Windows.Forms.Button control, to add a custom icon you will use one of the other two ToolboxBitmapAttribute constructors, the first one accepts a string as the full path for the image, the second one with a string and a type this one is used more frequently.

[code:c#]

[ToolboxBitmap(typeof(ButtonEx),"ButtonEx.png")]
public class ButtonEx : Button

[/code]

Note: For this to work you need to be sure of 2 things:

  1. The image name would be exactly spelled as the type (control).
  2. Right click the image and make sure its BuildAction is Embedded Resource.

Custom Controls Design Time Support Part 2: More Design Time Attributes

In my previous post I pointed out the most common used Attributes for Design Time Support. In this part I will point out some more Attributes that take advantage of the Design Time Environment.

Attributes Applied To Description
DisplayName Properties & Events Specifies the display name for a property or an event.
ParenthesizeProperty Properties  Indicates whether the name of the associated property is displayed with parentheses in the Properties window.
RefreshProperties Properties Indicates that the property grid should refresh when the associated property value changes.
NotifyParentProperty Properties Indicates that the parent property is notified when the value of the property that this attribute is applied to is modified.
ReadOnly Properties Specifies whether the property this attribute is bound to is read-only or read/write.
DesignOnly Properties

Specifies whether a property can only be set at design time.

[more] 

» DisplayNameAttribute is useful when you need the name displayed in the PropertyWindow different than property actual name.

[code:c#]

[DisplayName("Number of Clicks")]
public int NumOfClicks

[/code]

DisplayNameAttribute 

» ParenthesizePropertyAttribute when you have a special property such as Name and need it to appear between brackets this is the attribute to use.

[code:c#]

[ParenthesizePropertyName(true)]
public string SpecialProperty

[/code]

» RefreshPropertiesAttribute when you have property's value which depends on the value of another one then you will need this attribute, mark the property that when changed should refresh others with this attribute.

[code:c#]

[RefreshProperties(RefreshProperties.All)]
public string RefreshOtherProperty

[/code]

» NotifyParentPropertyAttribute Apply this one to a property if its parent property should receive notification of changes to the property's values. For example, the Size property has two nested properties: height and width. These nested properties should be marked with NotifyParentPropertyAttribute(true) so they notify the parent property to update its value and display when the property values change.

[code:c#]

[NotifyParentProperty(true)]
public string NotifyparentProperty

[/code]

» ReadOnlyAttribute When applying this attribute with parameter true then it will be unchangable in the design time, this is different than making a property without a set accessor since properties marked with ReadOnlyAttribute can be changed using code.

[code:c#]

[ReadOnly(true)]
public string ReadOnlyProperty

[/code]

» DesignOnlyAttribute from the name implies that the property marked with this attribute will only be available in the design mode.

[code:c#]

[DesignOnly(true)]
public string DesignTimeOnlyProperty

[/code]

ButtonEx.cs (3.36 kb)