tags:

views:

14

answers:

2

I am sure this question has been asked and answered; but I haven't managed to find it...

I am creating a very simple custom System.Web.UI.Control that has a few properties. I can define in my ASPX page the following tag and everything is happy:

<ns:MyControl runat="server" MyProperty="Value" />

However, if I want to have one or more "child" properties, like so:

<ns:MyControl runat="server" MyProperty="Value">
  <Element AnotherProperty="AnotherValue1" />
  <Element AnotherProperty="AnotherValue2" />
</ns:MyControl>

I cannot figure out what I need to do to make the XHTML validate. I always have

  • Content is not allowed between the opening and closing tag of element 'XXX'
  • Element 'XXX' is not supported
  • The name contains uppercase characters, which is not allowed

The code actually runs as expected, but I haven't manged to find a good example on how to do this correctly so that everything validates. In terms of implementation of the Custom Control, I just have all properties stubbed out at the moment, and looks something like:

  [ParseChildren(true)]
  [PersistChildren(false)] 
  public class MyControl : Control
  {
    public String MyProperty { get; set; }
    public String Element { get; set; }
  }

Ultimately, Element is intended to build up a collection of Elements. Any insight on how to do this properly and have the XHTML validate would be greatly appreciated.

Thanks in advance.

A: 

Look to the ASP.NET Menu Control for an example of serializing collections of child elements. This control uses a single inner property, called <Items>, which contains the collection of menu items You can mimic the definition of the Items property (appreviated for clarity).

[PersistenceMode(PersistenceMode.InnerProperty)]
public MenuItemCollection Items
{
    get { /* ... */ }
}

Note that you will probably want to remove the [ParseChildren(true)] and [PersistChildren(false)] from your class definition if you intend to serialize collections as inner properties.

If you expect to support exactly one child collection (a la DropDownList), then you can swap out the persistence mode for PersistenceMode.InnerDefaultProperty, and be able to add your items without a wrapping element.

kbrimington
May you please clarify why you recommend removing the Parse/Persist Children attributes? Granted I think I should have Parse as true and Persist as false, but curious as to why they should be removed altogether? Is it redundant with PersistenceMode?
Calgary Coder
The docs indicate "Marking your server control with the metadata attribute ParseChildren(true) instructs the parser to interpret the elements that are contained within the server control's tags as properties." In your example, you specify `MyProperty` as an attribute. Removing the decoration can protect you from unexpected behavior. That's all.
kbrimington
+1  A: 

I wrote a blog post back in 2008 about this, which you can find here: Describing ASP.net Control properties declaratively.

Basically, to achieve markup in this format:

<Abc:CustomControlUno runat="server" ID="Control1">
    <Children>
        <Abc:Control1Child IntegerProperty="1" StringProperty="Item1" />
        <Abc:Control1Child IntegerProperty="2" StringProperty="Item2" />
    </Children>
</Abc:CustomControlUno>

You'd need the following code:

[ParseChildren(true)]
[PersistChildren(true)]
[ToolboxData("<{0}:CustomControlUno runat=server></{0}:CustomControlUno>")]
public class CustomControlUno : WebControl, INamingContainer
{
    private Control1ChildrenCollection _children;

    [PersistenceMode(PersistenceMode.InnerProperty)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public Control1ChildrenCollection Children
    {
        get
        {
            if (_children == null)
                _children = new Control1ChildrenCollection();
            return _children;
        }
    }
}

public class Control1ChildrenCollection : List<Control1Child>
{
}

public class Control1Child
{
    private int integerProperty;
    private string stringProperty;

    public string StringProperty
    {
        get { return stringProperty; }
        set { stringProperty = value; }
    }

    public int IntegerProperty
    {
        get { return integerProperty; }
        set { integerProperty = value; }
    }
}
Rob