通过遵循旨在创建视频播放器的教程,我将其改编为构建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标记,输入控件也没有名称。因此,当我回发时,我没有从控件中获取数据。
答案 0 :(得分:1)
试试这个:
[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以恢复最后一个值并引发更改事件,如上例所示。
<强>更新强>
[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。
答案 1 :(得分:0)
问题是您的属性(Min
,Max
,Step
,Value
)由实例字段支持 - 在ASP.NET中,每个页面和每个控件实例当页面回发时,它会被重新创建。因此,每次这些属性都将具有默认值(或您可以在设计时设置的值) - 为了在回发后保留这些值,您需要使用视图状态来备份属性。例如:
public int Min
{
get
{
var value = ViewState["Min"];
return null != value ? (int)value : 0;
}
set { ViewState["Min"] = value; }
}
由于可以禁用视图状态,因此控件作者也可以使用控件状态来备份重要属性(这对于控件工作至关重要) - 对于使用控件状态,您需要覆盖{ {3}}和LoadControlState方法。
例如(使用控制状态):
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];
}
}