71

同僚が私にこれを見せてくれました:

彼は、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;
    }

ページの読み込み時にアイテムのツールチップが表示されますが、最初のポストバックで属性が失われます。これはなぜですか?また、回避策はありますか?

4

11 に答える 11

73

私は同じ問題を抱えており、作成者が継承された ListItem Consumer を作成して属性を ViewState に保持するこのリソースに貢献したいと考えました。うまくいけば、私がそれにつまずくまで私が無駄にした時間を誰かが救うでしょう.

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];
            }
        }
    }
}
于 2010-06-23T07:35:34.113 に答える
40

ありがとう、ララミー。ちょうど私が探していたもの。属性を完全に保持します。

拡大すると、以下は、VS2008 でドロップダウンリストを作成するために Laramie のコードを使用して作成したクラス ファイルです。App_Code フォルダーにクラスを作成します。クラスを作成したら、aspx ページで次の行を使用して登録します。

<%@ Register TagPrefix="aspNewControls" Namespace="NewControls"%>

次に、これを使用してWebフォームにコントロールを配置できます

<aspNewControls:NewDropDownList ID="ddlWhatever" runat="server">
                                                </aspNewControls:NewDropDownList>

さて、これがクラスです...

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];
                }
            }
        }
    }
  }
}
于 2011-05-10T21:58:21.760 に答える
15

簡単な解決策は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");
        }
于 2012-10-25T15:43:58.887 に答える
8

ページの最初の読み込み時にリスト項目のみを読み込みたい場合は、ViewState を有効にして、コントロールがそこで状態をシリアル化し、ページがポストバックしたときにそれを再読み込みできるようにする必要があります。

ViewState を有効にできる場所がいくつかあります。web.config の<pages/>ノードと、プロパティ<%@ page %>の aspx ファイル自体の上部にあるディレクティブも確認してください。この設定は、ViewState が機能するためEnableViewStateに必要です。true

ViewState を使用したくない場合は、if (!IsPostBack) { ... }を追加するコードの周囲から を削除するだけListItemsで、ポストバックごとに項目が再作成されます。

編集:申し訳ありません-あなたの質問を読み違えました。ViewState でシリアル化されていないため、属性がポストバックに耐えられないことは正しいです。ポストバックごとにこれらの属性を再度追加する必要があります。

于 2009-08-21T18:12:42.277 に答える
6

簡単な解決策の 1 つ - ポストバックを要求するクリック イベントでドロップ ダウン読み込み関数を呼び出します。

于 2011-07-12T12:59:29.210 に答える
3

これは、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 = 0
            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
                i += 1
                For Each attribute As String() In DirectCast(myState(i), String()())
                    li.Attributes(attribute(0)) = attribute(1)
                Next
            Next
        End If
    End Sub
End Class
End Namespace
于 2016-03-11T21:58:34.730 に答える
2

この問題の典型的な解決策は、通常の状況では実現不可能な新しいコントロールを作成することです。この問題には、簡単で簡単な解決策があります。

問題は、ListItemポストバックで属性が失われることです。ただし、リスト自体がカスタム属性を失うことはありません。このように、シンプルかつ効果的な方法でこれを利用できます。

手順:

  1. 上記の回答のコードを使用して属性をシリアル化します ( https://stackoverflow.com/a/3099755/3624833 )

  2. ListControl のカスタム属性 (ドロップダウン リスト、チェックリスト ボックスなど) に格納します。

  3. ポストバック時に、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]);
}

(PS: エラー処理とデータ変換を実行するライブラリ funcs がありますが、簡潔にするためにここでは省略しています)。

于 2015-01-06T03:53:22.797 に答える
1

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));
        }
    }
}
于 2015-11-11T19:05:20.380 に答える
0
    //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  
于 2013-07-12T15:00:37.367 に答える