Я настраиваю пользовательский элемент управления, управляемый конфигурацией XML. Это проще объяснить на примере. Взгляните на следующий фрагмент конфигурации:
<node>
<text lbl = "Text:"/>
<checkbox lbl = "Check me:" checked = "true"/>
</node>
Я пытаюсь перевести этот фрагмент в одно текстовое поле и элемент управления флажком. Конечно, если бы фрагмент содержал больше узлов, автоматически создавалось бы больше элементов управления.
Учитывая итеративный характер задачи, я решил использовать Repeater. В нем я разместил два (а точнее, больше, см. Ниже) Controls, одно CheckBox и одно Editbox. Чтобы выбрать, какой элемент управления будет активирован, я использовал команду встроенного переключателя, проверив имя текущего узла конфигурации.
К сожалению, это не работает. Проблема заключается в том, что переключатель запускается во время рендеринга, спустя много времени после того, как произошло связывание данных. Одно это не было бы проблемой, не потому, что узел конфигурации мог предложить необходимую информацию для привязки данных. Подумайте, что произойдет, если элемент управления флажком попытается привязаться к текстовому узлу в приведенном выше фрагменте, отчаянно ища его атрибут "checked".
Есть идеи, как это сделать?
Спасибо, Вооз
Вот мой текущий код:
Вот мой код (который работает с более сложным синтаксисом, чем приведенный выше):
<asp:Repeater ID = "settingRepeater" runat = "server">
<ItemTemplate>
<%
switch (((XmlNode)Page.GetDataItem()).LocalName)
{
case "text":
%>
<asp:Label ID = "settingsLabel" CssClass = "editlabel" Text='<%# XPath("@lbl") %>' runat = "server" />
<asp:TextBox ID = "settingsLabelText" Text='<%# SettingsNode.SelectSingleNode(XPath("@xpath").ToString()).InnerText %>'
runat = "server" AutoPostBack = "true" Columns='<%# XmlUtils.OptReadInt((XmlNode)Page.GetDataItem(),"@width",20) %>'
/>
<% break;
case "checkbox":
%>
<asp:CheckBox ID = "settingsCheckBox" Text='<%# XPath("@lbl") %>' runat = "server"
Checked='<%# ((XmlElement)SettingsNode.SelectSingleNode(XPath("@xpath").ToString())).HasAttribute(XPath("@att").ToString()) %>'
/>
<% break;
} %>
</ItemTemplate>
</asp:Repeater>





Вам нужно что-то похожее на это:
<ItemTemplate>
<%# GetContent(Page.GetDataItem()) %>
</ItemTemplate>
А затем создайте все ваши элементы управления в коде программной части.
Спустя один уик-энд вот что я пришел с решением. Моя главная цель состояла в том, чтобы найти что-то, что одновременно будет работать и позволит вам указывать точное содержимое шаблона элемента в разметке. Выполнение чего-либо из кода будет работать, но все же может быть громоздким.
Код должен быть простым, но суть состоит в двух частях.
Первый - это использование события создания элемента Repeater для фильтрации нежелательных частей шаблона.
Второй - хранить решения, принятые во ViewState, чтобы воссоздать страницу во время обратной публикации. Последнее имеет решающее значение, поскольку вы заметите, что я использовал Item.DataItem. Во время обратной публикации управление воспроизведением происходит намного раньше в жизненном цикле страницы. Когда срабатывает ItemCreate, DataItem имеет значение NULL.
Вот мое решение:
Разметка управления
<asp:Repeater ID = "settingRepeater" runat = "server"
onitemcreated = "settingRepeater_ItemCreated"
>
<ItemTemplate>
<asp:PlaceHolder ID = "text" runat = "server">
<asp:Label ID = "settingsLabel" CssClass = "editlabel" Text='<%# XPath("@lbl") %>' runat = "server" />
<asp:TextBox ID = "settingsLabelText" runat = "server"
Text='<%# SettingsNode.SelectSingleNode(XPath("@xpath").ToString()).InnerText %>'
Columns='<%# XmlUtils.OptReadInt((XmlNode)Page.GetDataItem(),"@width",20) %>'
/>
</asp:PlaceHolder>
<asp:PlaceHolder ID = "att_adder" runat = "server">
<asp:CheckBox ID = "settingsAttAdder" Text='<%# XPath("@lbl") %>' runat = "server"
Checked='<%# ((XmlElement)SettingsNode.SelectSingleNode(XPath("@xpath").ToString())).HasAttribute(XPath("@att").ToString()) %>'
/>
</asp:PlaceHolder>
</ItemTemplate>
</asp:Repeater>
Примечание: для большей простоты я добавил элемент управления PlaceHolder, чтобы сгруппировать элементы и решить, какие элементы управления легче удалить.
Код позади
Приведенный ниже код основан на представлении о том, что каждый элемент репитера имеет определенный тип. Тип извлекается из конфигурации xml. В моем конкретном сценарии я мог бы преобразовать этот тип в один элемент управления с помощью идентификатора. При необходимости это можно легко изменить.
protected List<string> repeaterItemTypes
{
get
{
List<string> ret = (List<string>)ViewState["repeaterItemTypes"];
if (ret == null)
{
ret = new List<string>();
ViewState["repeaterItemTypes"] = ret;
}
return ret;
}
}
protected void settingRepeater_ItemCreated(object sender, RepeaterItemEventArgs e)
{
string type;
if (e.Item.DataItem != null)
{
// data binding mode..
type = ((XmlNode)e.Item.DataItem).LocalName;
int i = e.Item.ItemIndex;
if (i == repeaterItemTypes.Count)
repeaterItemTypes.Add(type);
else
repeaterItemTypes.Insert(e.Item.ItemIndex, type);
}
else
{
// restoring from ViewState
type = repeaterItemTypes[e.Item.ItemIndex];
}
for (int i = e.Item.Controls.Count - 1; i >= 0; i--)
{
if (e.Item.Controls[i].ID != type) e.Item.Controls.RemoveAt(i);
}
}