4

我正在设置一个由 XML 配置驱动的用户控件。用例子来解释更容易。看看下面的配置片段:

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

我试图实现将该片段转换为单个文本框和复选框控件。当然,如果代码片段包含更多节点,则会自动生成更多控件。

鉴于任务的迭代性质,我选择使用Repeater。在其中我放置了两个(更多,见下文)控件,一个 CheckBox 和一个 Editbox。为了选择激活哪个控件,我使用了内联 switch 命令,检查当前配置节点的名称。

可悲的是,这行不通。问题在于,在数据绑定发生很久之后,切换是在渲染期间运行的。仅此一项就不是问题,不是因为配置节点可能会为数据绑定提供所需的信息。考虑一下如果复选框控件将尝试绑定到上面代码片段中的文本节点,拼命寻找它的“已选中”属性会发生什么。

任何想法如何使这成为可能?

谢谢,波阿斯

这是我当前的代码:

这是我的代码(运行的语法比上面的更复杂):

<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

2 回答 2

4

一个周末后,这是我提出的解决方案。我的主要目标是找到既可以工作又可以让您在标记中继续指定项目模板的确切内容的东西。从代码中做事会起作用,但仍然很麻烦。

代码应该是直截了当的,但问题的要点在于两部分。

第一个是使用Repeater item created 事件来过滤掉模板中不需要的部分。

第二个是存储在 ViewState 中做出的决定,以便在回发期间重新创建页面。后者至关重要,因为您会注意到我使用了 Item.DataItem 。在回发期间,控件重新生成在页面生命周期的早期发生。当 ItemCreate 触发时,DataItem 为空。

这是我的解决方案:

控制标记

 <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 中提取的。在我的特定场景中,我可以通过 ID 将该类型设置为单个控件。如果需要,这可以很容易地修改。

 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);
      }
   }
于 2008-11-09T22:35:47.897 回答
1

你需要看起来更像这样的东西:

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

然后在代码隐藏中生成所有控件。

于 2008-11-08T02:30:53.070 回答