データバインドされたリピーター内で条件付きコンテンツを作成する方法
-
07-07-2019 - |
質問
XML構成によって駆動されるユーザーコントロールをセットアップしています。例で説明する方が簡単です。次の構成スニペットをご覧ください。
<node>
<text lbl="Text:"/>
<checkbox lbl="Check me:" checked="true"/>
</node>
そのスニペットを単一のテキストボックスとチェックボックスコントロールに変換するために達成しようとしていること。もちろん、スニペットに含まれるノードが増えると、より多くのコントロールが自動的に生成されます。
タスクの反復的な性質を与え、リピーターを使用することにしました。その中に、2つの(さらに詳しくは以下を参照)コントロール、1つのCheckBoxと1つのEditboxを配置しました。アクティブにするコントロールを選択するために、インラインスイッチコマンドを使用して、現在の構成ノードの名前を確認しました。
残念ながら、それは機能しません。問題は、データバインディングが発生してからずっと後に、レンダリング中にスイッチが実行されているという事実にあります。それだけでは問題ではなく、構成ノードがデータバインドに必要な情報を提供する可能性があるという事実のためではありません。チェックボックスコントロールが上記のスニペットのテキストノードにバインドしようとしてどうなるかを考え、必死に「チェック済み」を探します。属性。
これを可能にする方法はありますか?
ありがとう、 ボアズ
現在のコードは次のとおりです。
ここに私のコードがあります(上記のコードよりも複雑な構文で実行されます):
<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>
解決
ある週末の後、私は解決策としてここに来ました。私の主な目標は、マークアップでアイテムテンプレートの正確なコンテンツを指定し続けることができ、機能するものを見つけることでした。コードから物事を行うと動作しますが、それでも面倒なことができます。
コードは簡単に理解できるはずですが、問題の要旨は2つの部分にあります。
1つ目は、Repeater item createdイベントを使用して、テンプレートの不要な部分を除外することです。
2番目は、ポストバック中にページを再作成するために、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から抽出されます。私の特定のシナリオでは、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);
}
}
他のヒント
次のようなものが必要です:
<ItemTemplate>
<%# GetContent(Page.GetDataItem()) %>
</ItemTemplate>
そして、すべてのコントロールを分離コードで生成します。