前一段时间写了几篇关于控件开发的文章,一直没有一个实战的控件出来。年前的一个项目中,发现一个比较好的效果,后来为了方面开发,就开发成了一个容器控件。来看控件的几个效果图:
运行效果图:
设计时的效果图:
控件的原理
很显然控件的最外层是标签<fieldset></fieldset>,内容部分是包在内部的一个<div></div>里面的,通过控制div的display来实现效果的。大体的前台html结构:
<fieldset>
<legend>测试例子</legend>
<div>
测试例子的内容
</div>
</fieldset>
UpdownPanel的实现
首先,确认UpdownPanel控件不需要继承某一个控件,但需要一些设计时的支持,如:边框的颜色、高、宽,所以可以确定控件继承于WebControl。控件标签的内容要解释成子控件,同时需要支持事件回传来通知是否已经打开,所以控件还需要继承IPostBackEventHandler。
我们先来看控件的属性:
/// <summary>
/// 标题
/// </summary>
public string Title
{
get
{
if (ViewState["Title"] == null)
return String.Empty;
return ViewState["Title"].ToString().Trim();
}
set
{
ViewState["Title"] = value;
}
}
/// <summary>
/// 距离左边距离
/// </summary>
public Unit PandingLeft
{
get
{
if (ViewState["PandingLeft"] == null)
return new Unit("20px");
return new Unit(ViewState["PandingLeft"].ToString());
}
set
{
ViewState["PandingLeft"] = value;
}
}
/// <summary>
/// 距离上边距离
/// </summary>
public Unit PandingTop
{
get
{
if (ViewState["PandingTop"] == null)
return new Unit("20px");
return new Unit(ViewState["PandingTop"].ToString());
}
set
{
ViewState["PandingTop"] = value;
}
}
/// <summary>
/// 是否打开
/// </summary>
[DefaultValue(false)]
public bool IsOpen
{
get
{
if (ViewState["IsOpen"] == null)
return false;
return Convert.ToBoolean(ViewState["IsOpen"]);
}
set
{
ViewState["IsOpen"] = value;
}
}
Title属性是表示标题,IsOpen属性表示是否已经展开,属性都很简单。
控件的事件参数类:
/// <summary>
/// IsOpenChanged事件的参数
/// </summary>
public class ChangedArgs : EventArgs
{
private bool _isOpened;
public bool IsOpened
{
get
{
return _isOpened;
}
set
{
_isOpened = value;
}
}
public ChangedArgs(bool bOpened)
{
IsOpened = bOpened;
}
}
事件回传:
static object _isOpenChanged = new object();
public event EventHandler<ChangedArgs> IsOpenChanged
{
add
{
Events.AddHandler(_isOpenChanged, value);
}
remove
{
Events.RemoveHandler(_isOpenChanged, value);
}
}
public void RaisePostBackEvent(string eventArgument)
{
ChangedArgs args = new ChangedArgs(Convert.ToBoolean(eventArgument.ToString()));
if (args.IsOpened)
IsOpen = false;
else
IsOpen = true;
if (Events[_isOpenChanged] != null)
{
(Events[_isOpenChanged] as EventHandler<ChangedArgs>)(null, args);
}
}控件的呈现时候,首先需要重写TagKey为"<filedset>",同时要注意注册事件回传的脚本,跟输出自己的控制脚本。这里就不列出所有的呈现的代码了,就看下关键的几行代码:
protected override void RenderContents(HtmlTextWriter writer)
{
//呈现Legend
writer.AddStyleAttribute(HtmlTextWriterStyle.FontStyle, this.Font.ToString());
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize, this.Font.Size.ToString());
writer.AddStyleAttribute(HtmlTextWriterStyle.Cursor, "pointer");
//-------------加载事件回传------------------
string strRef = Page.ClientScript.GetPostBackClientHyperlink(this,IsOpen.ToString());
writer.AddAttribute(HtmlTextWriterAttribute.Href, strRef);
writer.AddStyleAttribute(HtmlTextWriterStyle.TextDecoration, "none");
writer.RenderBeginTag(HtmlTextWriterTag.A);
//标题
writer.Write("   "+this.Title);
writer.RenderEndTag();
writer.RenderEndTag();
writer.AddAttribute(HtmlTextWriterAttribute.Id, "upChild" + this.ClientID);
writer.AddStyleAttribute(HtmlTextWriterStyle.Height, this.Height.ToString());
//判断是否打开
if (!this.IsOpen)
{
writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none");
}
writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingTop, this.PandingTop.ToString());
writer.RenderBeginTag(HtmlTextWriterTag.Div);
//呈现子控件
for (int i = 0; i < this.Controls.Count; i++)
{
this.Controls[i].RenderControl(writer);
}
writer.RenderEndTag();
}最后要注意的一点就是我们要让控件能够在设计时支持,还需要自定义一个ControlDesigner:
public class UpDownPanelDesigner : System.Web.UI.Design.ContainerControlDesigner
{
public override string FrameCaption
{
get
{
return "UpDownPanel的内容";
}
}
}因为UpDownPanel控件是容器控件,所以我们继承于ContainerControlDesigner,同时需要在控件上标识:
[Designer(typeof(UpDownPanelDesigner))]
public class UpDownPanle:WebControl,IPostBackEventHandler
{}
这下就可以使用了,只不过控件还比较粗糙,在以后会逐步完善,还请大家批评,呵呵。