Michiel van Oosterhout
Michiel van Oosterhout

Reputation: 23094

How can I define a property on an ASP.NET user or server control to allow multiple string values as nested tags with a custom tag name?

I need to write a user control that can be used with the following syntax:

<quiz:Question runat="server">
  <Answer>Foo</Answer>
  <Answer>Bar</Answer>
</quiz:Question>

I tried the following property declaration:

[ParseChildren(true, "Answer")]
public class Question : UserControl
{
  [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
  public string[] Answer { get; set; }
}

But then the Visual Studio editor insist that <Answers > should be self-closing and I get this exception if I decide otherwise:

Literal content ('Foo') is not allowed within a 'System.String[]'.

I have been looking at <asp:DropDownList> in Reflector, which inherits from ListControl which declares the Items property as follows:

ParseChildren(true, "Items")
public abstract class ListControl
{
  [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
  public virtual ListItemCollection Items { get; }
}

It's not really the same as what I want, because in DropDownList you must add <asp:ListItem> as children. And there are some things I don't understand about control design, which is currently preventing me from finding the solution:

Upvotes: 11

Views: 1214

Answers (1)

Alireza Sabouri
Alireza Sabouri

Reputation: 1436

I mixed behavior of controls those can have child items (like ListControl), with controls like Panel (ParseChildren=false) :

    [ParseChildren(true, "Answers")]
    public class Question : WebControl, INamingContainer
    {
        private AnswerCollection answers;

        public virtual AnswerCollection Answers
        {
            get
            {
                if (this.answers == null)
                {
                    this.answers = new AnswerCollection();
                }
                return this.answers;
            }
        }


        public override void RenderControl(HtmlTextWriter writer)
        {
            //access to each answer value
            foreach (var a in this.Answers)
                writer.WriteLine(((LiteralControl)a.Controls[0]).Text + "<br/>");
        }
    }

    [ParseChildren(false), PersistChildren(true)]
    public class Answer : Control
    {
    }

    public class AnswerCollection : List<Answer>
    {
    }

Then you would be able to have something like :

<cc1:Question runat="server">
    <cc1:Answer>Answer1</cc1:Answer>
    <cc1:Answer>Answer2</cc1:Answer>
</cc1:Question>

Hope it helps

Upvotes: 3

Related Questions