- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我创建了一个自定义控件(称为 BoostrapDropDown),它基本上将一堆 boostrap 标记包装在 asp.net DropDownList 周围。生成的控件层次结构基本上如下所示,除了 DropDownList 之外,所有内容都是 HtmlGenericControl:
<div class="form-group viInputID">
<label for="iInputID" class="control-label liInputID"></label>
<a style="display: none;" class="vhiInputID" role="button" tabindex="0" data-toggle="popover" data-trigger="click" data-content-selector=".hiInputID" data-placement="top">
<span class="glyphicon glyphicon-info-sign help-icon"></span>
</a>
<a style="display: none;" class="vsiInputID" role="button" tabindex="0">
<span class="glyphicon glyphicon-volume-up"></span>
</a>
<div class="validator-container">
<asp:DropDownList CssClass="form-control selectpicker show-tick iInputID" data-size="15" ID="iInputID" runat="server" DataSource='<%# DataSource %>' DataTextField="name" DataValueField="key"/>
<span class="error-msg" data-toggle="tooltip" data-placement="top"></span>
</div>
<div class="hiInputIDTitle" style="display: none;"></div>
<div class="hiInputID" style="display: none;"></div>
</div>
我正在将一个 DataSource 属性从我的控件“传递”到嵌套的 DropDownList,但在回发时,我失去了所有值。
这是尴尬的部分。一个月前,我在网上搜索并能够创建一个解决方案,但我没有很好地记录下来。现在我找不到用于创建解决方案的页面。我不知道它是如何工作的,我希望有人能阐明一些问题。下面是相关的源代码。
更新:完整代码
// Preventing the EventValidation for dropdown lists b/c they could be populated *only* on the client side;
// https://stackoverflow.com/a/8581311/166231
public class DynamicDropDownList : DropDownList { }
public class DynamicListBox : ListBox { }
public class HtmlGenericControlWithCss : HtmlGenericControl
{
public HtmlGenericControlWithCss(string tag) : base(tag) { }
public HtmlGenericControlWithCss(string tag, string css) : this(tag)
{
Attributes["class"] = css;
}
public HtmlGenericControlWithCss(string tag, string css, string style) : this(tag, css)
{
Attributes["style"] = style;
}
}
public class HtmlAnchorWithCss : HtmlAnchor
{
public HtmlAnchorWithCss(string css) : base()
{
Attributes["class"] = css;
}
public HtmlAnchorWithCss(string css, string style) : this(css)
{
Attributes["style"] = style;
}
}
public abstract class BootstrapInputBase : WebControl, INamingContainer
{
protected HtmlGenericControl formGroup;
protected bool isBootstrap4;
public string HelpPlacement
{
get => (string)ViewState["HelpPlacement"] ?? "top";
set => ViewState["HelpPlacement"] = value;
}
public string Label
{
get => (string)ViewState[nameof(Label)];
set => ViewState[nameof(Label)] = value;
}
public string LabelCss
{
get => (string)ViewState[nameof(LabelCss)];
set => ViewState[nameof(LabelCss)] = value;
}
public string HelpContent
{
get => (string)ViewState[nameof(HelpContent)];
set => ViewState[nameof(HelpContent)] = value;
}
public override void RenderControl(HtmlTextWriter writer)
{
using (var sw = new StringWriter())
using (var hw = new HtmlTextWriter(sw))
{
base.RenderControl(hw);
// need formatted so browser renders it nice (otherwise wierd spacing issues if some of the whitespace is removed)
var html = XElement.Parse(sw.ToString());
writer.Write(html.ToString());
}
}
public void AddControl(Control control)
{
EnsureChildControls();
formGroup.Controls.Add(control);
}
protected override void CreateChildControls()
{
isBootstrap4 = true;
/*
<div class="form-group viInputID">
<label for="iInputID" class="control-label liInputID"></label>
<a style="display: none;" class="vhiInputID" role="button" tabindex="0" data-toggle="popover" data-trigger="click" data-content-selector=".hiInputID" data-placement="top">
<span class="glyphicon glyphicon-info-sign help-icon"></span>
</a>
<a style="display: none;" class="vsiInputID" role="button" tabindex="0">
<span class="glyphicon glyphicon-volume-up"></span>
</a>
<div class="validator-container"> [abstract] </div>
<div class="hiInputIDTitle" style="display: none;"></div>
<div class="hiInputID" style="display: none;"></div>
</div>
*/
formGroup = new HtmlGenericControlWithCss("div", "form-group v" + ID);
Controls.Add(formGroup);
formGroup.Controls.Add(CreateLabel());
var help = new HtmlAnchorWithCss("vh" + ID, string.IsNullOrEmpty(HelpContent) ? "display: none;" : null);
help.Attributes["role"] = "button";
help.Attributes["tabindex"] = "0";
help.Attributes["data-toggle"] = "popover";
help.Attributes["data-trigger"] = "click";
help.Attributes["data-content-selector"] = ".h" + ID;
help.Attributes["data-placement"] = HelpPlacement;
// Couldn't use server controls b/c it put <a><span .../></a> with no space, if newline before span, then HTML rendered a little break after the label
// help.InnerHtml = Environment.NewLine + "<span class='glyphicon glyphicon-info-sign help-icon'></span>";
formGroup.Controls.Add(help);
help.Controls.Add(new HtmlGenericControlWithCss("span", isBootstrap4 ? "fal fa-question-circle help-icon" : "glyphicon glyphicon-info-sign help-icon"));
var voice = new HtmlAnchorWithCss("vs" + ID, "display: none;");
voice.Attributes["role"] = "button";
voice.Attributes["tabindex"] = "0";
// Couldn't use server controls b/c it put <a><span .../></a> with no space, if newline before span, then HTML rendered a little break after the label
// voice.InnerHtml = Environment.NewLine + "<span class='glyphicon glyphicon-volume-up'></span>";
formGroup.Controls.Add(voice);
voice.Controls.Add(new HtmlGenericControlWithCss("span", isBootstrap4 ? "fal fa-volume-up" : "glyphicon glyphicon-volume-up"));
formGroup.Controls.Add(CreateValidatorContainer());
formGroup.Controls.Add(new HtmlGenericControlWithCss("div", "h" + ID, "display: none;") { InnerHtml = HelpContent });
formGroup.Controls.Add(new HtmlGenericControlWithCss("div", "h" + ID + "Title", "display: none;"));
}
protected abstract HtmlGenericControl CreateValidatorContainer();
public abstract string Value { get; set; }
protected virtual HtmlGenericControl CreateLabel()
{
var label = new HtmlGenericControlWithCss("label", "control-label l" + ID + (!string.IsNullOrEmpty(LabelCss) ? " " + LabelCss : "")) { InnerHtml = Label, EnableViewState = true };
label.Attributes["for"] = ID;
return label;
}
protected virtual HtmlGenericControl CreateErrorMessage()
{
var errorMessage = new HtmlGenericControlWithCss("span", "error-msg");
errorMessage.Attributes["data-toggle"] = "tooltip";
errorMessage.Attributes["data-placement"] = "top auto";
return errorMessage;
}
}
public class BootstrapDropDown : BootstrapInputBase
{
private ListControl inputControl;
// If this is false and the client wants to postback to the server for processing,
// I would need to try to grab values via Request.Form[ UniqueID + ":" + ID ].
// But the CalcEngine would *have* to validate the item is inside a known list and
// no malicious values were posted back to server.
public bool SupportEventValidation
{
get => (bool?)ViewState[nameof(SupportEventValidation)] ?? true;
set => ViewState[nameof(SupportEventValidation)] = value;
}
public bool AllowMultiSelect
{
get => (bool?)ViewState[nameof(AllowMultiSelect)] ?? false;
set => ViewState[nameof(AllowMultiSelect)] = value;
}
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;
}
}
public ListControl ListControl
{
get
{
// Don't want this, would like to just use Items property
// to clear/add items but wasn't working and I still don't understand
// how my dropdown list is retaining view state. SO Question:
// https://stackoverflow.com/questions/56299350/saving-viewstate-in-nested-dropdownlist-in-a-custom-control
EnsureChildControls();
return inputControl;
}
}
protected override void LoadViewState(object savedState)
{
var allState = (object[])savedState;
HelpContent = (string)allState[4];
Label = (string)allState[3];
Value = (string)allState[2];
((IStateManager)Items).LoadViewState(allState[1]);
base.LoadViewState(allState[0]);
}
protected override object SaveViewState()
{
var allState = new object[5];
allState[0] = base.SaveViewState();
allState[1] = ((IStateManager)Items).SaveViewState();
allState[2] = Value;
allState[3] = Label;
allState[4] = HelpContent;
return allState;
}
public override string Value
{
get
{
EnsureChildControls();
return inputControl.SelectedValue;
}
set
{
EnsureChildControls();
inputControl.SelectedValue = value;
}
}
public string SelectedValue => Value;
public virtual string Text
{
get
{
EnsureChildControls();
return inputControl.SelectedItem?.Text;
}
}
protected override HtmlGenericControl CreateValidatorContainer()
{
/*
<div class="validator-container">
<asp:DropDownList CssClass="form-control selectpicker show-tick iInputID" data-size="15" ID="iInputID" runat="server" DataSource='<%# xDSHelper.GetDataTable( "TableTaxStatus" ) %>' DataTextField="name" DataValueField="key"/>
<span class="error-msg" data-toggle="tooltip" data-placement="top"></span>
</div>
*/
var validatorContainer = new HtmlGenericControlWithCss("div", "validator-container");
inputControl = SupportEventValidation
? AllowMultiSelect
? new ListBox() { CssClass = "form-control selectpicker show-tick " + ID, ID = ID, DataValueField = DataValueField, DataTextField = DataTextField, DataSource = DataSource, SelectionMode = ListSelectionMode.Multiple } as ListControl
: new DropDownList() { CssClass = "form-control selectpicker show-tick " + ID, ID = ID, DataValueField = DataValueField, DataTextField = DataTextField, DataSource = DataSource } as ListControl
: AllowMultiSelect
? new DynamicListBox() { CssClass = "form-control selectpicker show-tick " + ID, ID = ID, DataValueField = DataValueField, DataTextField = DataTextField, DataSource = DataSource, SelectionMode = ListSelectionMode.Multiple } as ListControl
: new DynamicDropDownList() { CssClass = "form-control selectpicker show-tick " + ID, ID = ID, DataValueField = DataValueField, DataTextField = DataTextField, DataSource = DataSource } as ListControl;
inputControl.Attributes["data-size"] = "15";
if (AllowMultiSelect)
{
inputControl.Attributes["data-selected-text-format"] = "count > 2";
}
else
{
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());
return validatorContainer;
}
}
并且通过以下方式在标记中使用该控件:
<mh:BootstrapDropDown runat="server" ID="iGroup" Label="Select Group Name" EnableViewState="true" DataSource='<%# Groups %>' DataTextField="Text" DataValueField="Value" />
然后在后面的代码中,有以下内容:
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;
}
}
所以这是我的困惑......
CreateChildControls
期间,DataSource
只会出现在原始渲染中。因此,我在嵌套的 DropDownList 上调用 DataBind
以使其第一次填充,然后我将所有控件 Items 存储回 Items
属性。Items
是如何保存到 ViewState 或从 ViewState 加载的。Items
属性是如何被用来重新填充 DropDownList 的?我在想可能是因为我添加了 Load\SaveViewState
(称为 base.Load\SaveViewState
)才真正解决了我的问题,但是当我注释掉时所有对我的 Items
属性的引用,我再次丢失了下拉列表值。Items
是如何在回发时重新填充 inputControl.Items
的?!
最佳答案
我明白最终的问题是:
How in the world is Items repopulating inputControl.Items on postback?!
不过,我认为这个问题不需要(或不应该)回答,原因有二:
您的初始需求声明:
I created a custom control that essentially wraps a bunch of boostrap markup around a asp.net DropDownList.
事实上,您的代码(我指的是您代码的原始版本,对于我们的讨论来说足够好且足够长)包含许多技术,这些技术与持久化复杂类型的自定义控件属性有关ViewState(LoadViewState
、SaveViewState
、Triplet
、IStateManager
等)但其中大部分是 在您的情况下不需要,因为(此时您的需求声明变得至关重要):
BootstrapDropDown
只是一个复合自定义控件,它嵌入了一个 DropDownList
并且可以(并且应该)委派所有工作给它!
事实上,您已经很好地为 Text
和 Value
属性完成了该操作。为什么不对 Items
属性也这样做呢?您的控件通过组合 工作。它不需要维护自己的 ListItemCollection
,更不用说在 ViewState 中传递它了。
最后但同样重要的是,记住嵌入式服务器控件将自动管理它们自己的 ViewState 非常重要。换句话说,您无需手动管理 inputControl
的 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 */
}
}
ASPX:
<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>
代码隐藏:
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
集合后被数据绑定(bind)。这很重要,因为将控件添加到集合也是控件开始跟踪其 ViewState 的点。您可以在这篇优秀的文章中阅读更多(或全部)相关内容:
https://weblogs.asp.net/infinitiesloop/Truly-Understanding-Viewstate
此外,我在 Dino Esposito 的这篇文章中找到了对 IStateManager
机制的引用:
https://www.itprotoday.com/web-application-management/inside-aspnet-control-properties
关于c# - 在自定义控件的嵌套 DropDownList 中保存 ViewState,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56299350/
我想实现自定义搜索,但遇到了一个麻烦。我需要将 UIButton、SearchBar 组合在一个控件中,以便我可以通过指针引用它。然后我将向该组合控件动态添加更多 UIbutton。最重要的是,我想将
它没有在我的方法中执行 if block 中的语句 母版页:- 页面加载事件:- Control c = new Control(); DoSomething(c); 我的方法:- protecte
ComboBox 控件有一个 setConverter 方法,请参阅 JavaFX ComboBox - Display text but return ID on selection举个例子。我正在
我没有找到任何包含用于标记化(标记)文本输入的控件的 wpf 库。也许我找不到库,因为我错误地调用了这个组件。怎么叫或者哪里有这样的库? 最佳答案 DevExpress WPF 库包含多个数据编辑控件
是否有 Silverlight 控件允许您输入文本并将其突出显示为代码? 例如: foreach (client in Clients){ client.Save();} would become
我有以下用户控件 a) Panel.ZIndex="99999999" 是否是将此控件设置为该控件中 TopMost 的正
是否可以在 Windows 窗体中使用 C# 在窗体加载时隐藏所有特定控件,例如标签或按钮,然后选择显示我不想显示的那些? 我有一个包含很多按钮和标签的程序,但我只想在加载时显示一两个,我觉得对每个标
这个问题已经有答案了: 已关闭11 年前。 Possible Duplicate: Duplicating components at Run-Time 我有一个TMyControl ( Contro
我正在尝试在 Delphi 中编写一个 dll 库,其中包含一个创建 TFrame 后代实例并返回它的函数。但是当我在应用程序中导入这个函数时,每次调用它时,我都会得到一个异常,例如“'xxx'控件没
是否有 Win32 API 调用来确定哪些窗口和/或控件在特定坐标和/或鼠标下可见? 最佳答案 您可以使用GetWindowFromPoint 。它将返回窗口句柄,以便您可以使用 GetClassNa
我需要在编辑控件中输入以下公式: 公式是在 MS Word 中制作的。尝试将其复制/粘贴到编辑控件(单行或多行)后,我得到 M 0.33 Q10T9.1-9.7。 当我输入这个时,我正在研究 Rich
我只是想成功地将它添加到我的窗口中,但这却出奇地困难。 我已经尝试过 #include "windef.h" #include "winbase.h" #include "initguid.h" #i
我希望能够使用 google maps api v3 拥有自己的“街景”按钮。单击按钮时,我希望它根据我的标记经纬度加载街景。然后我希望按钮更改为“返回 map ”,然后再次加载默认 map View
我目前正在用 PHP 开发(另一个)开源 CMS,我想使用 javascript 控件,尤其是管理面板。问题是,是否有任何具有 PHP 接口(interface)的开源、可自由分发的控件(用于创建 j
我为其编写软件的产品之一是会计类应用程序。它用 C++ 编写,使用 C++ Builder 和 VCL 控件,连接到运行在 Linux 上的 PostgreSQL 数据库。 PostgreSQL 数据
我使用 Key Listener 来读取用户的输入,但我遇到了问题。首先,我读到 JTextField“请输入您的姓名”。如果用户输入一个名字,例如 John,它将更改为 John。但是,如果用户输入
我正在尝试对齐数据表列中的复选框(h=center,v=middle) ... 但复选框仍然显示在错误的位置(见附图)
我有一个包含统计信息的 JSON 数据树: { prefix: "a", count: 20, children: [ { prefix: "a:b", c
我在 Photoshop 中设计了一个模型,我打算将它应用到我的产品目录的 ListView 控件中,但它似乎没有正确显示(未对齐?),我希望这里的人可以像我一样指出我的错误试图弄清楚无济于事。 预期
您是使用 ASP.NET 控件还是仅使用带有 CSS 的 HTML? 我在 TextBox 和 DropDownList 的宽度方面遇到了一些问题。在不同的浏览器中,宽度会有所不同,控件的大小也不会相
我是一名优秀的程序员,十分优秀!