概要: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,用于指定是否允许对所添加的内容换行
效果如下:
下面是整个的源代码:
控件代码:

控件代码
1
using System;
2
using System.Data;
3
using System.Configuration;
4
using System.Web;
5
using System.Web.Security;
6
using System.Web.UI;
7
using System.Web.UI.WebControls;
8
using System.Web.UI.WebControls.WebParts;
9
using System.Web.UI.HtmlControls;
10
using System.ComponentModel;
11
/**//// <summary>
12
/// MyPanel 的摘要说明
13
/// </summary>
14
///
15
namespace 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控件开发(五)之深入理解控件的呈现原理