Listitems يتم فقد السمات في القائمة المنسدلة على عون البريد؟
-
19-09-2019 - |
سؤال
أظهر لي زميل العمل هذا:
لديه قائمة هبوطية وزر على صفحة ويب. إليك الرمز وراء:
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;
}
على تحميل الصفحة، تظهر تلميح الأدوات "العناصر"، ولكن على إعادة النشر الأولى، يتم فقد السمات. لماذا هذا هو الحال، وهل هناك أي حلول؟
المحلول
كان لدي نفس المشكلة وأراد المساهمة هذه المورد حيث أنشأ المؤلف مستهلك قائمة Listitem الموروثة للسمات الثابتة لعرضه. نأمل أن ينقذ شخص ما الوقت الذي أهدرته حتى تعثرت عليه.
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];
}
}
}
}
نصائح أخرى
شكرا، لارامي. فقط ما كنت أبحث عنه. إنه يحتفظ السمات تماما.
للتوسع، أدناه ملف فئة قمت بإنشائه باستخدام رمز Laramie لإنشاء قائمة هبوطية في VS2008. قم بإنشاء الفئة في مجلد App_Code. بعد إنشاء الفصل، استخدم هذا السطر في صفحة ASPX لتسجيله:
<%@ Register TagPrefix="aspNewControls" Namespace="NewControls"%>
يمكنك بعد ذلك وضع عنصر التحكم على شبكة الإنترنت الخاصة بك مع هذا
<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];
}
}
}
}
}
}
الحل البسيط هو إضافة سمات Tooltip في 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");
}
إذا كنت ترغب فقط في تحميل Listitems في الحمل الأول من الصفحة، فستحتاج إلى تمكين ViewState بحيث يمكن للرقابة تسلسل حالته هناك وإعادة تحميله عند عودة الصفحة.
هناك عدة أماكن حيث يمكن تمكين ViewState - تحقق من <pages/>
عقدة في web.config وأيضا في <%@ page %>
التوجيه في الجزء العلوي من ملف ASPX نفسه ل EnableViewState
خاصية. سيحتاج هذا الإعداد إلى أن يكون true
للعرض للعمل.
إذا كنت لا ترغب في استخدام ViewState، فما عليك سوى إزالة if (!IsPostBack) { ... }
من حول الكود الذي يضيف ListItems
وسيتم إعادة إنشاء العناصر في كل عملية عاجة.
يحرر: أعتذر - أنا أساء قراءة سؤالك. أنت صحيح أن صفات لا تبدي البقاء على قيد الحياة لأنها غير متسلسلة في ViewState. يجب عليك إعادة إضافة هذه الصفات على كل عملية عاجة.
حل واحد بسيط - استدعاء وظيفة التحميل المنسدلة الخاصة بك في أحداث click حيث تطلب إلى الوراء.
تتضمن الحلول النموذجية لهذه المشكلة إنشاء عناصر تحكم جديدة غير ممكنة تماما في الظروف العادية. هناك حل بسيط ولكنه تافه لهذه المشكلة.
القضية هي أن ListItem
يفقد سماتها على إعادة النشر. ومع ذلك، فإن القائمة نفسها لا تفقد أي سمات مخصصة. يمكن للمرء الاستفادة من ذلك بطريقة بسيطة ولكنها فعالة وبالتالي.
خطوات:
تسلسل سماتك باستخدام الرمز في الإجابة أعلاه (https://stackoverflow.com/a/3099755/362483.)
قم بتخزينها إلى سمة مخصصة ل ListControl (القائمة المنسدلة، مربع الاختيار، أيا كان).
في Post Back، اقرأ السمة المخصصة من ListControl ثم تراجعه مرة أخرى كسمات.
هنا هو الرمز الذي اعتدت عليه (DE) سمات التسلسل (ما كنت بحاجة إليه هو متابعة العناصر التي تم تقديمها في الأصل كما تم تحديدها عند الاسترجاع من الخلفية ثم حفظ الصفوف أو حذفها وفقا للتغييرات التي تم إجراؤها حسب التغييرات المستخدم على 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]);
}
(ملاحظة: لدي مكتبة تقوم بتنفيذ الأخطاء وتحويلات البيانات، حذف نفسه هنا للإيجاز).
إليك قانون VB.NET للحل الذي اقترحه لارامي ورفضه Glapman.
تحديث: الرمز الذي نشرته أدناه هو في الواقع لعنصر التحكم ListBox. فقط قم بتغيير الميراث إلى القائمة المنسدلة وإعادة تسمية الفصل.
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)، واستخدام string.split ('؛') للحصول على 2 "قيم" من القيمة واحدة، كحل بديل للابتعاد مع عدم الاضطرار إلى إنشاء عنصر تحكم مستخدم من جديد. خاصة إذا كان لديك سوى عدد قليل من السمات الإضافية، وإذا لم يكن طويلا. يمكنك أيضا استخدام قيمة 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، إنشاء عنصر تحكم خادم جديد أو SMTH مجمع:
خلق:
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();
}
}
}
عندما يتم تحميل الصفحة في المرة الأولى، يتم ملء القائمة، وأضيف سمة صورة ضائعة بعد إعادة النشر :( لذلك في الوقت الذي أضيف العناصر بسماته، أقوم بإنشاء مخطط جلسة واحدة "أسرة" بالإضافة إلى عدد العنصر الذي اتخذته من دورة "ل" (ستكون مثل Atte0، Attr1، Atte2، ETC ...) وفيها أحفظ قيمة السمة (المسار إلى صورة في حالتي)، عند حدوث عملية إعادة النشر (داخل " آخر ") أنا فقط حلقة القائمة وإضافة السمة المأخوذة من متغير الجلسة باستخدام" INT "لحلقة" for "وهذا هو نفسه عندما تم تحميل الصفحة (هذا لأنه في هذه الصفحة لا أقوم بإضافة عناصر إلى القائمة فقط اختيار حتى يكون لديهم دائما نفس الفهرس) ويتم تعيين السمات مرة أخرى، آمل أن يساعد هذا شخصا في المستقبل، تحياتي!