How to create conditional content in a data relay

I am setting up User Control, controlled by XML configuration. This is easier to explain with an example. Take a look at the following piece of configuration:

<node> <text lbl="Text:"/> <checkbox lbl="Check me:" checked="true"/> </node> 

What I'm trying to do is to translate this fragment into a single text box and checkbox control. Of course, if the fragment contained more nodes, then more controls would be created automatically.

Give the iterative nature of the task, I decided to use Repeater. Inside it I placed two (even more, see below) Controls, one CheckBox and one Editbox. To select which control to activate, I used the built-in switch command, checking the name of the current node configuration.

Unfortunately this does not work. The problem is that the switch starts during rendering, long before the data binding occurs. This alone will not be a problem, not the fact that the node configuration can provide the necessary information for data binding. Think about what happens if the checkbox element tries to associate with the text node in the snippet above, desperately looking for its "checked" attribute.

Any ideas how to make this possible?

Thanks Boaz

Here is my current code:

Here is my code (which works on more complex syntax than above):

 <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; } %> &nbsp;&nbsp; </ItemTemplate> </asp:Repeater> 
+4
source share
2 answers

One weekend later, that’s what I came up with the solution. My main goal was to find something that would work and let you specify the exact contents of the element template in the markup. Doing things from code will work, but can still be cumbersome.

The code must be straightforward to follow, but the gist of the matter is in two parts.

The first is the use of the event generated by the Repeater element to filter out unwanted parts of the template.

The second is to store decisions made in ViewState to recreate the page during feedback. The latter is critical, as you will notice that I used Item.DataItem. During post backs, rest control occurs much earlier in the page life cycle. When the ItemCreate starts, the DataItem is null.

Here is my solution:

Control markup

  <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> 

Note. For added simplicity, I added a PlaceHolder control to group things together and decide which controls are easier to remove.

Code for

The code below is built on the notion that each relay element has a type. The type is retrieved from the xml configuration. In my specific scenario, I could make this type a single control using an identifier. If necessary, this can be easily changed.

  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); } } 
+4
source

You need something similar to this:

 <ItemTemplate> <%# GetContent(Page.GetDataItem()) %> </ItemTemplate> 

And then all your controls are generated in code.

+1
source

All Articles