如果当前是升序用户选择了降序或者当前是降序用户选择了升序时,都意味这用户选择的改变,所以还需要一种机制来记录用户的选择,这个功能由SortOrderValue 实现,但是这里还容易忽略一个问题就是“默认”的排序方式,如果用户在使用该控件时没有在布局代码里明确指出是升序还是降序,那就需要在Sorter里给出一种默认的排序方式,这个功能有FlipSortOrder属性完成。
FlipSortOrder属性主要用于默认排序,如下请看其代码:
bool _flipSortOrder = false;
public bool FlipSortOrder { get { return _flipSortOrder; } set { _flipSortOrder = value; } }
从这里似乎还可不到它是怎么实现的,在后面介绍SortOrderValue时读者可以看到它用了“?”运算符进行比较来实现的。
SelectedSortOrder属性用于生成排序方式(包括按照哪一例),
public string SelectedSortOrder
{ get { return SortColumnValue + " " + SortOrderValue.Trim();} }
例如我们给SortColumnValue传递Author,给SortOrderValue传递Asc则SelectedSortOrder的值相当于 (Select * from Community_ContentPage Order By) Author Asc
在这里,需要在SortColumnValue和SortOrderValue之间加入空格,这就是SortColumnValue和SortOrderValue直接由一个“+ " " +”的原因。
SortColumnValue属性设置为列的值,它的值就是前面说的Date Created、View Count、Rating、Title、Date Commented、Date Updated、Author、Default和Topic的任意一个。
public string SortColumnValue {
get {
if (ViewState["SortColumn"] == null)
return _items[0].Value;
else
return (string)ViewState["SortColumn"];
}
set { ViewState["SortColumn"] = value; }
}
读者可以看到,对于SortColumnValue它的取值为_items[0].Value,这里的items[0]和你布局Sorter的使用有关,例如按照如下的使用方式:
<community:Sorter id="Sorter" align="right" runat="Server">
<ListItem Text="Default Order" value="Default" />
<ListItem Text="Date Posted" value="DateCreated"/>
<ListItem Text="Title" value="Title"/>
<ListItem Text="Popularity" value="ViewCount"/>
</community:Sorter>
那么_items[0].Value就是“Default Order”‘如果使用方式如下
<community:Sorter id="Sorter" align="right" runat="Server">
<ListItem Text=" Title " value="Default" />
<ListItem Text="Date Posted" value="DateCreated"/>
<ListItem Text=" Default Order " value="Title"/>
<ListItem Text="Popularity" value="ViewCount"/>
</community:Sorter>
那么_items[0].Value就是“Title”。当页面回传时使用ViewState记住用户的选择。这里Sorter并没有类似DropDownList的Selected属性,所以不能够直接设置被选择的选项。
SortOrderValue属性设置为排序的值,它的值是asc或者desc之一。
public string SortOrderValue {
get {
if (ViewState["SortOrder"] == null)
return _flipSortOrder ? "asc" : "desc";
else
return (string)ViewState["SortOrder"];
}
set { ViewState["SortOrder"] = value; }
}
请看这里的“默认”设置,页面在加载时,SortOrder将为空,前面可以看到_ flipSortOrder的值是false,所以return _flipSortOrder ? "asc" : "des
该文章转载自1024k:http://www.1024k.cn/control/2007/200702/1271_4.html
c"返回的是降序,这就是我们为什么浏览页面时页面降序显示的原因。
读者可以将_ flipSortOrder的值是true,那么当你浏览所有区域时,默认将是按照升序进行排序。
,如果SortOrder不为空,SortOrderValue是怎么知道回传改变的呢?在LoadPostData里有如下代码:
public bool LoadPostData(String postDataKey, NameValueCollection values)
{ string newSortOrderValue = values[SortOrderHelperID];
if (newSortColumnValue != SortColumnValue || newSortOrderValue != SortOrderValue)
{… SortOrderValue = newSortOrderValue; … }
正如你所看到的,当用户选择不同的排序时,LoadPostData会将新值赋值给SortOrderValue,这降导致SortOrderValue的值的改变,然后将用新值生成SQL预计。
SortColumnOptionHelper和SortOrderOptionHelper都是用于检索选项ListItem的值,它们的区别仅仅是值的不同,SortColumnOptionHelper值是Date Created、View Count、Rating等不固定的,而SortOrderOptionHelper则是Asc或者Desc,但是本质上处理是一样的,代码如下:
private string SortColumnOptionHelper(ListItem item) {
if (String.Compare(item.Value, SortColumnValue) == 0)
return String.Format("<option value=\"{0}\" selected=\"selected\">{1}</option>", item.Value, item.Text);
else
return String.Format("<option value=\"{0}\">{1}</option>", item.Value, item.Text);
}
这里请注意如下事项:
(1)Format用于格式化数据,在上面代码里Format需要格式化两个变量:item.Value和item.Text,这样在使用Format格式化时,使用{0}表示第一个参数item.Value,用{0}表示第二个参数item.Text。
(2)对于转移符号需要使用“\”,例如"<option value=\"{0}\" selected=\"selected\">{1}</option>",我们希望它的输出类似如下:<option value=”myitemvalue” selected=”selected">myitemText</option>,但是对于引号如果直接写会被系统直接使用不会输出,所以使用“\””就可以输出引号。
private string SortOrderOptionHelper(string itemText, string itemValue) {
if (String.Compare(itemValue, SortOrderValue) == 0)
return String.Format("<option value=\"{0}\" selected=\"selected\">{1}</option>", itemValue, itemText);
else
return String.Format("<option value=\"{0}\">{1}</option>", itemValue, itemText);
}
SortOrderOptionHelper和SortColumnOptionHelper功能类似,后面会介绍。
SortColumnHelperID属性和SortOrderHelperID属性用于返回SortColumn/SortOrder下拉框的值,这里sc是SortColumn的缩写,so是SortOrder的缩写,如下:
private string SortColumnHelperID
{ get { return UniqueID + "_sc"; } }
private string SortOrderHelperID
{ get { return UniqueID + "_so"; } }
OnChangeHelper用于获取对客户端脚本函数的引用,调用该函数将使服务器发送回该页。该方法还将一个参数传递到在服务器上执行回发处理的服务器控件。这里的参数this表示返回到原控件。
private string OnChangeHelper {
get { return "javascript:" + Page.GetPostBackEventReference(this); } }
在后面代码里可以看到对如下一句代码
writer.AddAttribute(HtmlTextWriterAttribute.Onchange, OnChangeHelper);
这就告诉系统,当选项发生改变触发OnChange时,就调用OnChangerHelper脚本,系统通过在页面生成类似如下脚本
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform;
if (window.navigator.appName.toLowerCase().indexOf("netscape") > -1) {
theform = document.forms["Form1"];
}
else {
theform = document.Form1;
}
theform.__EVENTTARGET.value = eventTarget.split("$").join(":");
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
而现在要控件当用户选择不同选项时触发回发就要调用该教本,通过OnChangeHelper返回给客户端的HTML代码类似如下:
<select name="sorts_sc" onchange="javascript:__doPostBack('sorts','')">
<select name="sorts_so" onchange="javascript:__doPostBack('sorts','')">
那么如何理解GetPostBackEventReference(this)里面的this参数呢?
This参数指出具体处理返回到该控件本身。例如我在使用该控件的代码类似如下:
<sort:Sorter runat="server" id="mysorts">
<ListItem Text="Default Order" value="Default" />
<ListItem Text="Date Posted" value="DateCreated"/>
<ListItem Text="Title" value="Title"/>
<ListItem Text="Popularity" value="ViewCount"/>
<ListItem Text="Topic" value="Topic" />
</sort:Sorter>
那么它生成的HTML代码就类似为:
<select name="sorts_sc" onchange="javascript:__doPostBack('mysorts','')">
<select name="sorts_so" onchange="javascript:__doPostBack('mysorts','')">
具体有控件本身处理。
在Sorter里用LoadPostData验证用户的选择由没有更改,如果更则返回true,否则返回false。
public bool LoadPostData(String postDataKey, NameValueCollection values) {
string newSortColumnValue = values[SortColumnHelperID];
string newSortOrderValue = values[SortOrderHelperID];
if (newSortColumnValue != SortColumnValue || newSortOrderValue != SortOrderValue) {
SortColumnValue = newSortColumnValue;
SortOrderValue = newSortOrderValue;
return true;
} else
return false;
}
请看下面示意图5-56,
我在选择排序时,开始使用Title进行排序,当我再次选择按照Date排序时,此时数据回发到服务器,原来的SortColumnValue的值为Title,而newSortColumnValue的值为Date,这样
if (newSortColumnValue != SortColumnValue || newSortOrderValue != SortOrderValue) {...}
将返回true,ASP.NET页框架将自动跟踪LoadPostDate返回值的控件,对于返回值为true的,则调用RaisePostDataChangedEvent,在Sorter类里就通过在RaisePostDataChangedEvent里调用OnOrderChanged函数实现页面更新排序。 代码如下:
public void RaisePostDataChangedEvent() {
OnOrderChanged(EventArgs.Empty);
}
在OnOrderChanged函数里调用orderChanged事件,如下:
public virtual void OnOrderChanged(EventArgs e) {
if (OrderChanged != null)
OrderChanged(this, e);
&nb
该文章转载自1024k:http://www.1024k.cn/control/2007/200702/1271_5.html
sp; }
这样,我们就可以实现Order改变时的排序。例如在Photo模块里使用OrderChanged代码如下:
if (objSorter != null)
objSorter.OrderChanged += new EventHandler(ContentList_OrderChanged);
可以看到,具体的排序由ContentList_OrderChanged完成,后面我们会介绍ContentList_OrderChanged的实现。
将控件注册为需要回发处理的控件。请注意这里选择的是OnPreRender。
protected override void OnPreRender(EventArgs e) {
Page.RegisterRequiresPostBack(this);
}
Render判断有没有选现,如果没有选项则项目不显示该控件。请注意所谓的不显示该控件就是不调用基类的base.Render()。
protected override void Render(HtmlTextWriter writer) {
if (_items.Count > 0)
base.Render(writer); }
RenderContents方法将呈现SortColumn和SortOrder这两个下拉框控件。但是具体则是由RenderSortColumn和RenderSortOrder实现。
protected override void RenderContents(HtmlTextWriter writer) {
// 打开tr标记
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
RenderSortColumn(writer);
RenderSortOrder(writer);
writer.RenderEndTag();
}
上面调用的RenderSortColumn和 RenderSortOrder方法代码如下:
private void RenderSortColumn(HtmlTextWriter writer) {
//获取SectionInfo信息
SectionInfo objSectionInfo = (SectionInfo)Context.Items["SectionInfo"];
//如果Topic不可用,则从下拉框里移除该选项
ListItem deleteItem;
if (!objSectionInfo.EnableTopics) {
deleteItem = _items.FindByValue( "Topic" );
if (deleteItem != null)
_items.Remove(deleteItem);
}
//如果Rating不可用,则从下拉框里移除该选项
if (!objSectionInfo.EnableRatings) {
deleteItem = _items.FindByValue( "Rating" );
if (deleteItem != null)
_items.Remove(deleteItem);
}
//打开单元格
writer.RenderBeginTag(HtmlTextWriterTag.Td);
//打开select
//这里就使用了SortColumnHelperID以便name的唯一性
writer.AddAttribute(HtmlTextWriterAttribute.Name, SortColumnHelperID);
writer.AddAttribute(HtmlTextWriterAttribute.Onchange, OnChangeHelper);
writer.RenderBeginTag(HtmlTextWriterTag.Select);
// 显示每一个Item
//读者可以看到,对于每一个option,分别输出
foreach (ListItem item in _items) {
writer.Write(SortColumnOptionHelper(item)); }
//关闭Select
writer.RenderEndTag();
//关闭单元个
writer.RenderEndTag();
}
private void RenderSortOrder(HtmlTextWriter writer) {
//打开单元格
writer.RenderBeginTag(HtmlTextWriterTag.Td);
// 打开select
//同样这里使用了SortOrderHelperID以保证唯一性
writer.AddAttribute(HtmlTextWriterAttribute.Name, SortOrderHelperID);
writer.AddAttribute(HtmlTextWriterAttribute.Onchange, OnChangeHelper);
writer.RenderBeginTag(HtmlTextWriterTag.Select);
//呈现Ascending/Descending
if (_flipSortOrder) {
writer.WriteLine(SortOrderOptionHelper(_ascendingText, "asc"));
writer.WriteLine(SortOrderOptionHelper(_descendingText, "desc"));
} else {
writer.WriteLine(SortOrderOptionHelper(_descendingText, "desc"));
writer.WriteLine(SortOrderOptionHelper(_ascendingText, "asc"));
}
// 关闭 select
writer.RenderEndTag();
// 关闭单元格
writer.RenderEndTag();
}
如果控件 A 在页上的其控件标记中有嵌套控件,页分析器会将那些控件的实例添加到 A 的 Controls 集合。这通过调用 A 的 AddSubParsedObject 方法来实现。每个控件从 Control 继承此方法,默认实现只不过将子控件插入到控件层次结构树中。通过重写 AddSubParsedObject 方法,控件可以重写默认的分析逻辑
在Sorter里当分析特定类型的子控件时,它只会将类型为ListItem的对象添加到集合,而忽略其它对象。
protected override void AddParsedSubObject(Object obj) {
if (obj is ListItem) {
_items.Add((ListItem)obj);
}
}
Sort的构造函数调用基类,并生成table标记,因为他是基于表的
public Sorter() : base(HtmlTextWriterTag.Table) { }
Sorter类里的SorterControlBuilder从ControlBuilder派生,它重写GetChildControlType方法,使得Sorter标记之间只有为ListItem或者是asp:ListItem时才添加子控件。
还要注意一下,在代码里不管是ListItem还是asp:ListItem,它返回的都是ListItem,这样可以放置asp:但被忽略被解析。
public class SorterControlBuilder : ControlBuilder {
public override Type GetChildControlType(String tagName, IDictionary attributes)
{
if (String.Compare(tagName, "ListItem", true) == 0 || String.Compare(tagName, "asp:ListItem", true) == 0)
该文章转载自1024k:http://www.1024k.cn/control/2007/200702/1271_6.html
> { return typeof(ListItem); }
return null;
}
}
}
下面我们给出该控件的基本使用模式
<sort:Sorter runat="server" id="sorts">
<ListItem Text="Default Order" value="Default" />
<ListItem Text="Date Posted" value="DateCreated"/>
<ListItem Text="Title" value="Title"/>
<ListItem Text="Popularity" value="ViewCount"/>
<ListItem Text="Topic" value="Topic" />
</sort:Sorter>
同时给出了由基本使用模式生成的HTML代码,在HTML里,读者必须明白它不仅仅生成了HTML的select元素,还包括table、tr、td和javascript脚本等
<table id="sorts">
<tr>
<td><select name="sorts_sc" onchange="javascript:__doPostBack('sorts','')">
<option value="Default" selected="selected">Default Order</option><option value="DateCreated">Date Posted</option><option value="Title">Title</option><option value="ViewCount">Popularity</option><option value="Topic">Topic</option>
</select></td><td><select name="sorts_so" onchange="javascript:__doPostBack('sorts','')">
<option value="desc" selected="selected">Descending</option>
<option value="asc">Ascending</option>
</select></td>
</tr>
</table></TD>
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform;
if (window.navigator.appName.toLowerCase().indexOf("netscape") > -1) {
theform = document.forms["Form1"];
}
else {
theform = document.Form1;
}
theform.__EVENTTARGET.value = eventTarget.split("$").join(":");
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
</script>
在后面我们将进一步介绍使用客户端脚本的控件