我创建了一个自定义控件(称为BoostrapDropDown),该控件实际上将一堆boostrap标记包装在asp.net DropDownList周围。我正在从控件中“传递” DataSource属性到嵌套的DropDownList,但是回发时,我丢失了所有值。
这是令人尴尬的部分。一个月前,我在网上搜索并能够创建解决方案,但是我没有很好地记录下来。现在,我找不到用于创建解决方案的页面。现在我已经知道它是如何工作的,我希望有人能有所启发。下面是相关的源代码。
public class BootstrapDropDown : BootstrapInputBase
{
private DropDownList inputControl;
public string DataTextField
{
get => (string)ViewState[ nameof( DataTextField ) ];
set => ViewState[ nameof( DataTextField ) ] = value;
}
public string DataValueField
{
get => (string)ViewState[ nameof( DataValueField ) ];
set => ViewState[ nameof( DataValueField ) ] = value;
}
public object DataSource { get; set; }
ListItemCollection items;
public virtual ListItemCollection Items
{
get
{
if ( items == null )
{
items = new ListItemCollection();
if ( IsTrackingViewState )
{
( (IStateManager)items ).TrackViewState();
}
}
return items;
}
}
protected override void LoadViewState( object savedState )
{
var triplet = (Triplet)savedState;
Value = (string)triplet.Third;
( (IStateManager)Items ).LoadViewState( triplet.Second );
base.LoadViewState( triplet.First );
}
protected override object SaveViewState()
{
var triplet = new Triplet();
triplet.First = base.SaveViewState();
triplet.Second = ( (IStateManager)Items ).SaveViewState();
triplet.Third = Value;
return triplet;
}
public override string Value
{
get
{
EnsureChildControls();
return inputControl.SelectedValue;
}
set
{
EnsureChildControls();
inputControl.SelectedValue = value;
}
}
public virtual string Text
{
get
{
EnsureChildControls();
return inputControl.SelectedItem?.Text;
}
}
protected override void CreateChildControls()
{
/* Added other html markup controls */
var validatorContainer = new HtmlGenericControl( "div" );
validatorContainer.Attributes[ "class" ] = "validator-container";
inputControl = new DropDownList() { CssClass = "form-control selectpicker show-tick " + ID, ID = ID, DataValueField = DataValueField, DataTextField = DataTextField, DataSource = DataSource };
inputControl.Attributes[ "data-size" ] = "15";
inputControl.Attributes[ "data-live-search" ] = "true";
validatorContainer.Controls.Add( inputControl );
if ( DataSource != null )
{
inputControl.DataBind();
Items.AddRange( inputControl.Items.Cast<ListItem>().ToArray() );
}
validatorContainer.Controls.Add( CreateErrorMessage() );
Controls.Add( validatorContainer );
/* Added other html markup controls */
}
}
该控件通过以下方式在标记中使用:
<mh:BootstrapDropDown runat="server" ID="iGroup" Label="Select Group Name" EnableViewState="true" DataSource='<%# Groups %>' DataTextField="Text" DataValueField="Value" />
这是我的困惑...
CreateChildControls
期间,DataSource
仅存在于原始渲染中。因此,我在嵌套的DropDownList上调用DataBind
,以使其首次填充,然后将所有控件Items存储回Items
属性。Items
是如何持久保存到ViewState或从ViewState加载的。Items
属性又如何习惯于重新填充DropDownList?我以为可能是因为我添加了Load\SaveViewState
(称为base.Load\SaveViewState
)才真正解决了我的问题,但是当我注释掉对我的Items
属性的所有引用时,再次失去了下拉列表值。 Items
在回发时如何重新填充inputControl.Items
?
答案 0 :(得分:1)
我知道最终的问题是:
Items在世界上如何重新填充inputControl.Items 回发?!
尽管如此,我认为这是一个不需要(或不应)回答的问题,其原因有两个:
您的初始要求声明:
我创建了一个自定义控件, 在asp.net DropDownList周围包裹一堆boostrap标记。
您的代码(我指的是您的代码的原始版本,对于我们的讨论来说足够好并且足够长)这一事实包含了许多技术,这些技术与将复杂类型的自定义控件属性持久保存在ViewState(SaveViewState
,Triplet
,IStateManager
,BootstrapDropDown
等),但大多数是 not 在您的情况下是必需的,因为(此时,您的要求声明变得至关重要)
DropDownList
只是一个嵌入Text
的复合自定义控件,它可以(并且应该)委托对其起作用!
实际上,您已经为Value
和Items
属性很好地做到了。为什么也对ListItemCollection
属性不这样做?您的控件通过组成起作用。它不需要维护自己的inputControl
,更不用说在ViewState中传递它了。
最后但并非最不重要的一点是,记住嵌入式服务器控件将自动管理它们自己的ViewState,这一点非常重要。换句话说,您无需手动管理public class BootstrapDropDown : WebControl, INamingContainer
{
private DropDownList inputControl;
public string DataTextField
{
get => (string)ViewState[nameof(DataTextField)];
set => ViewState[nameof(DataTextField)] = value;
}
public string DataValueField
{
get => (string)ViewState[nameof(DataValueField)];
set => ViewState[nameof(DataValueField)] = value;
}
public IEnumerable DataSource { get; set; }
public virtual ListItemCollection Items
{
get
{
EnsureChildControls();
return inputControl.Items;
}
}
public virtual string Value
{
get
{
EnsureChildControls();
return inputControl.SelectedValue;
}
set
{
EnsureChildControls();
inputControl.SelectedValue = value;
}
}
public virtual string Text
{
get
{
EnsureChildControls();
return inputControl.SelectedItem?.Text;
}
}
protected override void CreateChildControls()
{
/* Added other html markup controls described above */
var validatorContainer = new HtmlGenericControl("div");
validatorContainer.Attributes["class"] = "validator-container";
inputControl = new DropDownList() {
CssClass = "form-control selectpicker show-tick " + ID,
ID = ID,
DataValueField = DataValueField,
DataTextField = DataTextField,
DataSource = DataSource
};
inputControl.Attributes["data-size"] = "15";
inputControl.Attributes["data-live-search"] = "true";
validatorContainer.Controls.Add(inputControl);
Controls.Add(validatorContainer);
if (DataSource != null)
{
inputControl.DataBind();
}
/* Added other html markup controls described */
}
}
的ViewState。
话虽如此,这是一个基于您(原始)代码的示例,该示例可以在不使用黑魔法的情况下工作:
<mh:BootstrapDropDown
runat="server"
ID="iGroup"
Label="Select Group Name"
DataSource='<%# Groups %>'
DataTextField="Text"
DataValueField="Value" />
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" /><br />
<asp:Label ID="Label1" runat="server" Text=""></asp:Label><br />
<asp:Label ID="Label2" runat="server" Text=""></asp:Label>
ASPX :
protected System.Collections.ArrayList Groups
{
get
{
var al = new System.Collections.ArrayList();
al.Add(new ListItem("[Select a Group]", ""));
al.Add(new ListItem("Group A", "A"));
al.Add(new ListItem("Group B", "B"));
return al;
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DataBind();
}
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = iGroup.Text;
Label2.Text = iGroup.Value;
}
背后的代码:
inputControl
还有最后一件事值得一提。请注意,将Controls
添加到IStateManager
集合中后,将其绑定到数据中。这很重要,因为向集合添加控件也是控件开始跟踪其ViewState的关键。您可以在这篇出色的文章中了解更多(或全部)内容:
https://weblogs.asp.net/infinitiesloop/Truly-Understanding-Viewstate
此外,我在Dino Esposito的这篇文章中找到了对{{1}}机制的参考:
https://www.itprotoday.com/web-application-management/inside-aspnet-control-properties