一、解决ToolboxData元数据与Designer元数据冲突的问题
上一篇文章《千万级数据分页之三----实现设计时支持和数字分页 》 ,实现了设计时支持和数字分页,但还是有些不完善的地方,比如“自动套用格式”有“英文样式”和“字符样式”,但是如果我不想要这两种样式,即需要恢复初始状态时,需要再加入“默认样式”,在AspNetPagerAutoFormat类中加入默认样式。代码如下:
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Web.UI;
using System.Web;
using System.Web.UI.Design;
using System.Web.UI.Design.WebControls;
using System.Web.UI.WebControls;
using System.Drawing;
namespace CustomControls
... {
public class AspNetPagerAutoFormat : DesignerAutoFormat
...{
public AspNetPagerAutoFormat(string name) : base(name) ...{ }
public override void Apply(Control control)
...{
if (control is AspNetPager)
...{
AspNetPager aspNetPager = (AspNetPager)control;
if (this.Name == "英文样式")
...{
aspNetPager.ButtonText = "Go";
aspNetPager.FirstPageText = "First";
aspNetPager.PrePageText = "Prev";
aspNetPager.NextPageText = "Next";
aspNetPager.EndPageText = "End";
aspNetPager.LabelStyle.ForeColor = Color.Blue;
aspNetPager.LabelStyle.Font.Bold = true;
aspNetPager.TextBoxStyle.CssClass = "blue_rounded";
aspNetPager.TextBoxStyle.Width = Unit.Parse("50px");
aspNetPager.PageSize = 20;
aspNetPager.RecordCount = 0;
}
else if (this.Name == "符号样式")
...{
aspNetPager.ButtonText = "转到";
aspNetPager.FirstPageText = "<font face=webdings color="red">9</font>";
aspNetPager.PrePageText = "<font face=webdings color="red">7</font>";
aspNetPager.NextPageText = "<font face=webdings color="red">8</font>";
aspNetPager.EndPageText = "<font face=webdings color="red">:</font>";
aspNetPager.LabelStyle.ForeColor = Color.Red;
aspNetPager.LabelStyle.Font.Bold = true;
aspNetPager.TextBoxStyle.CssClass = "blue_rounded";
aspNetPager.TextBoxStyle.Width = Unit.Parse("40px");
aspNetPager.PageSize = 30;
}
else if (this.Name == "默认样式")
...{
aspNetPager.ButtonText = "Go";
aspNetPager.FirstPageText = "首页";
aspNetPager.PrePageText = "上一页";
aspNetPager.NextPageText = "下一页";
aspNetPager.EndPageText = "末页";
aspNetPager.LabelStyle.ForeColor = Color.Blue;
aspNetPager.LabelStyle.Font.Bold = true;
aspNetPager.TextBoxStyle.CssClass = "blue_rounded";
aspNetPager.TextBoxStyle.Width = Unit.Parse("40px");
aspNetPager.PageSize = 20;
aspNetPager.RecordCount = 0;
}
}
else
...{
throw new Exception("The method or operation is not implemented.");
}
}
}
}
然后在AspNetPagerDesigner类中加入默认样式,代码如下:
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Web.UI.Design;
using System.ComponentModel.Design;
namespace CustomControls
... {
[SupportsPreviewControl(true)]
public class AspNetPagerDesigner : ControlDesigner
...{
private DesignerAutoFormatCollection _dafc;
public override DesignerAutoFormatCollection AutoFormats
...{
get
...{
if (_dafc == null)
...{
_dafc = new DesignerAutoFormatCollection();
_dafc.Add(new AspNetPagerAutoFormat("英文样式"));
_dafc.Add(new AspNetPagerAutoFormat("符号样式"));
_dafc.Add(new AspNetPagerAutoFormat("默认样式"));
}
return _dafc;
}
}
public override DesignerActionListCollection ActionLists
...{
get
...{
DesignerActionListCollection actionLists = new DesignerActionListCollection();
actionLists.Add(new AspNetPagerActionList(this));
return actionLists;
}
}
}
}
设计效果如下:
当点击默认样式的时候,发现分页控件里面的“首页”,“上一页”等分页文本不见了。这种情况很让人纳闷。
我想了很久也没找出是什么原因造成的。
而我去掉以下代码中的ToolboxData("<{0}:AspNetPager runat='server' PageSize='25' FirstPageText='首页' PrePageText='上一页' NextPageText='下一页' EndPageText='末页' ButtonText='GO'></{0}:AspNetPager>")出现了以下错误,如图所示:
最后我通过在构造函数里对相关属性进行初始化才解决了这个问题。
... {
PageIndex = 1;
PageSize = 20;
FirstPageText = "首页";
PrePageText = "上一页";
NextPageText = "下一页";
EndPageText = "末页";
ButtonText = "GO";
}
效果如下:
原因我一直没能搞明白,希望了解的兄弟告诉一下,谢谢!
二、增加分页模式
在分页控件中加入一个属性PageMode,类型为枚举:
public virtual AspNetPageMode PageMode
... {
get
...{
return ViewState["PageMode"] != null ? (AspNetPageMode)ViewState["PageMode"] : AspNetPageMode.Comprehensive;
}
set
...{
ViewState["PageMode"] = value;
}
}
其中AspNetPageMode定义如下:
using System.Collections.Generic;
using System.Text;
namespace CustomControls
... {
/**//// <summary>
/// 分页模式
/// </summary>
public enum AspNetPageMode
...{
Comprehensive=0,//综合分页(即普通分页与数字分页的综合
Normal=1,//普通分页
}
}
分页模式分综合分页模式和普通分页模式,通过设置改属性,可以选取自己需要的分页模式。
在RenderContents(HtmlTextWriter writer)方法里加入判断:
... {
if (_lblPageCount.Text == "")
...{
for (int i = 0; i < 10; i++)
...{
LinkButton lb = (LinkButton)this.FindControl("_page_" + i.ToString());
lb.Enabled = false;
lb.Text = (i + 1).ToString();
lb.RenderControl(writer);
writer.Write(" ");
}
}
else
...{
int pageCount = int.Parse(_lblPageCount.Text);
int quot = PageIndex / 10;
int rema = PageIndex % 10;
if (pageCount < 10 || pageCount - PageIndex < 10)
...{
for (int i = quot * 10 + 1; i <= pageCount; i++)
...{
LinkButton lb2 = (LinkButton)this.FindControl("_page_" + (i % 10).ToString());
lb2.Text = i.ToString();
if (i == PageIndex)
...{
lb2.Enabled = false;
lb2.Font.Bold = true;
lb2.ForeColor = Color.Red;
}
else
...{
lb2.Enabled = true;
}
lb2.RenderControl(writer);
writer.Write(" ");
}
}
else
...{
for (int i = quot * 10 + 1; i <= quot * 10 + 10; i++)
...{
LinkButton lb2 = (LinkButton)this.FindControl("_page_" + (i % 10).ToString());
lb2.Text = i.ToString();
if (i == PageIndex)
...{
lb2.Enabled = false;
lb2.Font.Bold = true;
lb2.ForeColor = Color.Red;
}
else
...{
lb2.Enabled = true;
}
lb2.RenderControl(writer);
writer.Write(" ");
}
}
}
}
设置PageMode分页效果如下:
三、完善设计时支持
请看下面的图,除了“自动套用格式”外,还多了一个标题:“设计时面板设置”,还有很多的属性设置,比如宽度,高度等,通过这个面板,我们可以进行快速的外观设计,提高了工作效率。而做到这些就需要继承DesignerActionList类来实现。
DesignerActionList为定义用于创建智能标记面板的项列表的类型提供基类,重载DesignerActionItemCollection GetSortedActionItems(),可以设置控件的属性(DesignerActionPropertyItem)、方法(DesignerActionMethodItem)等。这里主要用的DesignerActionPropertyItem类,代码如下:
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Web.UI.Design;
using System.Web.UI.Design.WebControls;
using System.ComponentModel;
using System.ComponentModel.Design;
namespace CustomControls
... {
public class AspNetPagerActionList:DesignerActionList
...{
private DesignerActionItemCollection _daic;
private AspNetPagerDesigner _designer;
public AspNetPagerActionList(AspNetPagerDesigner designer)
: base(designer.Component)
...{
_designer = designer;
}
属性#region 属性
public bool Visible
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.Visible;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["Visible"];
prop.SetValue(_designer.Component, value);
}
}
public string ButtonText
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.ButtonText;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["ButtonText"];
prop.SetValue(_designer.Component, value);
}
}
public string FirstPageText
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.FirstPageText;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["FirstPageText"];
prop.SetValue(_designer.Component, value);
}
}
public string PrePageText
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.PrePageText;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["PrePageText"];
prop.SetValue(_designer.Component, value);
}
}
public string NextPageText
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.NextPageText;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["NextPageText"];
prop.SetValue(_designer.Component, value);
}
}
public string EndPageText
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.EndPageText;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["EndPageText"];
prop.SetValue(_designer.Component, value);
}
}
public int PageSize
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.PageSize;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["PageSize"];
prop.SetValue(_designer.Component, value);
}
}
public Unit Width
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.Width;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["Width"];
prop.SetValue(_designer.Component, value);
}
}
public Unit Height
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.Height;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["Height"];
prop.SetValue(_designer.Component, value);
}
}
public Unit BorderWidth
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.BorderWidth;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["BorderWidth"];
prop.SetValue(_designer.Component, value);
}
}
public BorderStyle BorderStyle
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.BorderStyle;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["BorderStyle"];
prop.SetValue(_designer.Component, value);
}
}
public Color BorderColor
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.BorderColor;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["BorderColor"];
prop.SetValue(_designer.Component, value);
}
}
public Color BackColor
...{
get
...{
AspNetPager pager = (AspNetPager)_designer.Component;
return pager.BackColor;
}
set
...{
PropertyDescriptor prop = TypeDescriptor.GetProperties(_designer.Component)["BackColor"];
prop.SetValue(_designer.Component, value);
}
}
#endregion
public override DesignerActionItemCollection GetSortedActionItems()
...{
if (_daic == null)
...{
_daic = new DesignerActionItemCollection();
_daic.Add(new DesignerActionHeaderItem("设计时面板设置"));
_daic.Add(new DesignerActionPropertyItem("Visible","是否显示"));
_daic.Add(new DesignerActionPropertyItem("ButtonText", "转到按钮文字"));
_daic.Add(new DesignerActionPropertyItem("FirstPageText", "首页文本"));
_daic.Add(new DesignerActionPropertyItem("PrePageText", "上一页文本"));
_daic.Add(new DesignerActionPropertyItem("NextPageText", "下一页文本"));
_daic.Add(new DesignerActionPropertyItem("EndPageText", "末页文本"));
_daic.Add(new DesignerActionPropertyItem("PageSize", "每页记录数"));
_daic.Add(new DesignerActionPropertyItem("Width","宽度"));
_daic.Add(new DesignerActionPropertyItem("Height", "高度"));
_daic.Add(new DesignerActionPropertyItem("BackColor","背景色"));
_daic.Add(new DesignerActionPropertyItem("BorderStyle", "边框类型"));
_daic.Add(new DesignerActionPropertyItem("BorderColor", "边框颜色"));
_daic.Add(new DesignerActionPropertyItem("BorderWidth", "边框宽度"));
}
return _daic;
}
}
}
然后在AspNetPagerDesigner类重载属性ActionLists,把AspNetPagerActionList加入设计器里。如下面代码的粗体字所示
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Web.UI.Design;
using System.ComponentModel.Design;
namespace CustomControls
... {
[SupportsPreviewControl(true)]
public class AspNetPagerDesigner : ControlDesigner
...{
private DesignerAutoFormatCollection _dafc;
public override DesignerAutoFormatCollection AutoFormats
...{
get
...{
if (_dafc == null)
...{
_dafc = new DesignerAutoFormatCollection();
_dafc.Add(new AspNetPagerAutoFormat("英文样式"));
_dafc.Add(new AspNetPagerAutoFormat("符号样式"));
_dafc.Add(new AspNetPagerAutoFormat("默认样式"));
}
return _dafc;
}
}
public override DesignerActionListCollection ActionLists
...{
get
...{
DesignerActionListCollection actionLists = new DesignerActionListCollection();
actionLists.Add(new AspNetPagerActionList(this));
return actionLists;
}
}
}
}
到这里,一个分页控件基本上算是完成了,这里面考虑的了几点:
一是分页的实现:
分页有普通分页模式(即上一页、下一页等),还有综合分页模式(即在普通模式的基础上加了数字分页),基本上能满足常用的分页需求。
二是设计时支持:
修正了设计时支持的一些显示错误的问题,增加了“自动套用格式”和“属性列表”,可以快速定制自己的样式。
提供自定义样式支持,可以通过LinkButtonStyle,TextBoxStyle,LabelStyle,ButtonStyle等复杂属性来定制自己需要的样式,如图所示:
看字面意思就知道,LinkButtonStyle就是设置按钮LinkButton的样式,像数字分页,上一页,下一页等用的都是这个按钮。
三、安全性方面
主要集中于数字分页按钮LinkButton和跳转一块,如果用户输入的是非法字符,分页控件将会提示用户“非法页码”并返回,其他安全性方面有待进一步的研究。
... {
string strID = ((LinkButton)sender).ID;
if (strID != "")
...{
int pageIndex = 0;
int pageNum = PageIndex / 10;
try
...{
int pageNum2 = int.Parse(strID.Split('_')[2]);
if (pageNum2 == 0)
pageNum2 = 10;
pageIndex = pageNum2 + pageNum*10;
}
catch
...{
System.Web.HttpContext.Current.Response.Write("<Script>alert('非法的页码!');</script>");
return;
}
_lblCurrentPage.Text = pageIndex.ToString();
OnPageChanged(EventArgs.Empty);
}
}
/**/ /// <summary>
/// 跳转到指定页
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void BtnChangePage_Click( object sender, EventArgs e)
... {
int pageIndex = 0;
try
...{
pageIndex = int.Parse(_txtPageIndex.Text);
}
catch
...{
System.Web.HttpContext.Current.Response.Write("<Script>alert('请输入正确的页数!');</script>");
return;
}
int pageCount = int.Parse(_lblPageCount.Text);
if (pageIndex == 0)//如果为0,则提示错误
...{
System.Web.HttpContext.Current.Response.Write("<Script>alert('请输入正确的页数!');</script>");
return;
}
if (pageIndex > pageCount)//如果大于总页数则提示错误
...{
System.Web.HttpContext.Current.Response.Write("<Script>alert('请输入正确的页数!');</script>");
return;
}
_lblCurrentPage.Text = pageIndex.ToString();
OnPageChanged(EventArgs.Empty);
}
四、分页性能方面
以下是在我机子上的测试:查询1000万条数据,每页查询15条记录,40万页以下基本在1秒以内,,40万页以上也就两三秒左右。
当然这主要归功于存储过程写得好,但是如果分页代码写得不好也会影响分页速度。我的做法是记录当前页码,根据当前页码来显示相应的数字页码,比如当前页码是23页,则显示21到30页,并且23页灰显,粗体。除了可以突出显示页面,还可以避免重复点击造成不必要的数据传输。上一页,下一页等分页方式也做了相应的判断。
五、关于删除记录导致总记录数变化的问题
可以通过以下代码来解决这个问题,如粗体代码所示,如果删除成功,则调用TestDataCount()该方法重新统计总记录数,总页数
/**//// <summary>
/// 删除纪录
/// </summary>
/// <param name="sender"></param>
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
...{
if (e.CommandName == "Delete")
...{
string pagerID = GridView1.DataKeys[Convert.ToInt32(e.CommandArgument)].Value.ToString();
int flag = 0;
try
...{
flag = ExecProc.DeleteData(int.Parse(pagerID));
}
catch (Exception ex)
...{
}
if (flag != 0)
...{
Response.Write("<script>alert('删除成功!');</script>");
TestDataCount();//重新统计总记录数,总页数
BindPaperDefineProgramme(1);
}
else
...{
Response.Write("<script>alert('删除失败!');</script>");
BindPaperDefineProgramme(AspNetPager1.PageIndex);
}
}
}
#endregion
六、通用性
适用于GridView,DataGrid,DataList,Repeater等数据绑定控件,其中使用GridView分页效果如下:
需要分页控件源代码和使用示例的,请到这里来下载: