Как я могу определить свойство элемента управления пользователя или сервера ASP.NET, чтобы разрешить несколько значений строк в виде вложенных тегов с пользовательским именем тега?
Мне нужно написать пользовательский элемент управления, который можно использовать со следующим синтаксисом:
<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
Я смешанное поведение элементов управления, у которых могут быть дочерние элементы (например, 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>
Надеюсь, что это поможет