views:

43

answers:

2

I'm trying to add a dynamically expandable property to a composite control that I can drop on the designer surface. I've tried this with a String Array, a List, and an ArrayList. All with similar results. I'm missing something and I don't know what. Here is what I think is the relevant code:

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 
    NotifyParentProperty(true),
    PersistenceMode(PersistenceMode.InnerProperty)]
    public String[] AccessLevels
    {
        get
        {
            String[] s = (String[])ViewState["AccessLevels"];
            return s;
        }

        set
        {
            ViewState["AccessLevels"] = value;
        }
    }

The control compiles fine and I can drop it on my designer surface. It gives me a property in the "Properties" window on Visual Studio 2008 called "+AccessLevels" with a value of "String[] Array" and an elipsis [...] next to it. If I click the elipsis it opens up an editor where I can insert properties one line at a time. If I click the little plus symbol next to the "AccessLevels" property, it shows the properties that I entered and each has a number next to it, indicating the index of the array. Great! But when I do this, regardless of what values I enter as properties, the following HTML is autogenerated by the designer.

    <cc2:HBAdmin ID="HBAdmin1" runat="server">
        <AccessLevels>
        <system.string></system.string>
        <system.string></system.string>
        <system.string></system.string>
        </AccessLevels>
    </cc2:HBAdmin>

HBAdmin is the name of my control, and the cc2 namespace is correct. The problem is the tags. There should be a value in there right? I also get intellisense for the <AccessLevels> tag telling me "Content is not allowed between the opening and closing tags for element 'AccessLevels'", and also a validation error on the <system.string> tag telling me "Element 'System.String' is not supported. Then if I try to view the page with the control in the browser (after editing the properties in the design window that results in the markup above) I get the following error on the line with the opening tag for the control:

"Array creation must have array size or array initializer"

I only get this error when I try to edit the properties in the properties window of the designer. I can populate the property with values in the constructor and the page with the control will load in the browser and work fine, and the values show up fine in the properties window of the design surface and I can use the values throughout the control, but I can't edit them in the properties window. If I try to add one I get the same symptoms as described above.

I feel like I'm probably just missing some sort of attribute or declaration or something. Please help?

+1  A: 

Try something like:

Usage:

<cc:SomeClass >
 <CustomLavel key="" value="" />
 <CustomLavel key="" value="" />
 <CustomLavel key="" value="" />
</cc:SomeClass>


public class SomeClass: Control, INamingContainer
{
           private Collection<CustomLabel> _customLabelList;

           protected override void AddParsedSubObject(object obj)
        {
            base.AddParsedSubObject(obj);

            if (obj is CustomLabel)
            {
                _customLabelList.Add((CustomLabel)obj);
                return;
            }
        }

[Category("Behavior")]
        [Description("The fields collection")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]    
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        [DefaultValue(null), MergableProperty(false), Bindable(false)]
        public Collection<CustomLabel> CustomLabelList
        {
            get
            {
                return _customLabelList;
            }
        }
}

[TypeConverter(typeof(ExpandableObjectConverter))]
public class CustomLabel
{
    private string _key;
    private string _value;

    public CustomLabel()
        : this(string.Empty, string.Empty)
    {
    }

    public CustomLabel(string key, string value)
    {
        _key = key;
        _value = value;
    }


    [Category("Behavior")]
    [DefaultValue("")]
    [Description("Key")]
    [NotifyParentProperty(true)]
    public string Key
    {
        get
        {
            return _key;
        }
        set
        {
            _key = value;
        }
    }

    [Category("Behavior")]
    [DefaultValue("")]
    [Description("Value")]
    [NotifyParentProperty(true)]
    public string Value
    {
        get
        {
            return _value;
        }
        set
        {
            _value = value;
        }
    }
}
skyfoot
I'm trying to use this code, but I don't understand the overridden AddParsedSubObject method. What is _customLabelList and where is it defined? Is "SomeClass" the class that defines my Composite Control, or does it define the type that holds my data, or is it just used as an example and I would replace it with my Composite control?
Camenwolf
Ah sorry customLabelList is a private field, I'll edit the answer. SomeClass is your control which you will put on the page. You would replace my code with your specific needs. This is an example which I use on one of my sites.
skyfoot
When I assign values to the property in the designer they seem to work, but when I okay out of the designer, they are not listed in the property window, and they don't persist. Also, I try to add some default values in the constructor like this.AddParsedSubObject(new CustomLabel("thiskey", "thatvalue")); then when I drop the ctrl on the designer I get "Object reference not set to and instance of an object". So I try to instantiate the object and assign it then use that instance in the call to AddParsedSubObject. Same thing.
Camenwolf
Also, is it possible to just use a dynamic string array here instead of a custom type?
Camenwolf
I managed to allow the values to persist and got rid of the error by creating a new Collection<CustomLabel> object instead of just allocating it. That is (in case I'm using incorrect terminology) by changing the first line from this: private Collection<CustomLabel> _customLabelList; to this: private Collection<CustomLabel> _customLabelList = new Collection<CustomLabel>();
Camenwolf
I feel like I'm almost there. Now I can add and remove properties in the designer but when I do then view the page in the browser I get this error: Type SomeNamespace.SomeControl' does not have a public property named 'CustomLabel'. I'm still getting validation errors for the <CustomLabel value="somevalue></CustomLabel> tags.
Camenwolf
@Camenwold for your last question I think you need the prefix added to your control e.g. <cc2:CustomLabel value="" key="" />. When Im back in work I will test this.
skyfoot
I was not able to get this code to work, so what I ended up doing was creating an entirely new class and copying your code to it. After modifying the first line to create a new collection (see above), your code worked! Then I realized why I can't get it to work in my project. I'm creating a composite control. When I change the inheritance from control to compositecontrol and view the page I get Type 'SomeClass1' does not have a public property named 'CustomLabel'. I'm getting close here, if I could must a little more assitance. I'd love to gain some understanding of these concepts.
Camenwolf
+2  A: 

You can use Collection<string> instead of string[]

AsifQadri