FineUIPro控件库深度解析

FineUIPro控件库

FineUIPro是一套基于jQuery的专业ASP.NET控件库,始于2008年的开源版FineUI控件库。

当年为了提升项目的开发效率,降低代码复杂度,减少对CSS和JavaScript的依赖,我们提出了"No JavaScript, No CSS, No UpdatePanel,No ViewState,No WebServices"的口号,现在看起来仍然激动人心。

首先,JavaScript灵活性与复杂性使得大型项目的开发备受挑战,FineUIPro尝试使用服务器端的强类型语言(C#,VB.NET)来代替大部分的JavaScript实现,不仅可以利用IDE的强大功能(智能提示,代码重构),而且强类型语言的编译时错误检查也是一个加分项。

其次,FineUIPro提供统一的控件集合和页面主题,使得我们无需在代码中自定义CSS样式,不仅减少编码和调试CSS的工作量,而且能够保持整个项目中页面风格的统一和美观。

最后,FineUIPro内置了AJAX的交互支持,使得我们无需写一行JavaScript代码,就能把整个页面的回发变为AJAX过程。另外,FineUIPro也内置了IFrame支持,有助于在页面层级对代码进行解耦合。

 

那么,FineUIPro是如何工作的呢?FineUIPro的控件使用和原生的ASP.NET控件有哪些异同点?FineUIPro的AJAX交互过程又是什么样子的呢?

为了回答这些问题,我们将分别使用FineUIPro和ASP.NET控件来实现一个服务器端分页的表格页面。

ASP.NET的表格控件

首先来看下ASP.NET的原生GridView控件定义:


	
		
		
			
				
			
		
		
		
		
		
		
	

由于GridView并不支持服务器端分页,因此我们没有设置表格的AllowPaging和PageSize属性,而是自定义了两个按钮来实现服务器端分页:



Page

of
  

页面第一次打开时需要加载表格数据:

protected void Page_Load(object sender, EventArgs e)
{
	if (!IsPostBack)
	{
		BindGrid();
	}
}

private void BindGrid()
{
	// 1.设置总项数
	int recordCount = GetTotalCount();

	// 2.获取当前分页数据
	DataTable table = GetPagedDataTable(CurrentPageIndex, PAGE_SIZE);

	// 3.绑定到Grid
	Grid1.DataSource = table;
	Grid1.DataBind();

	UpdatePageControls(recordCount);
}  

绑定表格数据分为如下几个步骤:

1. 获取总记录数

2. 获取当前分页数据

3. 绑定分页数据到表格

其实,表格对象对当前分页状态一无所知(第几页,总共有几页),我们需要自己在页面上保存这些数据:

private int CurrentPageIndex
{
	get
	{
		var pageIndexState = ViewState["CurrentPageIndex"];
		if (pageIndexState == null)
		{
			return 0;
		}
		else
		{
			return Convert.ToInt32(pageIndexState);
		}
	}
	set
	{
		ViewState["CurrentPageIndex"] = value;
	}
}
private const int PAGE_SIZE = 5;

private int CalculatePageCount(int recordCount)
{
	int pageCount = recordCount / PAGE_SIZE;
	if (recordCount % PAGE_SIZE != 0)
	{
		pageCount++;
	}
	return pageCount;
}  

将当前表格分页索引CurrentPageIndex保存到ViewState中,以便在后续的页面回发中获取分页索引。

总页数可以根据当前分页索引和每页记录数计算而来,我们将其逻辑封装到CalculatePageCount方法中。

最后,来看下UpdatePageControls方法:

private void UpdatePageControls(int recordCount)
{
	int pageCount = CalculatePageCount(recordCount);

	lblTotalPages.Text = pageCount.ToString();
	lblCurrentPage.Text = (CurrentPageIndex + 1).ToString();
	if (CurrentPageIndex == 0)
	{
		btnPrevious.Enabled = false;

		if (pageCount > 0)
		{
			btnNext.Enabled = true;
		}
		else
		{
			btnNext.Enabled = false;
		}
	}
	else
	{
		btnPrevious.Enabled = true;

		if (CurrentPageIndex == pageCount - 1)
		{
			btnNext.Enabled = false;
		}
		else
		{
			btnNext.Enabled = true;
		}
	}
}  

根据当前表格分页索引和总页面设置分页按钮的状态。

此时运行页面,显示效果:

FineUIPro控件库深度解析_第1张图片

 

点击Next按钮时,会发起一个页面回发到后台事件:

protected void OnPageButtonClick(object sender, CommandEventArgs e)
{
	switch (e.CommandName)
	{
		case "Previous":
			CurrentPageIndex--;
			break;
		case "Next":
			CurrentPageIndex++;
			break;
	}

	BindGrid();
}  

在分页按钮的点击事件中,首先根据e.CommandName来判断点击了哪个按钮,然后从ViewState中读取当前表格分页索引,最后重新绑定表格数据。

点击Next后页面截图如下:

FineUIPro控件库深度解析_第2张图片

此时页面的回发是Form表单的POST过程,因此会导致整个页面的刷新,用户体验并不好。

 

FineUIPro的表格控件

FineUIPro中的大部分实现代码和GridView的实现代码一样。

不过由于FineUIPro表格默认支持服务器端分页,因此无需在后台通过ViewState保存表格分页索引,也无需自己动手更新分页按钮的状态,因此代码要简单的多。



	
		
		
		
			
				
			
		
		
		
		
		
		
	
  

这个表格定义和之前的GridView很类似,有几点不同的地方:

1. PageManager是每一个使用FineUIPro控件的页面都需要的,其中的AjaxLoadingType用来定义AJAX回发的提示类型。

2. 表格控件的AllowPaging,IsDatabasePaging,PageSize用来指定服务器端分页和分页记录大小,这样就无需自己维护分页信息了。

3. 表格控件的PageIndexChanged用来定义服务器端分页事件。

表格列还有一些特定的属性,实现不同的显示效果:

4.1. 表格列定义了RowNumberField,用来显示行序号。

4.2 CheckBoxField的RenderAsStaticField用来指定复选框的显示样式。

4.3 HyperLinkField的ExpandUnusedSpace用来定义本列宽度占据所有未使用空间。

后台数据绑定代码很简单:

protected void Page_Load(object sender, EventArgs e)
{
	if (!IsPostBack)
	{
		BindGrid();
	}
}

private void BindGrid()
{
	// 1.设置总项数
	Grid1.RecordCount = GetTotalCount();

	// 2.获取当前分页数据
	DataTable table = GetPagedDataTable(Grid1.PageIndex, Grid1.PageSize);

	// 3.绑定到Grid
	Grid1.DataSource = table;
	Grid1.DataBind();
}  

此时页面显示效果:

FineUIPro控件库深度解析_第3张图片

由于FineUIPro内置了很多主题,因此我们可以在Web.config中设置不同的主题,得到不同的显示效果:

FineUIPro控件库深度解析_第4张图片

FineUIPro控件库深度解析_第5张图片

FineUIPro控件库深度解析_第6张图片

分页事件处理函数也很简单:

protected void Grid1_PageIndexChange(object sender, GridPageEventArgs e)
{
	BindGrid();
}  

由于FineUIPro表格自行管理分页信息,因此我们只需要重新绑定数据即可。

此时点击下一页,页面截图:

FineUIPro控件库深度解析_第7张图片

此时的回发是AJAX POST过程,整个页面不会刷新,在回发过程中,FineUIPro会显示一个回发提示动画:

FineUIPro控件库深度解析_第8张图片

 

如果仅从代码和运行效果对比,我们可以看出FineUIPro的表格控件相比ASP.NET原生控件,有如下优点:

1. 代码有90%和原生控件保持一致

2. 代码更少(得益于FineUIPro表格对服务器端分页的内置支持)

3. 页面显示效果更美观大方,并且可以通过全局配置切换不同的显示样式

4. 分页过程是AJAX部分刷新,并内置了提示动画

 

另外,全部示例代码没有一行JavaScript和CSS代码,但是实际上FineUIPro却是严重依赖JavaScript和CSS来实现页面效果和交互。

下面我们会深入分析两个示例的异同。

 

页面渲染的对比

虽然两个示例的大部分ASPX和C#代码一模一样,但是从一开始两者的实现方式就完全不同。

ASP.NET的表格控件

首先来看下ASP.NET表格控件生成的页面HTML代码:

FineUIPro控件库深度解析_第9张图片

简化后看的更清楚:

姓名 性别 入学年份 是否在校 所学专业 分组
陈萍萍 2000 计算机应用技术
Page 1 of 5  

可以看出:

1. ASP.NET表格渲染到页面上是

标签,并且包含了当前页的全部数据

2. 分页按钮最终调用的__doPostBack函数,这个函数我们并不陌生,几乎每个页面都包含这样一个默认的定义

  

毫无疑问,调用此回发函数,其实就是对页面上全局表单对象的提交(theForm.submit()),这将会是整个页面的刷新。

 

FineUIPro的表格控件

FineUIPro表格控件生成的页面HTML代码:

FineUIPro控件库深度解析_第10张图片

简化一下:

...
  

可以看出:

1. 表格数据在JavaScript代码中,并渲染到页面上一个容器(Grid1_wrapper)

2. 分页事件同样触发的是__doPostBack事件

 

两相对比,我们可以得出如下结论:

1. ASP.NET表格控件直接渲染为table标签(包含数据)

2. FineUIPro表格控件会在页面上生成一个div占位符,然后通过JavaScript来渲染出表格控件

 

FineUIPro的做法更加灵活,并且可以实现更加复杂的显示效果,看下生成的DOM结构:

FineUIPro控件库深度解析_第11张图片

只所以有这么多的层次结构,是有很多原因的,简单来说:

1. FineUIPro中表格是从面板继承下来的,所以最外层的div节点是面板相关的

div.f-panel
        ->div.f-panel-header
        ->div.f-panel-bodyct
                ->div.f-panel-body

2. f-panel-body里面的层次才是表格的特定结构

div.f-panel-body

        ->div.f-grid-inner

                ->div.f-grid-headerct

                ->div.f-grid-bodyct

                        ->table.f-grid-table

表格的这个特定DOM层次结构在启用列锁定时会变的更加复杂,如下所示:

FineUIPro控件库深度解析_第12张图片

启用列锁定时,f-grid-inner里面会分裂成两部分,分别对应于锁定表格和主表格,FineUIPro会负责这两部分的同步工作。

由此可知,ASP.NET表格控件直接渲染table节点和数据的方式仅适合于简单的形式,而FineUIPro为了更加好看的界面效果和更加复杂的逻辑实现,必须通过JavaScript来渲染界面和数据。而这一切对于开发人员都是透明的,FineUIPro开发人员只需要写ASPX表格和C#代码即可,剩下的交给我们。

 

页面回发的对比

前面分析可知,ASP.NET表格和FineUIPro的分页回发都是调用的__doPostBack函数,为什么一个是整个页面刷新,而另一个是AJAX部分刷新?

这是因为FineUIPro耍了个小把戏,重写了__doPostBack函数,翻开FineUIPro的客户端JavaScript源代码:

function _fjs_doPostBack(eventTarget, eventArgument, options) {
	$.ajax({
		type: 'POST',
		url: url,
		data: formDataBeforeAJAX,
		dataType: 'text',
		headers: {
			'X-FineUI-Ajax': true
		},
		success: function (data) {
		},
		error: function (xhr, textStatus) {
		},
		complete: function (xhr, textStatus) {
			ajaxComplete(xhr.responseText, textStatus, xhr);
		}
	});
}
	
(function() {
	if (!isUND(__doPostBack)) {
		__originalDoPostBack = __doPostBack;
		__doPostBack = _fjs_doPostBack;
	}
})();  

这是简化后的代码,可以看到FineUIPro重新赋值:__doPostBack=_fjs_doPostBack;

而在_fjs_doPostBack中,调用了jQuery.ajax来发起AJAX请求,当然实际的实现要复杂的多,FineUIPro让这一切变得透明起来,开发人员甚至不用写一行JavaScript代码就能享受jQuery.ajax的无刷新回发。

ASP.NET表格的回发(整个页面刷新)

浏览器中F12,打开Network选项卡,观察ASP.NET表格的分页回发过程:

FineUIPro控件库深度解析_第13张图片

可以看出:

1. ASP.NET表格页面回发是整个页面刷新,返回的是完整的HTML标签(包含html,head,body....)

2. 由于是页面重新渲染,所以页面资源会重新加载,比如common.css文件

 

FineUIPro表格的回发(AJAX部分刷新)

浏览器中F12,打开Network选项卡,观察FineUIPro表格的分页回发过程:

FineUIPro控件库深度解析_第14张图片

 

可以看出,请求参数中包含X-Requested-With=XMLHttpRequest参数,说明这是一个AJAX部分刷新过程

返回的响应正文如下所示:

FineUIPro控件库深度解析_第15张图片

这是一段JavaScript代码,其中包含表格当前页的数据,并通过表格的客户端API函数来重现加载表格数据。

由于是部分刷新,页面资源无需重新加载,整个页面DOM节点也无需重建,而且响应正文的大小也要小很多。

源代码下载

下载后放到FineUIPro官网示例源代码中即可:

https://files.cnblogs.com/files/sanshi/fineuipro_database_paging.zip

 

小结

经过上述分析,我们可以得知,FineUIPro使用JavaScript来渲染页面,并且使用jQuery.ajax来更新页面控件。

对于开发人员来说这一切都是透明的,开发人员只需要关注ASPX和C#代码,关注自己的业务既可以了,剩下的都丢给FineUIPro来处理。

 

你可能感兴趣的:(FineUIPro控件库深度解析)