This is a mirror of official site: http://jasper-net.blogspot.com/

'Inheriting' from an Internal WinForms Designer

| Thursday, January 27, 2011
Introduction

A designer is often the best choice to extend the behavior of an associated component in design mode. While there exist other means like TypeDescriptionProvider, ITypeDescriptorFilterService or overriding the Component.Site property; a designer remains the easiest and most concise way. As most components rely on the public framework ComponentDesigner or ControlDesigner, using a derived custom designer poses no problem.

Trouble starts when the designer is marked internal for the System.Design assembly and is non trivial to reimplement by 'borrowing' code from Microsoft. Customizing a smart tag is one the features, which is nearly impossible without using a custom designer. An example of problematic hidden designers are ToolStrip / ToolStripItem components, with their lot of interdepending internal classes enhancing our IDE experience.

I propose the simple idea of using the internal default designer, by encapsulating it in a suitable ComponentDesigner or ControlDesigner and delegating member calls to the internal designer. This article highlights some not too obvious issues involved to make it work.The demo project uses a ContextMenuStrip and a TreeView control without any added real functionality as proof of concept.

Custom Designer Skeleton

The framework ContextMenuStrip is a Control, yet it's associated ToolStripDropDownDesigner derives from the ComponentDesigner. So our custom designer will too:

internal abstract class ToolStripDropDownDesigner : ComponentDesigner
{
   protected ComponentDesigner defaultDesigner;

   public override void Initialize(IComponent component)
   {
       // internal class ToolStripDropDownDesigner : ComponentDesigner
       // Name: System.Windows.Forms.Design.ToolStripDropDownDesigner ,
       // Assembly: System.Design, Version=4.0.0.0
       Type tDesigner = Type.GetType
       ("System.Windows.Forms.Design.ToolStripDropDownDesigner, System.Design");
       defaultDesigner = (ComponentDesigner)Activator.CreateInstance
       (tDesigner, BindingFlags.Instance | BindingFlags.Public, null, null, null);

       defaultDesigner.Initialize(component);
       base.Initialize(component);
   }

   public override void InitializeNewComponent(IDictionary defaultValues)
   {
       base.InitializeNewComponent(defaultValues);
       defaultDesigner.InitializeNewComponent(defaultValues);
   }

   protected override void Dispose(bool disposing)
   {
       if (disposing)
       {
           if (defaultDesigner != null)
           {
               defaultDesigner.Dispose();
           }
       }

       base.Dispose(disposing);
   }
}


Designer Properties

A designer may expose design time only properties, adding new ones or ones that shadow existing control properties.These can be marked private, as design time environment uses Reflection to read and set values. Now our custom designer was specified as the principal designer by the DesignerAttribute and our designer instead of the default designer will be queried for properties. Does this mean we have to reimplement all designer properties on our custom designer and fidget with Reflection to delegate all calls?
Luckily inserting a single line will save us from the trouble:

public override void Initialize(IComponent component)
{
   ...
   // use Designer properties of nested designer ( do before base.Initialize ! )
   TypeDescriptor.CreateAssociation(component, defaultDesigner);

   defaultDesigner.Initialize(component);
   base.Initialize(component);
}


Quoted from MSDN:

"The CreateAssociation method creates an association between a primary and a secondary object. Once an association is created, a designer or other filtering mechanism can add properties that route to either object into the primary object's property set. When a property invocation is made against the primary object, the GetAssociation method will be called to resolve the actual object instance that is related to its type parameter."

For clarity: Any defined properties on our designer will be queried as well, we just created an additional target. BTW, CreateAssociation() was the missing piece, when I first failed at encapsulation some years ago.

IDesignerFilter Methods

ComponentDesigner inherits from IDesignerFilter interface and we must override its methods to delegate the calls to the default designer. As the methods are marked protected, we cast the designer to the interface, in order to access them:


protected IDesignerFilter designerFilter;
designerFilter = defaultDesigner;
ComponentDesigner's PreFilterAttributes() and PreFilterEvents() implementations are empty, and PostFilterProperties() only deals with the seldom case, that component inherits from IPersistComponentSettings. We won't bother overriding these methods.


Read more: Codeproject

Posted via email from Jasper-net

0 comments: