概要:1.样式概述,了解控件样式属性的创建方法2.操作ControlStyle的几种方法3.WebControl是如何把样式生成委托给ControlStyle属性的4.样式的状态管理5.重写样式属性6.实现自定义类型化样式
前言:
上节中已讲述了呈现原理,我们在开发自已控件时,重写了Render方法来创建自定义输出,ASP.net给控件开发者提供了编程方式控制样式的途径。样式控制控件的可视化外观。如果您开发支持样式的控件,则从 System.Web.UI.WebControls.WebControl 派生,它将样式作为强类型属性(例如 Font、Height、Width 等)公开并为使用样式属性提供方法
概要:
1.样式概述,了解控件样式属性的创建方法
2.操作ControlStyle的几种方法
3.WebControl是如何把样式生成委托给ControlStyle属性的
4.样式的状态管理
5.重写样式属性
6.实现自定义类型化样式
正题
一.样式概述:样式是如何工作的
我们都知道在webcontrol类中已经内含了许多样式属性,这些属性由生成的html元素的可视化外观。WebControl类的样式功能封装在controlstyle属性中,样式属性实际上是ControlStyle属性的子属性。我们来看看示例就清楚了,这段代码包含了webcontrol 的BorderColor属性的定义。
public virtual Color BorderColor
{
get
{
if (!this.ControlStyleCreated)
{
return Color.Empty;
}
return this.ControlStyle.BorderColor;
}
set
{
this.ControlStyle.BorderColor = value;
}
}
其中的this.ControlStyle.BorderColor中,从这里可以看出BorderColor的内部属性,那么我们有必要来看看ControlStyle的源代码,它是如何实现的。一步一步的解析其实现
public Style ControlStyle
{
//定义一个ControlStyle属性
get
{
if (this.controlStyle == null)
{
this.controlStyle = this.CreateControlStyle();
if (base.IsTrackingViewState)
{
this.controlStyle.TrackViewState();
}
if (this._webControlFlags[1])
{
this._webControlFlags.Clear(1);
this.controlStyle.LoadViewState(null);
}
}
return this.controlStyle;
}
}
protected virtual Style CreateControlStyle()
{
//定义CreateControlStyle方法
return new Style(this.ViewState);
}
ControlStyle是一个只读属性,返回一个Style类型,当第一次访问该属性的时候被创建。其实现过程:当controlStyle不存在时,我们就调用了CreateControlStyle方法去创建一个Style类型,然后,执行视图状态跟踪任务,其具体过程由Style类所提供的TrackViewState方法来完成。然后加载视图状态。
对于上述代码,我们来了解两点:
1.为什么是由Style类提供的TrackViewState方法完成呢。我们接下来看看如过程:
在ControlStyle我们调用了this.controlStyle.TrackViewState()方法,而其中的TrackViewState方法调用的是Style的TrackViewState方法:
伪代码如下:style中的TrackViewState方法
protected internal virtual void TrackViewState
{
this.ViewState.TrackViewState();
}
2.那么我们来看看CreateControlStyle为我们做了什么。
CreateControlStyle方法的默认实现返回一个Style对象,调用其Style的构造函数,接收一个ViewState状态包,那么ViewState是在WebControl是如何实现的呢,
protected virtual StateBag ViewState
{
get
{
if (this._viewState == null)
{
this._viewState = new StateBag(this.ViewStateIgnoresCase);
if (this.IsTrackingViewState)
{
this._viewState.TrackViewState();
}
}
return this._viewState;
}
}
它去实例化一个StateBag,创建一个状态包实例,再判断其视图状态是否跟踪了其视图状态,在这里是确保视图被正确保存到了ViewState中。它是一个虚方法,所以我们可以在派生的控件中可以重载其方法来创建并返回一个自定义的Style类的自定义样式。
ControlStyle属性返回的的类型是System.Web.UI.WebControls.Style类,而Style实现了样式属性并有内建了状态管理和生成功能。
伪代码:
public class Style : Component, IStateManager
{
…}
二.操作ControlStyle的几种方法
为了操作ControlStyle属性,WebControl定义了三种方法。
1.CreateControlStyle 自定义控件可以重载该方法来修改样式属性的默认值或为ControlStyle属性创建一个新的类型化样式。
格式:
protected virtural Style CreateControlStyle()
创建由WebControl类在内部用来实现所有与样式有关的属性的样式对象。
注意:因为WebControl不会自动保存ControlStyle的ViewState,所以如果要重写CreateControlStyle,那么最好按默认的策略,把ViewState作为Style保存数据的地方:
示例:
protected override Style CreateControlStyle()
{
return new TableStyle(ViewState);
}
1. ApplyStyle把上传给该方法的样式中设置的样式属性复制到控件自已的ControlStyle中。
格式:
public void ApplyStyle(Style s)
将指定样式的所有非空白元素复制到服务器控件,改写控件的所有现有的样式元素。其中s表示要复制的样式。
示例:
开始:如下
Style style = new Style();
style.BorderColor = Color.Red;
this.controlTable.ApplyStyle(style);//这里的controlTable是一个自定义控件
应用样式后:
1.MergeStyle 从上传给该方法的样式中复制样式属性到控件自已的ControlStyle中,即复制那些指定的样式中设置的而不是在ControlStyle中设置的属性。
格式:
public void MergeStyle(Style s)
将指定样式的所有非空白元素复制到服务器控件,但不改写该控件现有的任何样式元素。其中s表示要复制的样式。
示例:
开始:如下
Style style = new Style();
style.BorderColor = Color.Red;
this.controlTable.MergeStyle(s);
应用后:
现在,就可以看出ApplyStyle与MergeStyle的区别了。
三.WebControl是如何把样式生成委托给ControlStyle属性的
接下来,我们来分析WebControl是如何把样式生成后给ControlStyle属性的。在WebControl的AddAttributesToRender方法中,它调用了ControlStyle 属性的AddAttributesToRender方法。
protected virtual void AddAttributesToRender(HtmlTextWriter writer)
{
。。。。。。。。。。。。。
。。。。。
if (this.ControlStyleCreated && !this.ControlStyle.IsEmpty) //是否已经创建了控件样式
{
this.ControlStyle.AddAttributesToRender(writer, this);
}
。。。。。。。。。。。
。。。。
}
public bool ControlStyleCreated
{
get
{
return (this.controlStyle != null);
}
}
前面说过。WebControl中的样式属性实际上是ControlStyle属性的子属性,ControlStyle属性 AddAttributesToRender方法实现了把这些属性作为控件标签中的HTMl或Css来生成逻辑。
四.样式的状态管理
WebControl是如何把与样式相关的状态管理委托给ControlStyle属性来完成的呢?从前面我们可以看到ControlStyle属性的Style类型实现了IStateManager接口,并对自身进行状态管理。在TrackViewState,SaveViewState,LoadViewState方法中,WebControl调用了ControlStyle属性的相应方法,
为了在WebControl实现状态管理,在Style中有两个构造器,一个只有一个StateBag类型的参数,另一个没有任何参数。当在CreateControlStyle中创建控件的样式时,WebControl使用的是一个参数的构造器,把自已的ViewState传给该构造器,
Style的两个构造函数:
publicStyle() : this(null)
{
,。。。
}
五:重写样式属性
样式属性的重载与其他属性的重写类似,但必须记住一点:就是对属性值的所估摸的修改必须给控件的ControlStyle属性。
示例:
我们举一个了简单示例。
控件代码
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
/**//// <summary>
/// LabelStyle 的摘要说明
/// </summary>
///
namespace cnblogs.sui
{
public class LabelStyle :Label
{
public LabelStyle()
{
base.BorderColor = System.Drawing.Color.Red;
base.ForeColor = System.Drawing.Color.Red;
//
// TODO: 在此处添加构造函数逻辑
//
}
public override System.Drawing.Color BorderColor
{
get
{
return base.BorderColor;
}
set
{
throw new NotSupportedException("can not set BorderColor");
}
}
public override System.Drawing.Color ForeColor
{
get
{
return base.ForeColor;
}
set
{
base.ForeColor = value;
}
}
}
}
Aspx页面:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Namespace="cnblogs.sui" TagPrefix="lab" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>无标题页</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<lab:LabelStyle runat=server Text="thid is labelstyle" BorderWidth=1px ID="bl" >
</lab:LabelStyle>
</div>
</form>
</body>
</html>
在构造器中,LabelStyle对基类的BorderColor设置成Color.Red,该值将委托给ControlStyle属性,通过在LabelStyle构造器中
设置基类的BorderColor,ForeColor,把新的值传给ControlStyle属性,它把新的值传给出ControlStyle属性,该属性负责状态管理以
及样式属性的生成,如果没有把改变传到ControlStyle那么重载的样式属性就不会控预期的那样显示。
六.实现自定义类型化样式
继承自Style类的类称为类型化样式。Style类可以由控件开发人员来扩展,创建一个自定义类型化样式,它重写或者添加Style类的属性。服务器控件也可以把自定义类型化样式作为ControlStyle属性的类型。例如,Table控件的ControlStyle属性就是TableStyle类型,该类型是扩展的Style,添加了例如CellPadding、CellSpacing和GridLines属性等。在初步了解类型化样式属性的基本概念之后,下面列举了实现类型化样式属性的方法要点。
1. 创建一个派生类自System.Web.UI.WebControl.Style的类
2. 定义样式为控件提供属性
3. 重写Reset方法,
4. 重写AddAttributesToRender方法,产生 HTML和CSS
5. 复制定义的属性或给定样式属性合并
示例:创建一个MyPanel控件,及相关联的类型化样式MypanelStyle,这个MyPanel控件并从WebControl继承,在MyPanelStyle中设置了3个样式属性。
(1)BackImageUrl,用于指定背景图片的URL;
(2)HorizontalAlign,用于指定所添加内容的水平对其方式;
(3)Wrap,用于指定是否允许对所添加的内容换行
效果如下:
下面是整个的源代码:
控件代码:
控件代码
1using System;
2using System.Data;
3using System.Configuration;
4using System.Web;
5using System.Web.Security;
6using System.Web.UI;
7using System.Web.UI.WebControls;
8using System.Web.UI.WebControls.WebParts;
9using System.Web.UI.HtmlControls;
10using System.ComponentModel;
11/**//// <summary>
12/// MyPanel 的摘要说明
13/// </summary>
14///
15namespace cnblogs.sui
16{
17
18 MyPanelStyle#region MyPanelStyle
19
20 public class MypanelStyle :Style
21 {
22 //定义内部属性
23 internal const int PROP_BACKIMAGEURL = 1;
24 internal const int PROP_HORIZOHTALALIGN = 2;
25 internal const int PROP_WRAP = 3;
26 构造函数#region 构造函数
27 public MypanelStyle()
28 { }
29 public MypanelStyle(StateBag bag) : base(bag) {
30 }
31 #endregion
32 属性列表#region 属性列表
33 [Bindable(true), Description("背影图片的url"), NotifyParentProperty(true)]
34
35 //背影图片
36 public virtual string BackImageUrl {
37
38 get {
39 if (IsSet(PROP_BACKIMAGEURL))
40 {
41 return (string)ViewState["BackImageUrl"];
42
43 }
44
45 return String.Empty;
46 }
47 set {
48 ViewState["BackImageUrl"] = value;
49 }
50
51 }
52 //实现行布局属性
53 [Bindable(true), Category("layout"), DefaultValue(HorizontalAlign.NotSet), NotifyParentProperty(true)]
54 public virtual HorizontalAlign HorizonalAlign
55 {
56 get {
57
58 if (IsSet(PROP_HORIZOHTALALIGN))
59 {
60 return (HorizontalAlign)ViewState["HorizontalAlign"];
61 }
62 return HorizontalAlign.NotSet;
63 }
64
65 set
66 {
67 if (value < HorizontalAlign.NotSet || value > HorizontalAlign.Justify)
68 {
69 throw new ArgumentOutOfRangeException("value");
70 }
71 ViewState["HorizontalAlign"] = value;
72 }
73 }
74 //实现IsEmpty
75 protected new internal bool IsEmpty //使用 new 修饰符显式隐藏从基类继承的成员
76 {
77
78 get {
79
80 return base.IsEmpty && !IsSet(PROP_BACKIMAGEURL) && IsSet(PROP_HORIZOHTALALIGN) && IsSet(PROP_WRAP);
81
82 }
83 }
84 //实现换行
85 [Bindable(true), Category("layout"), DefaultValue(true), NotifyParentProperty(true)]
86 public virtual bool Wrap {
87
88 get {
89
90 if (IsSet(PROP_WRAP))
91 {
92
93 return (bool)ViewState["Wrap"];
94
95 }
96 return true;
97 }
98 set {
99
100 ViewState["wrap"] =value;
101 }
102 }
103 #endregion
104 internal bool IsSet(int propNumber)
105 {
106
107 string key = null;
108 switch (propNumber)
109 {
110
111 case PROP_BACKIMAGEURL: key = "BackImageUrl";
112 break;
113 case PROP_HORIZOHTALALIGN: key = "HorizontalAlign";
114 break;
115 case PROP_WRAP: key = "Wrap";
116 break;
117 }
118 if (key != null)
119 {
120 return ViewState[key] != null;
121
122 }
123 return false;
124 }
125 //重写AddAttributesToRender方法
126 public override void AddAttributesToRender(HtmlTextWriter writer, WebControl owner)
127 {
128 if (IsSet(PROP_BACKIMAGEURL))
129 {
130 string s=BackImageUrl;
131 if (s.Length > 0)
132 {
133
134 if (owner != null)
135 {
136 s = owner.ResolveUrl(s);
137 }
138 writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundImage, "url(" + s + ")");
139
140 }
141 }
142 if (IsSet(PROP_HORIZOHTALALIGN))
143 {
144 HorizontalAlign hAlign = this.HorizonalAlign;
145
146 if (hAlign != System.Web.UI.WebControls.HorizontalAlign.NotSet)
147 {
148 TypeConverter hac = TypeDescriptor.GetConverter(typeof(HorizontalAlign));
149 writer.AddAttribute(HtmlTextWriterAttribute.Align, hac.ConvertToInvariantString(hAlign));
150 }
151 }
152
153 if (IsSet(PROP_WRAP))
154 {
155 bool wrap = Wrap;
156 if (!Wrap)
157 {
158 writer.AddAttribute(HtmlTextWriterAttribute.Nowrap, "nowwrap");
159 }
160 }
161
162
163 base.AddAttributesToRender(writer, owner);
164 }
165 public override void CopyFrom(Style s)
166 {
167 if (s != null)
168 {
169 base.CopyFrom(s);
170 if (s is MypanelStyle)
171 {
172
173 MypanelStyle mps = (MypanelStyle)s;
174 if (!mps.IsEmpty)
175 {
176 if (mps.IsSet(PROP_BACKIMAGEURL))
177 this.BackImageUrl = mps.BackImageUrl;
178 if (mps.IsSet(PROP_HORIZOHTALALIGN))
179 this.HorizonalAlign = mps.HorizonalAlign;
180 if (mps.IsSet(PROP_WRAP))
181 this.Wrap = mps.Wrap;
182
183 }
184 }
185 }
186
187 }
188
189 // 重写MergeWith方法
190 public override void MergeWith(Style s)
191 {
192 if (s != null)
193 {
194 if (IsEmpty)
195 {
196 CopyFrom(s);
197 return;
198 }
199 base.MergeWith(s);
200 if (s is MypanelStyle)
201 {
202 MypanelStyle mps = (MypanelStyle)s;
203 if (!mps.IsEmpty)
204 {
205 if (mps.IsSet(PROP_BACKIMAGEURL) && !this.IsSet(PROP_BACKIMAGEURL))
206 this.BackImageUrl = mps.BackImageUrl;
207 if (mps.IsSet(PROP_HORIZOHTALALIGN) && !this.IsSet(PROP_HORIZOHTALALIGN))
208 this.HorizonalAlign = mps.HorizonalAlign;
209 if (mps.IsSet(PROP_WRAP) && !this.IsSet(PROP_WRAP))
210 this.Wrap = mps.Wrap;
211 }
212 }
213 }
214 }
215
216 //重写Reset方法
217 public override void Reset()
218 {
219 base.Reset();
220 if (IsEmpty) return;
221 if (IsSet(PROP_BACKIMAGEURL))
222 ViewState.Remove("BackImageUrl");
223 if (IsSet(PROP_HORIZOHTALALIGN))
224 ViewState.Remove("HorizontalAlign");
225 if (IsSet(PROP_WRAP)) ViewState.Remove("Wrap");
226 }
227
228 public void Method()
229 {
230 throw new System.NotImplementedException();
231 }
232
233
234
235
236
237
238 }
239 #endregion
240
241 [ ParseChildren(false), PersistChildren(true) ]
242 [ToolboxData("<{0}:panel runat=server></{0}:panel>")]
243
244 public class MyPanel : WebControl
245 {
246 // 定义构造函数
247 public MyPanel() : base(HtmlTextWriterTag.Div) { }
248 // 实现属性BackImageUrl
249 [Bindable(true)]
250 [Category("Appearance")]
251 [DefaultValue("")]
252 public virtual string BackImageUrl
253 {
254 get
255 {
256 if (ControlStyleCreated)
257 {
258 return ((MypanelStyle)ControlStyle).BackImageUrl;
259 }
260 return String.Empty;
261 }
262 set
263 {
264 ((MypanelStyle)ControlStyle).BackImageUrl = value;
265 }
266 }
267 // 实现属性HorizontalAlign
268 [Bindable(true)]
269 [Category("Layout")]
270 [DefaultValue("")]
271 public virtual HorizontalAlign HorizontalAlign
272 {
273 get
274 {
275 if (ControlStyleCreated)
276 {
277 return ((MypanelStyle)ControlStyle).HorizonalAlign;
278 }
279 return HorizontalAlign.NotSet;
280 }
281 set
282 {
283 ((MypanelStyle)ControlStyle).HorizonalAlign = value;
284 }
285 }
286 // 实现属性Wrap
287 [Bindable(true)]
288 [Category("Layout")]
289 [DefaultValue("")]
290 public virtual bool Wrap
291 {
292 get
293 {
294 if (ControlStyleCreated)
295 {
296 return ((MypanelStyle)ControlStyle).Wrap;
297 }
298 return true;
299 }
300 set
301 {
302 ((MypanelStyle)ControlStyle).Wrap = value;
303 }
304 }
305 protected override Style CreateControlStyle()
306 {
307 return new MypanelStyle(ViewState);
308 }
309
310 }
311}
Aspx页面代码:
aspx页面
1<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %>
2<%@ Register Namespace="cnblogs.sui" TagPrefix="panel" %>
3<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4
5<html xmlns="http://www.w3.org/1999/xhtml" >
6<head runat="server">
7 <title>无标题页</title>
8</head>
9<body>
10 <form id="form1" runat="server">
11 <div>
12 <panel:MyPanel runat=server ID="p" BackImageUrl="1.JPG" HorizontalAlign=center Wrap=true BorderColor="#FF8000" BorderWidth="1px" Width="200px" Height="200px">
13
14 <div style="background-color:BlueViolet; ">这就是背影加换行哦。。</div>
15 asfasdfadsf
16 asdfasdf
17 aasd
18 fasdf
19 asd
20
21 fasd
22 f
23 sadf
24 asf
25 asd
26 fsad
27 fasd
28 fas
29 fad
30 sad
31 fas
32 </panel:MyPanel>
33
34
35 </div>
36 </form>
37</body>
38</html>
39
类视图:
解说一下:MyPanel类没有什么可说的,主要解析一下MypanelStyle类。继承于Style,是创建类型化样式的关键。定义一些字段与属性没有什么可说的,主要的说一下其中的方法。
1. 重写了AddAttributesToRender方法。当控件呈现时生成相关的html和css。
2. 重写了CopyFrom,MergeWith,Reset方法。
CopyFrom 与MergeWith是为了复制给定的MypanelStyle或把给定的MypanelStyle与自身合并。
重写Reset是为了删除添加到期ViewState 中的属性,
后记:
.net控件开发系列
1.net组件开发系列(—)之武术系列-----------马步功 之基本功
2.net组件开发系列(—)之武术系列--------太极拳 开发ajax控件
3..net组件开发系列之武术系列武术招数控件生命周期与控件事件机制
4..net控件开发系列事件处理机制三个接口两个方法
5..net控件开发(五)之深入理解控件的呈现原理