Click here to Skip to main content
Click here to Skip to main content

Getting to know IExtenderProvider

By , 2 Aug 2003
 

Note: A demo isn't provided because the IExtenderProvider is a design time tool, the runtime value is to be provided by your code which makes use of the design time enhancements.

Table of Contents

What is an IExtenderProvider component?

In my own words I would describe an IExtenderProvider component as a component which extends (or adds) its own properties to other controls or components in the VS.NET designer.  These properties are then used by the component to perform some task.  Some examples of IExtenderProviders in the framework are the ToolTip and ErrorProvider controls/components.  Just on CodeProject there are at least a couple uses for an IExtenderProvider, one adds images to menus another modifies Toolbar behavior.  I was also working on another to add images to menus, which is what eventually lead to writing this article.

Creating your own IExtenderProvider component

While I was writing another article (to be published at a later date) Nnamdi, suggested that I cover how to create your own IExtenderProvider component.  This discussion was going to be a part of that article, but instead it has grown and is now its own article after I found that there wasn't one dedicated to IExtenderProvider on CodeProject.

The basics

The IExtenderProvider interface only has one method, bool CanExtend(object), this method tells the designer whether the extended properties should be displayed for the given object.  Notice that this interface has no information about said properties, the designer gets the properties by looking for the ProvideProperty attribute on the component implementing IExtenderProvider.  This method should return true to have the designer use reflection to begin looking for the extended properties. 

The ProvideProperty attribute specifies the property name as well as the type of objects it can be applied to, this just checks whether the type is a derivative of the control selected.  If you wanted a property to appear on all Checkboxes, RadioButtons, and Buttons you would specify typeof(ButtonBase) as the type.

Once you have the attribute added you need to write two public methods, these methods act as the Get/Set parts of a regular property and are named Get<Name> and Set<Name>.  The Get<Name> method takes in a type of the kind the ProvideProperty attribute defines and returns the data type of the property.  The Set<Name> method takes two arguments, the first is of the type of specified in the ProvideProperty attribute, the second is of the same data type as the Get<Name> returns, this is the value the user wishes to set the property to.

Because the extended properties are applied to all controls of a specific type chances are that you will have to deal with the property being used on multiple instances of the controls.  This calls for a Hashtable being used to maintain the association between the specific instances and the extended property or properties on each instance.  I prefer to use a nested class with public members to store the property values, this enables me to only have one Hashtable in memory.  It also makes it easier to add or remove properties from the extender while I create it.

Another interesting point is that you can still apply some of your favorite design time attributes to the extended properties, simply apply that attribute to the Get<Name> method and the designer should pull it out.  Unfortunately the "standard" code to provide a drop-down to let you select an image from an image list doesn't work with an extended property.  You could still rewrite the editor so that it was aware of the proper ImageList control.

A code sample here would probably help out a lot since it can get confusing to talk about this abstractly.  In this sample the IExtenderProvider component will provide two properties: one will be applied to only Panels and hooks the Paint event to draw a custom string in the middle; the second will be applied to all types of Buttons and will simply provide an integer >= -1, suitable for another ImageIndex type of control, though there will be no effect in doing so.

It is important to note, that while our IExtenderProvider is adding properties to objects of type Panel and ButtonBase, the provider itself can be of any type.  The most common are provided as Components so they can sit in the component tray of the Windows Forms designer.

public class MyExtender : Component, IExtenderProvider
{
    private Hashtable properties;
    
    public MyExtender()
    {
        props = new Hashtable();
    }
    
    public MyExtender(IContainer parent) : this()
    {
        parent.Add(this);
    }
    
    bool IExtenderProvider.CanExtend(object o)
    {
        if( o is Panel || o is ButtonBase )
            return true;
        else
            return false;
    }

    private Properties EnsurePropertiesExists(object key)
    {
        Properties p = (Properties) properties[key];

        if( p == null )
        {
            p = new Properties();

            properties[key] = p;
        }

        return p;
    }
    
    private class Properties
    {
        public string MyString;
        public int MyInt;

        public Properties()
        {
            MyString = System.Environment.Version.ToString();
            MyInt = -1;
        }
    }
}

First things first, this simple component does three things.

  1. It inherits from Component and implements IExtenderProvider.
  2. Defines a helper class to contain the values for the two properties we will be adding.
  3. Create a private method to ensure the helper class gets added to the Hashtable.

This is the basic skeleton you will probably use in your own extenders, this leaves three things for you to do to create your own extender and we will do that now.

Adding your properties

The first thing to do is to add the ProvideProperty attributes to the component

[ProvideProperty("MyString", typeof(Panel))]
[ProvideProperty("MyInt", typeof(ButtonBase))]
public class ....

These attributes specify two things, the name of the extended property and the Type representing the objects this property will get added to.  This attribute can only be specified once for a property so you need to choose the most general Type that you require.  The caveat to this is that the CanExtend method (from the IExtenderProvider interface) doesn't know what property it is returning true or false for. 

This means that if you want two properties to be provided, one to Button and CheckBox types, and the other to the RadioButton type you'll have to split the provider into two different components.  This is because the most specific way that property one can be specified is by typeof(ButtonBase), normally this isn't a problem because the CanExtend method could just return false if the object was a RadioButton.  But CanExtend doesn't know which property it is being called for, so it has to return true for a RadioButton which means that property one will inadvertently get added to RadioButtons.

Now that the designer knows to look for our properties its time to add the code for them!

Coding the properties

[Description("This string will be drawn in the center of the panel")]
[Category("Appearance")]
public string GetMyString(Panel p)
{
    return EnsurePropertiesExists(p).MyString;
}

public void SetMyString(Panel p, string value)
{
    EnsurePropertiesExists(p).MyString = value;
}

The methods are fairly simple to write, but later you'll see that you can do more complex things such as add event handlers.  Now for the MyInt property methods.

[DefaultValue(-1)]
[Category("Something")]
[Description("This is used by some code somewhere to do something")]
public int GetMyInt(ButtonBase b)
{
    return EnsurePropertiesExists(b).MyInt;
}

public void SetMyInt(ButtonBase b, int value)
{
    if( value >= -1 )
        EnsurePropertiesExists(b).MyInt = value;
    else
        throw new ArgumentOutOfRangeException("MyInt", value, 
            "MyInt must be greater than or equal to -1");
}

You will notice three things about each set of methods, I apply the attributes that would normally go on the property to the Get method, each method takes as its first argument an instance of the type specified in the ProvideProperty attribute.  The Get method returns and the Set methods second argument is an instance of the type the property should be -- for the first set string, for the second set int.

Hooking up the event handlers

There is another part missing from the code, part of the MyString property was that it was supposed to draw the string in the middle of the Panel

public void SetMyString(Panel p, string value)
{
    EnsurePropertiesExists(p).MyString = value;

    if( value != null && value.Length > 0 )
    {
        p.Paint += new PaintEventHandler(PanelPaint);
    }
    else
    {
        p.Paint -= new PaintEventHandler(PanelPaint);
    }

    p.Invalidate();
}

private void PanelPaint(object sender, PaintEventArgs e)
{
    string str = EnsurePropertiesExists(sender).MyString;

    if( str != null && str.Length > 0 )
    {
        Panel p = (Panel) sender;

        SizeF size = e.Graphics.MeasureString(str, Control.DefaultFont);

        float x = (float) (p.Width - size.Width) / 2.0f;
        float y = (float) (p.Height - size.Height) / 2.0f;

        e.Graphics.DrawString(str, Control.DefaultFont, 
            SystemBrushes.ControlText, x, y);
    }
}

The code above will either add or remove the Paint event handler depending on whether MyString is set to a non-null and non-empty string.  The second method is responsible for doing the painting, but just as a sanity check I double check to make sure it isn't called with a null or empty string.  After the string has changed the panel is then invalidated so that the new string is drawn.

More advanced property management

As well as having Get/Set methods you can also control code serialization and have a more advanced reset functionality.  Like the Get/Set methods they have special names (these methods apply to all properties, not just the extended ones), bool ShouldSerialize<name> and void Reset<name>.  The typical implementation (ie when dealing with real properties) takes no arguments, but because extended properties need an object so that it can match the property with the value these methods take the same arguments as the Get method.  Unlike the Get/Set methods these methods are only used by the designer, so they can be private methods rather than public ones.

ShouldSerialize<name> should return false if the current value is the default and return true if it is different.  Reset<name> can only called if ShouldSerialize<name> returns true.  You could also apply the DefaultValue attribute, but it doesn't fit all cases.  For instance you are allowing menu items to have different fonts used on them and you default to SystemInformation.MenuFont

Because the system's menu font can be different on each computer you cannot specify the default value in an attribute as the default isn't a constant, but you can use the ShouldSerialize<name> method to determine whether the selected font is the same as the system's menu font.  This is how Controls seem to always know the system's default font to use without you having to specify the default font yourself.

To extend our example we will have the default value of the MyString property be set to the version of runtime the application is running under.

private bool ShouldSerializeMyString(Panel p)
{
    Properties props = EnsurePropertiesExists(p);

    if( GetMyString(p) != System.Environment.Version.ToString() )
        return true;
    else
        return false;
}

private void ResetMyString(Panel p)
{
    SetMyString(p, System.Environment.Version.ToString());
}

Now if you do a build of the component and go back to your project you can see two things, first is that you can right-click on the MyString property and choose Reset to have the runtime version be set as the value.  You can also look at the generated code and see that the call to SetMyString is no longer present, change the MyString property and go back to the code and you'll find it has been re-added.

Problems to be aware of

There are two implications having a Reset or DefaultValue option brings.  First is that if your property is Reset or the same as the DefaultValue attribute then no code will be written to set that property for that object.  If no code is written for that property then your extender is not aware of that control instance, the code above demonstrates what this means.  Go ahead and Reset the MyString property again, you should see that the string drawn inside the panel changes to the current CLR version.  Now close the form editor and re-open it.  You'll notice that the string is no longer drawn in the Panel but that the MyString property is set to the CLR version.

Why is this?  If we follow the code execution we can see why.  In the Form's InitializeComponent method you'll notice that the call to this.extender.SetMyString has been eliminated.  This was because the value of MyString is at its default so the designer doesn't think the code is needed.  What the designer doesn't know does hurt us!  The call to SetMyString is what hooks up the Paint event handler and also makes the extender aware of that Panel instance.  Because the extender isn't aware of the panel and the Paint event handler isn't hooked up, the string never gets drawn in the Panel.

There are some ways around this, one is to not allow the MyString property to have a default value or be reset.  This ensures that your SetMyString method is always called, so your extender is always aware of the Panel.  Another thing you can do is to provide another property that can't be reset and doesn't have a default value, this allows your real extended property to Reset, but keeps the extender aware of the Panel.

I will also modify the code to use this second technique, with a slight twist.  ShouldSerialize<name> will always return true and its value will always be true (in other words the Set<name> method doesn't modify a property value).  Its definitely a hack, but a good hack because it should always work.

We start by adding a new property, I'll call it HookToPanel.  This property will always return true, and we will pull the Paint event logic out of SetMyString and put it into this property's Set method. 

[ProvideProperty("MyString", typeof(Panel))]
[ProvideProperty("HookupToPanel", typeof(Panel))]

....

[Category("Design")]
[DefaultValue(false)]
public bool GetHookupToPanel(Panel p)
{
    EnsurePropertiesExists(p);

    // This shouldn't get called at run time,
    // but in case it does don't do anything
    if( DesignMode )
    {
        p.Paint -= new PaintEventHandler(PanelPaint);
        p.Paint += new PaintEventHandler(PanelPaint);

        p.Invalidate();
    }

    return true;
}

public void SetHookupToPanel(Panel p, bool value)
{
    Properties prop = EnsurePropertiesExists(p);
    
    p.Paint -= new PaintEventHandler(PanelPaint);
    p.Paint += new PaintEventHandler(PanelPaint);

    p.Invalidate();
}

If you recompile the extender then reload the Form you might not see any changes, but if you look at the code you'll see that a new line was added. 

this.extender.SetHookupToPanel(this.panel1, true);

This line is what will set up the Paint event handler, as well as ensure that the handler doesn't get hooked up multiple times.

Yet another way

Rather than hook up the Paint event handler in the SetHookupToPanel method we can defer it until the end of the InitializeComponent method by implementing another interface, ISupportInitialize.  When a control or component implements this interface the designer inserts two method calls into the InitializeComponent method of the designer.  The first, appropriately called BeginInit, is called before any of the properties are set on a control.  The second, called EndInit, is called just before the end of the InitializeComponent method.  We can take advantage of the fact that all properties, including extended properties, are set by this point to hook up our event handlers to the Paint method.

Begin by changing the SetHookupToPanel method, all it is responsible for is ensuring that our extender is aware of the Panel (by having a Properties class instance in the Hashtable, keyed to that Panel).

public void SetHookupToPanel(Panel p, bool value)
{
    EnsurePropertiesExists(p);
}

Next implement the ISupportInitialize interface (don't forget to add it to the class declaration)

void ISupportInitialize.BeginInit()
{
}

void ISupportInitialize.EndInit()
{
    if( !DesignMode )
    {
        foreach(DictionaryEntry de in properties)
        {
            Panel p = de.Key as Panel;

            if( p != null )
            {
               p.Paint += new PaintEventHandler(PanelPaint);
            }
        }
    }
}

The EndInit method enumerates through each key/value pair in the Hashtable, if the key is a Panel then we can proceed to hook up Paint event.  Notice that I left in the event hookup in the GetHookupToPanel method, this is done so that the designer reflects our changes when the MyString property changes.  Similarly, the EndInit method doesn't hook up to the Paint event unless it isn't in DesignMode.

Conclusion

In this article you saw how to create a basic IExtenderProvider component.  You then learned some techniques to allow your extended properties to better mimic real properties and how you can combine your component with ISupportInitialize to cut down on messy code.

IExtenderProvider components can enhance the design time experience while also providing a nice benefit at runtime.  With a little work and a lot of knowledge you can create wonderful drag and drop components that can greatly enhance the runtime behavior of some controls.

Article History

  • August 3rd, 2003
    • Initial posting

License

This article, along with any associated source code and files, is licensed under The BSD License

About the Author

James T. Johnson
Product Manager ComponentOne
United States United States
Member
James has been programming in C/C++ since 1998, and grew fond of databases in 1999. His latest interest has been in C# and .NET where he has been having fun writing code starting when .NET v1.0 was in its first beta.
 
He is currently employed by ComponentOne, a Division of GrapeCity, as a Product Manager for ActiveReports.
 
Code contained in articles where he is the sole author is licensed via the new BSD license.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5membermanoj kumar choubey17 Apr '12 - 18:31 
Nice
GeneralThanks alot !memberMazen el Senih13 Mar '12 - 8:52 
This was very useful and I have been looking for this for 8 months . Roll eyes | :rolleyes:
I used to search for something like property injection !?
Got my 5 and keep up the cool things ! Wink | ;)
GeneralMy vote of 5memberBigdeak1 Jul '10 - 23:59 
Thats very helpfull, thank you very much.
Another new thing i have learned Smile | :)
 
I wan't to create a "GrayedTextHelp" for Textboxes, so i can show help textes on Textbox or Richtextbox. With a component like a ToolTip it is a nice feature. With the help of this article, it will be very easy for me to create this component.
GeneralHelp Me Please in IExtenderProvider Properties SerializationmemberMathiyazhagan12 Jun '09 - 2:04 
Hi James,
First up all thanks for sharing great article in CP;It helped me a lot in my application. I have created a IExtenderProvider for Controls that Implements an Interface.using Extender , I am Iterating the collection of controls and validate it.It works fine.but, some time while changing some designs,or when designer refreshed (while removing some events from code), the properties that provided by Extender (Validation Order and Group) are getting lost from designer and the control is not added into Extender collection.so, The validation done by application collapsed (and, of course, application itself ).I have ShouldSerialize and Reset Methods in IExtenderProvider too.but nothing helped me.Please guide me how make all controls visible to extender at all times.
 
Thanks Again,
Mathi
QuestionGood Article - but I have a question.memberGandalf730 Jan '09 - 17:20 
I have a great idea for an extender, but it has to modify a couple of the protected overrides on a control. Is it possible and if so, How?
 
Dean
GeneralNice ArticlememberMike Hankey15 Mar '07 - 13:35 
Thanks...
 
Found it very helpful.
 
Mike
 
If you keep doing what you've been doing you'll keep getting what you've been getting!

QuestionAccessin programmatically the extended propertiesmemberTheCubanP1 Dec '06 - 7:14 
I create a provider for Controls control.. (all).
I set 4 different categories of Level and each one has 5 int choices. 1,2,3,4,5.
 
Ok. t works ok. I can see my 4 propertes in the Property window and i can change from 1 to 5.
 
My question now is...
 
I tried to access the so called Prperty by... this.myButton.DeleteLevel....
First of all, it doesn't show the Property DeleteLevel when i press . (dot) in the intelisense... so i have no control to change programatically this value.
 
All i want is, depends on the user Security Level (a range from 1 to 5) to be able to disable or enable this controls if the level comparison between the user and object are lower or higher.
 
Does it make sense so far?
 
is there a way i can programmatically "change", "access" the values of this Extended Properties for each control??
 
Kind Regards
 
Jose
AnswerRe: Accessin programmatically the extended propertiesmemberJames T. Johnson1 Dec '06 - 11:42 
An IExtenderProvider is merely a design-time convenience; the property being added by the IExtenderProvider won't actually exist in the class definition.
 
All is not lost though! Remember that the designer is just writing code to set the properties, so we can write the same code that the designer is writing.
 
Assuming your IExtenderProvider component is named myExtenderProvider and DeleteLevel is the property you have 'added' to the button class the following code will read and write the property value.
 
// Get the current DeleteLevel on 'myButton'
int delLevel = myExtenderProvider.GetDeleteLevel( myButton );
 
// Set a new DeleteLevel on 'myButton'
myExtenderProvider.SetDeleteLevel( myButton, newDeleteLevel );
 
Hope that helps,
 
James

GeneralGood articlememberJim Carnicelli21 Sep '06 - 9:36 
Very well crafted. Explanations are clear, code is appropriate, and gotchas and things to watch for are spelled out well. This is the best resource I've found on IExtenderProvider I've found on the web so far. Thanks.
 
- Jim

QuestionHow do I use a class property?memberBig he4 Sep '06 - 20:52 
For example, if "MyString" is a class:
class MyString
{
private string1;
private string2;
 
public string String1
{
get { return string1; }
set { string1 = value; }
}
public string String2
{
get { return string2; }
set { string2 = value; }
}
}
How can I notify the "p.Paint" event with "PanelPaint" when string1 or string2 is changed? Should I pass a reference of MyExtender to every MyString?
tks
AnswerRe: How do I use a class property?memberJames T. Johnson1 Dec '06 - 11:47 
This reply is way late; but someone else may want the answer.
 
The problem is that the MyString class doesn't expose any way for other objects to be notified if a value is changed. The solution is simple; add String1Changed and String2Changed events to the class and fire those events when the string is changed. At that point in time any interested objects can do what they need to do.
 
Whether that means the form is listening and tells the panel to repaint, or the extender is listening and forces a repaint...some object somewhere should listen to those events to tell the panel when it should update.
 
HTH,
 
James

Questionproviding a component propertymemberefabiano27 Jul '06 - 11:05 
Hi,
I built a extender provider with a property that is a component. Every thing works fine except that the component isn´t serialized. Could you help me?
tks
QuestionHow to extend a property of a control ?memberbeatles169214 Jul '06 - 20:09 
Hi!
Great article! I didn't know anything about IExtenderProvider before reading this article and by reading this article I can work with it.Thank you.
But I have a problem and I wonder if I can solve it using IExtenderProvider.
 
I'm working with a mulicolumn combobox which has a grid property.I want to format the grid after setting its datasource and I have my formatstyle saved on a xml file and there's a formatter that gets a grid and it's xml document to format my grid.
Until now every time the datasource is changed there should be a line of code to call the formatter and do the job.After reading this article I was thinking that I could use an extender provider and set my format style at design time and let the extender to the job.
But there's a little problem! that the grid is a property of the combobox control and It's browsable but I can't see my provider properties when I expand the property in design mode.So here is the question : Is there anyway that we can extend a property of a control using IExtenderProvider?
 
Thank you again.
Nima H

AnswerRe: How to extend a property of a control ?memberbeatles169214 Jul '06 - 22:24 
Hi!
I found a solution myself Smile | :)
First, my provider will provide extended properties for both the grid control and combobox control.
Second, when fomrmatting the extended control I will check the control type if it's a grid I'll format it and if it's a combobox I'will format it's grid property.
Anyway I wonder if there's another way to do this.
Thanks
Nima H
QuestionWhy use hack?membertwesterd11 Jun '06 - 6:50 
[Quote]
I will also modify the code to use this second technique, with a slight twist. ShouldSerialize will always return true and its value will always be true (in other words the Set method doesn't modify a property value). Its definitely a hack, but a good hack because it should always work.
[/Quote]
 
This is a BAD hack, you implemented the ISupportInitialize interface but seem to miss what it is for.
 
[Quote]
... You can also look at the generated code and see that the call to SetMyString is no longer present, change the MyString property and go back to the code and you'll find it has been re-added.
[/Quote]
 
I'm not trying to be rude, but the Default attribute is for helping the user visually see in the properties designer which values have been changed from their defaults, not to actually assign the default values. for example,
 
[default((int)5)]
public int Size {
get {return size;}
set {size = value;}
}
 
The default attribute tells the designer NOT to use a bold font if the size is set to a value of 5, it does not assign the value of 5 when the instance is created.
 
So, with the default attribute, you assign what values do not use the bold font (are defaults).
 
If that'd what it does, then where are the default values assigned?
 
The problem with components is that they are instantiated in two scenarios, in design mode, and at runtime. Hence, the ISupportInitialize allows for intialization logic.
 
You reconized the interface and a handy little property called 'DesignMode' which is available, but you didn;t solve your problem with them... use them to check for a value of the properties that seem to "disappear". They disappear because nothing was assigned as values. If you on't want them to showup in the intialize component section, then use the hidden attribute value.
 
// create default values in EndEdit()
if (DesignMode) {
... add values here
if (myValue == null)
myValue = blah blah
}
 
This was a very good article, but you should know that when you use the term "hack", you are probobaly missing the "right" way to resolve the problem.
AnswerRe: Why use hack?memberJames T. Johnson11 Jun '06 - 9:08 
The purpose of that code wasn't to provide a default value, but to hook the control up to the IExtenderProvider. To keep the sample simple and outline cases where the IExtenderProvider should always be attached to the controls it can extend this works fine.
 
If the generated code never calls one of the Set* methods, then the component simply won't know about the controls available without either: walking the entire control stack or more sophisticated designer magic to find all of the controls and then insert the calls behind its back.
 
James
GeneralRe: Why use hack?memberSPENNER13 Mar '08 - 19:51 
...I think you missed the point of the constructive criticism
 
-scott

GeneralEnumeration and IExtenderProvidermemberChrisChi092226 Mar '06 - 21:27 
HI
 
I have write a IExtenderProvider component, and I have defined a
custom enumeration type, but the Control always show the default
enumeration value (but the code (XXXProvider.SetXXX) is right), Why ?
Is it need a special attribute (like DefaultValue) or TypeConverter ?
 
Thanks
 

 
Chris
GeneralRe: Enumeration and IExtenderProvidermemberJames T. Johnson29 Mar '06 - 14:04 
If you are following the same pattern I use, you need to make sure that the class holding the data for the IExtenderProvider initializes the enum to the proper value. Otherwise the default will be the first item in the enum. If you want to use the same class in multiple controls then you need to change the Ensure*Exists method initializes the value when it creates a new instance of that class.
 
HTH,
 
James
GeneralMenuItemsmemberWackatronic116 May '05 - 12:56 
I tried to provide a property for my menu items that would hold a ResourceID. Somehow I'm missing something because I was able to get all the controls on the form to accept a resource/ID.
 
Here's the code. Can someone help me please.
 
using System;
using System.ComponentModel;
using System.Collections;
using System.Windows.Forms;
 
namespace MMOArchitect.Extender
{
[ProvideProperty("ResourceID", typeof(string))]
public class MenuItemExtender : Component, IExtenderProvider
{
private Hashtable properties;
private System.ComponentModel.Container components = null;
 
public MenuItemExtender()
{
properties = new Hashtable();
 
InitializeComponent();
}
 
public MenuItemExtender(System.ComponentModel.IContainer container ) : this()
{
container.Add(this);
}
 
///
/// Clean up any resources being used.
///

protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
 
#region Component Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///

private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
 
#region IExtenderProvider.CanExtend
bool IExtenderProvider.CanExtend(object o)
{
if ( o is MenuItem )
{
MenuItem menuItem = (MenuItem) o ;
return ! ( menuItem.Parent is MainMenu ) ;
}
else
{
return false;
}
}
#endregion
 
#region Properties class
private class Properties
{
public string ResourceID;
 
public Properties()
{
ResourceID = System.Environment.Version.ToString();;
}
}
#endregion
 
#region EnsurePropertiesExists
private Properties EnsurePropertiesExists(object key)
{
Properties p = (Properties) properties[key];
 
if( p == null )
{
p = new Properties();
 
properties[key] = p;
}
 
return p;
}
#endregion
 
#region ResourceID
[DefaultValue("Standard")]
[Category("Localization")]
[Description("This is used to provide a link to the required localized string")]
public string GetResourceID(MenuItem menuitem)
{
return EnsurePropertiesExists(menuitem).ResourceID;
}

public void SetResourceID(MenuItem menuitem, string value)
{
EnsurePropertiesExists(menuitem).ResourceID = value;
}
private bool ShouldSerializeResourceID(MenuItem menuitem)
{
if( GetResourceID(menuitem) != System.Environment.Version.ToString() )
return true;
else
return false;
}
 
private void ResetResourceID(MenuItem menuitem)
{
SetResourceID(menuitem, System.Environment.Version.ToString());
}
#endregion
}
}

 
Any help would be appreciated.
 
Fear is the mind killer.
GeneralRe: MenuItemsmemberJames T. Johnson16 May '05 - 14:14 
The correct attribute should be:
 
[ProvideProperty("ResourceID", typeof(MenuItem))]
 
The Type parameter tells the designer what Types can take this property, not the type of the property you are adding.
 

 
But, if you are using Visual Studio you could be recreating the wheel here.
 
When you design a Form in VS.NET you can set all of the properties, etc, how you want them to be by default. Then if you look at the properties for the Form you should see a Misc. category (assuming you are in category view).
 
One is named Localizable and the other is Language. Set Localizable to true and change the language to the desired language and set all of your properties to the correct values. Do this for all of the languages you want your app to support.
 
If you look at the files generated by VS.NET you will see several files named: [formname].[language].resx each of these contain the property values you set while the specified Language was set. You should also be able to edit these outside of Visual Studio using a tool like Resourcer from Lutz Roeder.
 
Hope that helps,
 
James
Generalthe formsussAnonymous20 Apr '05 - 18:27 
can the extender find out the form it's on too?
also, how does the designer check what constructor to use? It requires a () and a (System.ComponentModel.IContainer container) to be implemented only
Sniff | :^)
GeneralRe: the formmemberJames T. Johnson20 Apr '05 - 19:00 
Yes, the extender can find out which form it is placed on through some design-time trickery.
 
private Form host;
[
    DefaultValue(null),
    Category("Design"),
    Description("A reference to the form which contains this component.")
]
public Form Host
{
    get
    {
        if( host == null && DesignMode )
        {
            IDesignerHost designerHost = (IDesignerHost) GetService(typeof(IDesignerHost));
 
            if( designerHost != null && designerHost.RootComponent is Form )
                host = (Form) designerHost.RootComponent;
        }
 
        return host;
    }
    set
    {
        this.host = value;
    }
}
 
I don't know for sure, but I would guess that the Designer uses Reflection to see what type of constructor(s) the component implements. If it implements one taking an IContainer it uses that one, otherwise it uses an empty constructor. If neither are present then you should get an error when you drop the component on to your form.
 
James
Questionhow to add to toolboxsussAnonymous19 Apr '05 - 15:46 
I cannot drag it to the toolbox and work. it becomes a text document not component.
Dead | X|
AnswerRe: how to add to toolboxmembersixgun17 Feb '06 - 23:48 
Try putting the extender into a seperate dll.
 
Then build the dll, right click in the toolbox you want to add it to, choose "Add/Remove Items", then browse for the dll.
 
You should be able to pick which components to add from the dll, default is to add all of them.
 
Works for me.
 
Hope that helps.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 3 Aug 2003
Article Copyright 2003 by James T. Johnson
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid