[英]Custom server control sets properties to default on postback
通过遵循旨在创建视频播放器的教程,我对其进行了修改,以构建HTML5范围控件。 这是我的代码:
namespace CustomServerControls
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:Range runat=server ID=Range1></{0}:Range>")]
public class Range : WebControl
{
public int Min { get; set; }
public int Max { get; set; }
public int Step { get; set; }
public int Value { get; set; }
protected override void RenderContents(HtmlTextWriter output)
{
output.AddAttribute(HtmlTextWriterAttribute.Id, this.ID);
output.AddAttribute(HtmlTextWriterAttribute.Width, this.Width.ToString());
output.AddAttribute(HtmlTextWriterAttribute.Height, this.Height.ToString());
if (Min > Max)
throw new ArgumentOutOfRangeException("Min", "The Min value cannot be greater than the Max Value.");
if (Value > Max || Value < Min)
throw new ArgumentOutOfRangeException("Value", Value,
"The Value attribute can not be less than the Min value or greater than the Max value");
if (Min != 0)
output.AddAttribute("min", Min.ToString());
if (Max != 0)
output.AddAttribute("max", Max.ToString());
if (Step != 0)
output.AddAttribute("step", Step.ToString());
output.AddAttribute("value", Value.ToString());
output.AddAttribute("type", "range");
output.RenderBeginTag("input");
output.RenderEndTag();
base.RenderContents(output);
}
}
}
如您所见,它非常简单,并且可以设置单个属性。
如果我回发帖子以检查当前值是什么,则控件会将其属性重置为默认值(0)。 我想这是一个viewstate问题。 没有人看到上述代码中我缺少的任何内容,以使其正常工作吗?
编辑:
我注意到此标记正在呈现到页面:
<span id="Range1" style="display:inline-block;">
<input id="Range1" min="1" max="100" value="5" type="range">
</span>
这显然是错误的,我不想创建span标签,并且输入控件没有名称。 因此,当我回发时,无法从控件获得任何数据。
尝试这个:
[DefaultProperty("Value")]
[ToolboxData("<{0}:Range runat=server />")]
public class Range : WebControl, IPostBackDataHandler {
private static readonly object mChangeEvent = new object();
public Range() : base(HtmlTextWriterTag.Input) { }
[Category("Events")]
public event EventHandler Change {
add { Events.AddHandler(mChangeEvent, value); }
remove { Events.RemoveHandler(mChangeEvent, value); }
}
[DefaultValue(0)]
public int Value {
get { return (int?)ViewState["Value"] ?? 0; }
set { ViewState["Value"] = value; }
}
protected override void AddAttributesToRender(HtmlTextWriter writer) {
base.AddAttributesToRender(writer);
// Add other attributes (Max, Step and value)
writer.AddAttribute(HtmlTextWriterAttribute.Value, Value.ToString());
writer.AddAttribute(HtmlTextWriterAttribute.Name, UniqueID);
writer.AddAttribute(HtmlTextWriterAttribute.Type, "range");
}
protected virtual void OnChange(EventArgs e) {
if (e == null) {
throw new ArgumentNullException("e");
}
EventHandler handler = Events[mChangeEvent] as EventHandler;
if (handler != null) {
handler(this, e);
}
}
#region [ IPostBackDataHandler Members ]
public bool LoadPostData(string postDataKey, NameValueCollection postCollection) {
int val;
if (int.TryParse(postCollection[postDataKey], out val) && val != Value) {
Value = val;
return true;
}
return false;
}
public void RaisePostDataChangedEvent() {
OnChange(EventArgs.Empty);
}
#endregion
}
就@VinayC而言,您必须使用ViewState和ControlState(用于关键数据)来保留控件的状态。 另外,您还必须实现IPostBackDataHandler来还原最后一个值并引发change事件,如前面的示例所示。
更新
[DefaultProperty("Value")]
[ToolboxData("<{0}:Range runat=server />")]
public class Range : WebControl, IPostBackEventHandler, IPostBackDataHandler {
[Category("Behavior")]
[DefaultValue(false)]
public bool AutoPostBack {
get { return (bool?)ViewState["AutoPostBack"] ?? false; }
set { ViewState["AutoPostBack"] = value; }
}
protected override void OnPreRender(EventArgs e) {
base.OnPreRender(e);
if (!DesignMode && AutoPostBack) {
string script = @"
var r = document.getElementById('{0}');
r.addEventListener('mousedown', function (e) {{
this.oldValue = this.value;
}});
r.addEventListener('mouseup', function (e) {{
if (this.oldValue !== this.value) {{
{1};
}}
}});";
Page.ClientScript.RegisterStartupScript(
this.GetType(),
this.UniqueID,
string.Format(script, this.ClientID, Page.ClientScript.GetPostBackEventReference(new PostBackOptions(this))),
true);
}
}
#region [ IPostBackEventHandler Members ]
public void RaisePostBackEvent(string eventArgument) {
// If you need to do somthing on postback, derive your control
// from IPostBackEventHandler interface.
}
#endregion
}
上面的代码说明了如何使用ClientScriptManager.GetPostBackEventReference和IPostBackEventHandler为您的Range控件实现简单的AutoPostback。
问题是您的属性( Min
, Max
, Step
, Value
)由实例字段支持-在ASP.NET中,页面回发时将重新创建每个页面及其上的每个控件实例。 因此,每次这些属性都将具有默认值(或您可以在设计时设置的值)-为了在回发后保留这些值,您需要使用view-state来备份属性。 例如:
public int Min
{
get
{
var value = ViewState["Min"];
return null != value ? (int)value : 0;
}
set { ViewState["Min"] = value; }
}
由于可以禁用视图状态,因此控件创建者还可以使用控件状态来备份其重要属性(这对于控件的工作必不可少)-要使用控件状态,您需要重写LoadControlState和SaveControlState方法。
例如(使用控制状态):
public int Min { get; set; }
public int Max { get; set; }
public int Step { get; set; }
public int Value { get; set; }
protected override void OnInit(EventArgs e)
{
Page.RegisterRequiresControlState(this); // must for using control state
base.OnInit(e);
}
protected override object SaveControlState()
{
return new int[] { Min, Max, Step, Value };
}
protected override void LoadControlState(object state)
{
var values = state as int[];
if (null != values)
{
Min = values[0]; Max = values[1];
Step = values[2]; Value = values[3];
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.