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)

12 thoughts on “Custom Controls Design Time Support Part 10: Filtering Control Members”

  1. Nice article series Amr, but on the above one there is an error.

    You do a call like this: _control = this.Control;

    but Control in this context doesn’t exist. To what are you referering?

    Bye
    Andrea

  2. Hey, i was looking around for some Design Time support, and i finally found your blog.
    Pretty interesting, but i’m going mad with some things that your hints never solve.
    As an example, i bind my custom controls on IDesignerHost (i’m working on 1.1 framework), i need some custom properties to describe my component, and i need to remove propertygrid browsing of some others.
    My custom controls need to move (and need to be sized) around the container not in pixel but in a custom measure, so my grid unit is something like (12, 16).
    Whats the problem? In the propertygrid i’m not able to hide base windows.form.control properties like (standar pixel) Size or Location because if i do that (by IcustomTypeDescriptor or IDesignerFilter) the control will never be built correctly.
    So, have you got some hint about?

  3. Thank for you hint, but my problem is different.
    I got to hide property like Size and Location, and i need to show only my custom Size, Location, that because base Size and Location are in pixel, but i need to work in cells.
    let me show you a pic.
    http://img111.imageshack.us/my.php?image=sampleeh5.jpg
    This is my designer, i need to hide the normal location, and to show only my custom, i’m going crazy @_@
    any idea?

  4. Great series of articles, Amr! I’m looking forward to seeing much more on this topic.

    I tried this code for the adornments, adding a designer for a control I made that inherits from ContainerControl. The adornment works, I see the red rectangle, but now my control doesn’t act like a container. Instead of the normal squarish grab handle I get in the upper left corner of the control, it now lets me grab it like a regular, non-ContainerControl; and I can’t add child controls to it any more. The controls that were already children of the control stay there, but if I move them off (to the parent form, for example), I can’t put them back on.

    Do you have any idea why using this designer would have such a side effect, or how I could get around this?

  5. Hello Dan Vanderboom,
    I didnt try that case before, I dont know what really happens but before I try writing some code, make sure you used the ParentControlDesigner as the base designer class (not ControlDesigner) and if doesnt work let me know.
    Thanks Amr.

  6. You were right, Amr! Thank you. Since that was so easy for you, I have another that may be related.

    When I create a new UserControl, I get a new class that inherits from UserControl (of course). Any time I open MyUserControl, I get this nice designer surface that I can manipulate controls on. What is it about the UserControl class (or its designer) that Visual Studio knows to open this designer, and doesn’t for other classes? Is there some attribute I need to use? I have a control (called SuperContainer) that inherits from ContainerControl instead of UserControl, but I’d still like subclasses of SuperContainer to open in a UserControl-like designer, so it’s easy to place controls on it and reuse them as a group. Is this possible?

    My goal, ultimately, is to have a container control that will act like a container so I can add child controls to it when placed on a form, but will also act like a UserControl in the sense that I can create reusable groups of controls based on this container.

    My other option would be for SuperContainer to simply inherit from UserControl. Doing this, I do get the nice UserControl designer. And with your suggestion to make my custom designer inherit from ParentControlDesigner, it does in fact act like a container when placed on a form. The only problem is that when I try to save the changes in the form, I get an error "Object does not match target type", which pops up three times in a row. But it doesn’t give me a line number, nor does it tell me what class the error is from. It’s clearly having some kind of problem serializing/generating the control to the designer.cs file.

    The only mention I could find of this error mentioned the ISupportInitialize interface. I tried implementing this interface in my custom designer, but it didn’t make any difference. Any ideas, Amr?

  7. Hello Dan,
    glad you figured out a solution for the problem, further more I have something for you.

    What I understood from the first part of your comment was that you want to make a special control that has a special designer.

    So, the key for the answer is ( [b]IRootDesigner[/b] ) when a control designer implements this interface it then can have a standalone designer like that of System.Windows.Forms.Form and System.Windows.Forms.UserControl sure you know that the Form is just a special control, So to start, try searching for implementations and code samples on a custom Root Designer I really should have written about it in this series may be one day i will. Actually, I written one (root designer) before but it was a long time ago, but I am sure you will find a sample for that.

    Hope this helps.:D

  8. Hi Amr,

    Thanks for the tip; that’s very interesting. I have in the past wanted to create a stand-alone custom property designer (to display images, strings, etc). Does this mean that all drawing and layout, interaction with the toolbox, etc., must be handled by the custom root designer? Is this how custom DSL tools are created? It looks like FormDocumentDesigner is the designer for forms, but I don’t see (looking in Reflector) where the actual UI is defined or instantiated and added to the IDE.

  9. Hi Amr. I’ve figured out my problem. It turned out that, because my custom control is targeted for the Compact Framework, I needed to use the DeviceParentControlDesigner instead of the ParentControlDesigner as my base type. :)

Leave a Reply

Your email address will not be published. Required fields are marked *