George Shepherd's Windows Forms FAQ
Questions and answers in this FAQ have been collected from newsgroup posts, various mailing lists and the employees of Syncfusion.

56. Design Time Tips

WinForms FAQ Home
   56.1 When a property gets modified, how do you refresh other properties of a Component/Control in the property browser?
   56.2 How to Reinitialize extender provider dependencies (the logic within CanExtend) from within the designer?
   56.3 How do you detect the designer host getting loaded for the first time in your designer?
   56.4 How do you provide "dynamic" default values?
   56.5 How to debug your design time code (like designers, typeconverters, etc.)?
   56.6 How do you make your custom data types property browser browsable when contained in a Control/Component (as a property)?
   56.7 How to make properties in your custom data type appear in a particular order in the property browser?
   56.8 How do I provide Intellisense support to my controls while coding against them and also provide Description support for the properties in the property grid?
   56.9 How do I change the Zorder of my docked windows at design time?
   56.10 How can I tell my form is being used in design time and not runtime?
   56.11 How can I persist a collection of items into code?
   56.12 How can I supply a list of values to be chosen from a dropdown list at runtime for a specific type similar to enums?
   56.13 How to serialize properties of items that do not implement IComponent in a collection?
   56.14 What is the function of TypeDescriptor?
   56.15 What is the function of TypeConverters?
   56.16 How do I make my custom dialog droppable in the design surface like a component?
   56.17 How do I prevent my Component from becoming visible in the VS.Net Toolbox?



56.1 When a property gets modified, how do you refresh other properties of a Component/Control in the property browser?


Set this attribute for the property:

RefreshProperties(RefreshProperties.Repaint)


56.2 How to Reinitialize extender provider dependencies (the logic within CanExtend) from within the designer?


Set the Browsable attribute on the new property that hides the existing one. Here is a code snippet that hides the WordWrap property in the derived TextBox.

[C#]
public class MyTextBox : TextBox
{
     [Browsable(false)]
     public new bool WordWrap
     {
          get{ return false;} //always false
          set{}
     }
}

[VB.NET]
Public Class MyTextBox
     Inherits TextBox

      _
     Public Shadows Property WordWrap() As Boolean
          Get
               Return False 'always false
          End Get
          Set 'empty
          End Set
     End Property
End Class 'MyTextBox



56.3 How do you detect the designer host getting loaded for the first time in your designer?


Subscribe to IDesignerHost.LoadComplete event. Make any changes to the designerhost (like adding/deleting component, etc.) only after this event, or else the design document will not be made "dirty". (IdesignerSerializationManager.SerializationComplete will tell you when Code is deserialized into design time components).


56.4 How do you provide "dynamic" default values?


Provide ShouldSerialize#PropertyName# and Reset#PropertyName# along with your property.

Example:


private bool ShouldSerializeFont()
{
     return this.bFontSet;
}

///
/// Resets the property to its default value.
///
private void ResetFont()
{
     this.localFont = null;
}



56.5 How to debug your design time code (like designers, typeconverters, etc.)?


You need to use a second instance of VS.NET to debug the one that's running the code.

Put your control on a from in VS.NET Start a 2nd Vs.Net Choose the Debug menu >> Processes ... Double click "devenv.exe" and choose "Common Language Runtime" as the types of debugging Open your code file, set your breakpoint, and you're debugging.

Posted by Shawn Burke of MSFT in microsoft.public.dotnet.framework.windowsforms.


56.6 How do you make your custom data types property browser browsable when contained in a Control/Component (as a property)?


// Your custom data type
public class MySize
{
     ...
     public int Width{get{...}set{...}}
     public int Height{get{...}set{...}}
}

public class MySizeConverter:
     ExpandableObjectConverter
{
     public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
     {
          return true;
     }

     public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
     {
          MySize size = new MySize();
          size.Width = (int)propertyValues[(object)"Width"];
          size.Height = (int)propertyValues[(object)"Height"];
          return (object)size;
     }


56.7 How to make properties in your custom data type appear in a particular order in the property browser?


For example, in the above class (MySize) if you want to your properties "Width" and "Height" to appear in that order, you should provide this override:

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

public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
     System.ComponentModel.PropertyDescriptorCollection propertyDescriptorCollection;
     string[] propNames;
     propertyDescriptorCollection =
TypeDescriptor.GetProperties(typeof(System.Drawing.Size),attributes);
     propNames = (string[])new System.String[2];
     propNames[0] = @"Width";
     propNames[1] = @"Height";
     return propertyDescriptorCollection0.Sort(propNames);
} // end of method GetProperties


56.8 How do I provide Intellisense support to my controls while coding against them and also provide Description support for the properties in the property grid?


You can provide Intellisense support to your type and it's members by providing xml comments in code as follows:


     ///
     /// Summary description for Form3.
     ///
     public class Form3 : System.Windows.Forms.Form
     {
          ///
          /// Clean up any resources being used.
          ///
          protected override void Dispose( bool disposing )
          {
          }
          ///
          /// Summary of my property
          ///
          public bool MyProperty
          {
               get{..}
               set{..}
          }          
     }


Search for the "Tags for Documentation Comments" topic in MSDN for all the available documentation tags.

Then in your project, go to the Project Properties dialog, to the Configuration Properties/Build tab and specify a file for the XML Documentation File property. This will generate a file by that name when you compile your assembly. Place this xml file beside your dll. This will provide Intellisense support for your types in that assembly.

To provide Description support for your properties in the property grid in the designer, add the DescriptionAttribute attribute to your properties in code.


56.9 How do I change the Zorder of my docked windows at design time?


You can use the menu selection Format|Move To Front or Format|Send To Back to change the Zorder and update your docking.


56.10 How can I tell my form is being used in design time and not runtime?


Check the the property Form.DesignMode.

But, note that when in Visual Inheritance mode (designing a derived form), your Control's DesignMode property will be true when the base form's constructor gets executed in the design-time.

To workaround this, you could check if the app in which your control is running is not devenv.exe, as follows:


string exePath = Application.ExecutablePath;
exePath = exePath.ToLower();
if(Application.ExecutablePath.ToLower().IndexOf("devenv.exe") > -1)
{
     // Then you are running in vs.net.
}                    



56.11 How can I persist a collection of items into code?


The CollectionEditor allows adding and removing items from a collection at design time. If the items in this collection implement IComponent or if they are derived from Component, the items in your collection can be persisted in code.

Download collectioneditorsample.zip for a complete sample project.

Here are some steps you should follow:
1) Make sure your item is derived from Component or implements Icomponent.
For example:


     public class SomeItem : Component
     {
          private string label = "";

          public SomeItem()
          {
          }

          public SomeItem(string label)
          {
               this.label = label;
          }

          public string Label
          {
               get
               {
                    return label;
               }
               set
               {
                    label = value;
               }
          }

          public override string ToString()
          {
               return String.Format("SomeItem: ( Label = '{0}' )", label);
          }
     }


2) Next implement your Collection. You have to implement the Ilist interface. The CollectionEditor will determine the type of instances to be added to your collection using reflection inspecting the return type of the Item property (Indexer).


          [
          Description("The set of properties to be edited with CollectionEditor."),
          DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
          Editor(typeof(System.ComponentModel.Design.CollectionEditor),
               typeof(System.Drawing.Design.UITypeEditor))
          ]
          public SomeItemCollection SomeItems
          {
               get
               {
                    if (someItemCollection == null)
                    {
                         someItemCollection = CreateItemCollection();
                    }
                    return someItemCollection;
               }
          }

          protected SomeItemCollection CreateItemCollection()
          {
               return new SomeItemCollection(this);
          }

          public class SomeItemCollection : IList
          {
               // Fields
               private SomeItemDisplayer owner;
               public event EventHandler Changed;

               // Constructors
               public SomeItemCollection(SomeItemDisplayer owner)
               {
                    this.owner = owner;
               }

               internal ArrayList InnerArray
               {
                    get
                    {
                         return owner.someItems;
                    }
               }
               
               public void OnChanged()
               {
                    if (this.Changed != null)
                         this.Changed(this, EventArgs.Empty);
               }

               ///
               /// The CollectionEditor will determine the type of objects to be created by
               /// looking at the property type of the following method. CollectionEditor
               /// internally uses reflection to get the PropertyInfo for the "Item" property.
               /// This method must be public.
               ///
               public SomeItem this[int index]
               {
                    set
                    {
                         if (value == null)
                              throw new ArgumentNullException("value");

                         if (index < 0 || index >= this.InnerArray.Count)
                              throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}", "index", index.ToString()));

                         this.InnerArray[index] = value;
                         OnChanged();
                    }
                    get
                    {
                         if (index < 0 || index >= this.InnerArray.Count)
                              throw new ArgumentOutOfRangeException("Invalid Argument {0}: {1}", "index", index.ToString());
                         return (SomeItem) this.InnerArray[index];
                    }
               }

               public void AddRange(object[] items)
               {
                    InnerArray.AddRange(items);
                    OnChanged();
               }

               ///
               /// This implementation for the Item property for the IList interface. It's
               /// property type is object.
               ///
               object IList.this[int index]
               {
                    set
                    {
                         // forward call to public indexer
                         this[index] = (SomeItem) value;
                    }
                    get
                    {
                         // forward call to public indexer
                         return this[index];
                    }
               }

               public /*IEnumerable*/ IEnumerator GetEnumerator()
               {
                    return InnerArray.GetEnumerator();
               }


               public /*ICollection*/ int Count
               {
                    get
                    {
                         return InnerArray.Count;
                    }
               }

               public /*IList*/ void RemoveAt(int index)
               {
                    if (index < 0 || index >= this.InnerArray.Count)
                         throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}", "index", index.ToString()));

                    this.InnerArray.RemoveAt(index);
                    OnChanged();
               }


               public /*IList*/ void Remove(object value)
               {
                    int n = this.InnerArray.IndexOf(value,0);
                    if (n != -1)
                         this.RemoveAt(n);
               }


               public /*IList*/ void Insert(int index, object item)
               {
                    if (item == null)
                         throw new ArgumentNullException("item");
                    if (index < 0 || index > this.InnerArray.Count)
                         throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}","index", index.ToString()));

                    this.InnerArray.Insert(index,item);
                    OnChanged();
               }


               public /*IList*/ int IndexOf(object value)
               {
                    if (value == null)
                         throw new ArgumentNullException(String.Format("Invalid Argument {0}: {1}","value", "null"));
                    return this.InnerArray.IndexOf(value,0);
               }


               public /*IList*/ bool IsReadOnly
               {
                    get
                    {
                         return false;
                    }
               }

               public /*IList*/ void Clear()
               {
                    InnerArray.Clear();
                    this.owner.Invalidate();
               }


               public /*IList*/ bool Contains(object value)
               {
                    return this.IndexOf(value) != -1;
               }

               void System.Collections.ICollection.CopyTo(Array dest, int index)
               {
                    int count = this.InnerArray.Count;
                    for (int n1 = 0; n1 < count; n1++)
                         dest.SetValue(this.InnerArray[n1], (n1 + index));
               }


               int System.Collections.IList.Add(object item)
               {
                    int n = this.InnerArray.Add(item);
                    OnChanged();
                    return n;
               }


               bool System.Collections.IList.IsFixedSize
               {
                    get
                    {
                         return false;
                    }
               }


               bool System.Collections.ICollection.IsSynchronized
               {
                    get
                    {
                         return false;
                    }
               }


               object System.Collections.ICollection.SyncRoot
               {
                    get
                    {
                         return this;
                    }
               }

          }


3) Reference this collection in your control or component that should be designable. You need to supply a DesignerSerializationVisibility and an Editor attribute:


          private SomeItemCollection someItemCollection = null;
          ArrayList someItems = new ArrayList();
          
          [
          Description("The set of properties to be edited with CollectionEditor."),
          DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
          Editor(typeof(System.ComponentModel.Design.CollectionEditor),
               typeof(System.Drawing.Design.UITypeEditor))
          ]
          public SomeItemCollection SomeItems
          {
               get
               {
                    if (someItemCollection == null)
                    {
                         someItemCollection = CreateItemCollection();
                    }
                    return someItemCollection;
               }
          }

          protected SomeItemCollection CreateItemCollection()
          {
               return new SomeItemCollection(this);
          }




56.12 How can I supply a list of values to be chosen from a dropdown list at runtime for a specific type similar to enums?


You have to implement a TypeConverter for your class and override the GetStandardValues and GetStandardValuesSupported method. In your override of GetStandardValuesSupported you have to return true. In your override of GetStandardValues you should return the list of values.

Optionally you can override GetStandardValuesExclusive and allow the user to specify values that are not in the value list.

Note: The standard values collection can be initialized at runtime depending on the context of the instance object.


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

          public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
          {
               if (value is System.String)
               {
                    if (((string) value) != "")
                         return Type.GetType((string) value);
                    else
                         return null;
               }

               return base.ConvertFrom(context,culture,value);
          }