Как определить свойство в пользовательском или серверном элементе управления ASP.NET, чтобы разрешить использование нескольких строковых значений в виде вложенных тегов с именем настраиваемого тега? - PullRequest
12 голосов
/ 19 января 2012

Мне нужно написать пользовательский элемент управления, который можно использовать со следующим синтаксисом:

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

Я пробовал следующее объявление свойства:

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

Но тогда редактор Visual Studio настаивает на том, что <Answers > должен быть самозакрывающимся, и я получаю это исключение, если я решу иначе:

Литеральное содержимое ('Foo') не допускается в 'System.String []'.

Я смотрел на <asp:DropDownList> в Reflector, который наследуется от ListControl, который объявляет свойство Items следующим образом:

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

Это не совсем то, что я хочу, потому что в DropDownList вы должны добавить <asp:ListItem> как дети. И есть некоторые вещи, которые я не понимаю в дизайне элементов управления, которые в настоящее время мешают мне найти решение:

  • Почему для тега <asp:ListItem> не требуется атрибут runat="server"?
  • Могу ли я объявить такой «контроль»?
  • Что такого особенного в коллекции ListItemCollection, что она переводит в этот конкретный синтаксис?
  • Какой код я могу написать, который будет преобразован в синтаксис, приведенный в первом примере кода выше?

1 Ответ

3 голосов
/ 21 января 2012

Я смешал поведение элементов управления, которые могут иметь дочерние элементы (например, ListControl), с элементами управления, такими как 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>
    {
    }

Тогда вы сможете получить что-то вроде:

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

Надеюсь, это поможет

...