DropDownList の ListItems 属性はポストバック時に失われますか?
-
19-09-2019 - |
質問
同僚が私にこれを見せてくれました。
彼は Web ページ上に DropDownList とボタンを持っています。背後にあるコードは次のとおりです。
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ListItem item = new ListItem("1");
item.Attributes.Add("title", "A");
ListItem item2 = new ListItem("2");
item2.Attributes.Add("title", "B");
DropDownList1.Items.AddRange(new[] {item, item2});
string s = DropDownList1.Items[0].Attributes["title"];
}
}
protected void Button1_Click(object sender, EventArgs e)
{
DropDownList1.Visible = !DropDownList1.Visible;
}
ページの読み込み時に項目のツールチップが表示されますが、最初のポストバック時に属性が失われます。これはなぜ起こるのでしょうか?また、回避策はありますか?
解決
私も同じ問題を抱えていたので貢献したいと思いました これ このリソースでは、作成者が属性を ViewState に永続化するために継承された ListItem Consumer を作成しました。うまくいけば、私がそれに出会うまで無駄に費やしていた時間を誰かが節約できるでしょう。
protected override object SaveViewState()
{
// create object array for Item count + 1
object[] allStates = new object[this.Items.Count + 1];
// the +1 is to hold the base info
object baseState = base.SaveViewState();
allStates[0] = baseState;
Int32 i = 1;
// now loop through and save each Style attribute for the List
foreach (ListItem li in this.Items)
{
Int32 j = 0;
string[][] attributes = new string[li.Attributes.Count][];
foreach (string attribute in li.Attributes.Keys)
{
attributes[j++] = new string[] {attribute, li.Attributes[attribute]};
}
allStates[i++] = attributes;
}
return allStates;
}
protected override void LoadViewState(object savedState)
{
if (savedState != null)
{
object[] myState = (object[])savedState;
// restore base first
if (myState[0] != null)
base.LoadViewState(myState[0]);
Int32 i = 1;
foreach (ListItem li in this.Items)
{
// loop through and restore each style attribute
foreach (string[] attribute in (string[][])myState[i++])
{
li.Attributes[attribute[0]] = attribute[1];
}
}
}
}
他のヒント
おかげで、ララミー。ただ、私が探していたもの。それは完全な属性を保持します。
展開するには、以下の私はVS2008でのドロップダウンリストを作成するララミーのコードを使用して作成したクラスファイルです。 App_Codeフォルダー内のクラスを作成します。あなたがクラスを作成したら、それを登録するaspxページにこの行を使用します:
<%@ Register TagPrefix="aspNewControls" Namespace="NewControls"%>
あなたは、このを使用してWebフォーム上のコントロールを配置することができます。
<aspNewControls:NewDropDownList ID="ddlWhatever" runat="server">
</aspNewControls:NewDropDownList>
[OK]を、ここではクラスだ...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Security.Permissions;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace NewControls
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")]
public class NewDropDownList : DropDownList
{
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
protected override object SaveViewState()
{
// create object array for Item count + 1
object[] allStates = new object[this.Items.Count + 1];
// the +1 is to hold the base info
object baseState = base.SaveViewState();
allStates[0] = baseState;
Int32 i = 1;
// now loop through and save each Style attribute for the List
foreach (ListItem li in this.Items)
{
Int32 j = 0;
string[][] attributes = new string[li.Attributes.Count][];
foreach (string attribute in li.Attributes.Keys)
{
attributes[j++] = new string[] { attribute, li.Attributes[attribute] };
}
allStates[i++] = attributes;
}
return allStates;
}
protected override void LoadViewState(object savedState)
{
if (savedState != null)
{
object[] myState = (object[])savedState;
// restore base first
if (myState[0] != null)
base.LoadViewState(myState[0]);
Int32 i = 1;
foreach (ListItem li in this.Items)
{
// loop through and restore each style attribute
foreach (string[] attribute in (string[][])myState[i++])
{
li.Attributes[attribute[0]] = attribute[1];
}
}
}
}
}
}
簡単な解決策は、ドロップダウンのpre-render
イベントでツールチップ属性を追加することです。状態への変更はpre-render
イベントで行われる必要があります。
サンプルコード:
protected void drpBrand_PreRender(object sender, EventArgs e)
{
foreach (ListItem _listItem in drpBrand.Items)
{
_listItem.Attributes.Add("title", _listItem.Text);
}
drpBrand.Attributes.Add("onmouseover", "this.title=this.options[this.selectedIndex].title");
}
あなたがコントロールがその状態をシリアル化することができるようにViewStateを有効にし、それをリロードする必要があります時にバックページポストます。
<pages/>
プロパティのASPXファイル自体の先頭に<%@ page %>
ディレクティブでもweb.configファイルでEnableViewState
ノードをチェックして、 - 、ViewStateが有効にすることができるいくつかの場所があります。 ViewStateが動作するため、この設定はtrue
する必要があります。
は、単にif (!IsPostBack) { ... }
を追加するコードの周りからListItems
を削除し、アイテムが各ポストバックに再作成されます。
の編集の私は謝罪 - 私はあなたの質問を読み違えます。あなたは彼らがViewStateにシリアライズされていないとして、何のポストバックを生き残るんのはを属性という正しいません。あなたは、各ポストバックでそれらの属性を再度追加する必要があります。
あなたが戻ってポストを要求クリックイベントのロード機能ダウンあなたのドロップを呼び出すソリューション - シンプルなワンます。
この問題に対する一般的な解決策には、通常の状況ではまったく実現不可能な新しいコントロールを作成することが含まれます。この問題には単純だが簡単な解決策があります。
問題は、 ListItem
ポストバック時に属性が失われます。ただし、リスト自体はカスタム属性を失うことはありません。このように、シンプルかつ効果的な方法でこれを利用できます。
手順:
上記の回答のコードを使用して属性をシリアル化します (https://stackoverflow.com/a/3099755/3624833)
それを ListControl のカスタム属性 (ドロップダウンリスト、チェックリストボックスなど) に保存します。
ポストバック時に、ListControl からカスタム属性を読み取り、それを属性として逆シリアル化します。
これは、属性を(逆)シリアル化するために使用したコードです(私が行う必要があるのは、バックエンドから取得したときにリストのどの項目が最初に選択されたものとしてレンダリングされたかを追跡し、その後、実行された変更に従って行を保存または削除することでした) UI 上のユーザー):
string[] selections = new string[Users.Items.Count];
for(int i = 0; i < Users.Items.Count; i++)
{
selections[i] = string.Format("{0};{1}", Users.Items[i].Value, Users.Items[i].Selected);
}
Users.Attributes["data-item-previous-states"] = string.Join("|", selections);
(上記の「ユーザー」とは、 CheckboxList
コントロール)。
ポストバック (私の場合は送信ボタンのクリック イベント) で、以下のコードを使用して同じものを取得し、後処理のためにディクショナリに保存します。
Dictionary<Guid, bool> previousStates = new Dictionary<Guid, bool>();
string[] state = Users.Attributes["data-item-previous-states"].Split(new char[] {'|'}, StringSplitOptions.RemoveEmptyEntries);
foreach(string obj in state)
{
string[] kv = obj.Split(new char[] { ';' }, StringSplitOptions.None);
previousStates.Add(kv[0], kv[1]);
}
(追伸:エラー処理とデータ変換を実行するライブラリ関数がありますが、ここでは簡潔にするために省略しています)。
Laramie によって提案され、gleapman によって改良されたソリューションの VB.Net コードを次に示します。
アップデート: 以下に投稿したコードは、実際には ListBox コントロール用です。継承を DropDownList に変更し、クラスの名前を変更するだけです。
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Security.Permissions
Imports System.Linq
Imports System.Text
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace CustomControls
<DefaultProperty("Text")> _
<ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")>
Public Class PersistentListBox
Inherits ListBox
<Bindable(True)> _
<Category("Appearance")> _
<DefaultValue("")> _
<Localizable(True)> _
Protected Overrides Function SaveViewState() As Object
' Create object array for Item count + 1
Dim allStates As Object() = New Object(Me.Items.Count + 1) {}
' The +1 is to hold the base info
Dim baseState As Object = MyBase.SaveViewState()
allStates(0) = baseState
Dim i As Int32 = 1
' Now loop through and save each attribute for the List
For Each li As ListItem In Me.Items
Dim j As Int32 = 0
Dim attributes As String()() = New String(li.Attributes.Count - 1)() {}
For Each attribute As String In li.Attributes.Keys
attributes(j) = New String() {attribute, li.Attributes(attribute)}
j += 1
Next
allStates(i) = attributes
i += 1
Next
Return allStates
End Function
Protected Overrides Sub LoadViewState(savedState As Object)
If savedState IsNot Nothing Then
Dim myState As Object() = DirectCast(savedState, Object())
' Restore base first
If myState(0) IsNot Nothing Then
MyBase.LoadViewState(myState(0))
End If
Dim i As Int32 = 1
For Each li As ListItem In Me.Items
' Loop through and restore each attribute
' NOTE: Ignore the first item as that is the base state and is represented by a Triplet struct
For Each attribute As String() In DirectCast(myState(i), String()())
li.Attributes(attribute(0)) = attribute(1)
i += 1
Next
Next
End If
End Sub
End Class
End Namespace
@Sujay あなたは(「;」)(CSV形式など)のドロップダウンのvalue属性にセミコロン区切りのテキストを追加して、のstring.Splitを使用することができますつの値のうちの2「値」を取得するには、ないと逃げるための回避策として、ユーザーコントロールを新たに作成しました。あなただけのいくつかの余分な属性を持っており、それはあまりにも長くない場合場合は特に。また、ドロップダウンのvalue属性にJSON値を使用して、あなたがそこから必要なものは何でも出て解析することができます。
//In the same block where the ddl is loaded (assuming the dataview is retrieved whether postback or not), search for the listitem and re-apply the attribute
if(IsPostBack)
foreach (DataRow dr in dvFacility.Table.Rows)
{
//search the listitem
ListItem li = ddl_FacilityFilter.Items.FindByValue(dr["FACILITY_CD"].ToString());
if (li!=null)
{
li.Attributes.Add("Title", dr["Facility_Description"].ToString());
}
} //end for each
ViewStateはなく、簡単な解決策は、新しいサーバーコントロールやなめらか複合体を作成:
の作成:
public void AddItemList(DropDownList list, string text, string value, string group = null, string type = null)
{
var item = new ListItem(text, value);
if (!string.IsNullOrEmpty(group))
{
if (string.IsNullOrEmpty(type)) type = "group";
item.Attributes["data-" + type] = group;
}
list.Items.Add(item);
}
アップデートます:
public void ChangeItemList(DropDownList list, string eq, string group = null, string type = null)
{
var listItem = list.Items.Cast<ListItem>().First(item => item.Value == eq);
if (!string.IsNullOrEmpty(group))
{
if (string.IsNullOrEmpty(type)) type = "group";
listItem.Attributes["data-" + type] = group;
}
}
例:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
using (var context = new WOContext())
{
context.Report_Types.ToList().ForEach(types => AddItemList(DropDownList1, types.Name, types.ID.ToString(), types.ReportBaseTypes.Name));
DropDownList1.DataBind();
}
}
else
{
using (var context = new WOContext())
{
context.Report_Types.ToList().ForEach(types => ChangeItemList(DropDownList1, types.ID.ToString(), types.ReportBaseTypes.Name));
}
}
}
私は私の場合には、セッション変数を使用して私のリストは、それはかなりうまく機能して、多くの要素を含んでするつもりはないことを達成するために管理し、これは私がそれをやった方法です。
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string[] elems;//Array with values to add to the list
for (int q = 0; q < elems.Length; q++)
{
ListItem li = new ListItem() { Value = "text", Text = "text" };
li.Attributes["data-image"] = elems[q];
myList.Items.Add(li);
HttpContext.Current.Session.Add("attr" + q, elems[q]);
}
}
else
{
for (int o = 0; o < webmenu.Items.Count; o++)
{
myList.Items[o].Attributes["data-image"] = HttpContext.Current.Session["attr" + o].ToString();
}
}
}
ページが最初にロードされると、私は1つのセッション変数「attrの」プラス数のを作成リストが移入されると、私は一度、私はその属性を持つ要素を追加するよう:(ポストバック後に失われている画像の属性を追加ポストバックが発生したときに(、サイクル「のために」から取られた要素は、(それがattr0、ATTR1、ATTR2、等...のようになります)し、それらに私は(私の場合には、画像へのパス)属性の値を保存します)「他」私はちょうどループのリストと、このページでは私が行うため、ページがロードされたときと同じである「for」ループの「INT」を使用して、セッション変数から取られた属性を追加します(これは内部ちょうど彼らが常に同じインデックスを持っているので、選択リストに要素を追加)と属性が再設定されていない、私が挨拶!
、これは将来的に誰かを役に立てば幸い