HFSoft.MVC框架扩展表现逻辑功能
本文主要详细介绍通过HFSoft.MVC应用框架扩展数据分页查询功能。在该应用框架中并不能得到象DataGrid控件一样方便的数据表现控件,所有东西都通过传统的asp方式描述数据表现;看上去做一些重用性高的表现逻辑似乎是一件非常困难的事情。不过当你看完这编文章后可能对该框架的可扩展性有所改观。
在HFSoft.MVC应用框架中所有数据表现都依赖于接口,当需要扩展出某些表现逻辑时只需要用接口描述即可,并不需要象传统的Asp.Net服务器控件那样在扩展时需要了解一个些复杂的控件生命周期和处理事件机制。下面通过实现数据分页查询功能来体现框架的扩展性功能。
在做之前先分析一下数据分页查询的表现逻辑,Asp.Net的DataGrid控件就是一个很好的参考例子。明确表现逻辑后就制定相关接口:
/// <summary>
/// 数据分页描述
/// </summary>
public interface IDataInfo
{
int PageIndex
{
get;
set;
}
string OrderField
{
get;
set;
}
int PageCount
{
get;
}
int RecordCount
{
get;
set;
}
int PageSize
{
get;
set;
}
}
/// <summary>
/// 用于描述数据分页显示描述
/// </summary>
public interface IDataList
{
IDataInfo DataPaging
{
get;
set;
}
}
以上两种分页描述是针对不同情况的应用,IDataInfo用于描述分页信息成员存在于相关视图接口,IDataList用于描述分页信息成员存在于视图接口的某一对象成员下。实际应用中只有这两个接口描述是不够的,别忘了在分页和排序时还会存在相关查询条件参数;因此制定一个参数获取接口:
/// <summary>
/// 相关参数描述
/// </summary>
public interface IDataParam
{
UrlParma[] ParamUrl();
}
接下来要做的事情就是如何把描述接口转成相关HTML输出:
public class BindPaging
{
public static void ChangePage(HFSoft.MVC.IFormContext context, int index)
{
ChangePage(context, index, null);
}
public static void ChangePage(HFSoft.MVC.IFormContext context, int index, string orderfield)
{
System.Text.StringBuilder url = new StringBuilder();
if (context.View is IDataList)
{
BindUrl(((IDataList)context.View).DataPaging, index, orderfield, url);
}
else if (context.View is IDataInfo)
{
BindUrl((IDataInfo)context.View, index, orderfield, url);
}
BindParam(context.View as IDataParam, url);
HttpContext.Current.Response.Write(url.ToString());
}
}
打好基础后就可以根据自己的需求去实现一个分页功能的用户控件(由于框架是从WebForm改造下来的因此可以使用用户控件功能,因为Page和Controller是通过接口隔离,所以在Controller里是不能处理状态视图和接收相关事件处理).
<%@ Control Language="C#" AutoEventWireup="true" Inherits="HFSoft.MVC.BaseControl" %>
<%
HFSoft.MVC.Extends.IDataInfo pageinfo = HFSoft.MVC.Extends.BindPaging.GetPageInfo(this.FormContext);
%>
<table>
<tr>
<td >
<%=pageinfo.RecordCount %>条记录/共<%=pageinfo.PageSize %>页
</td>
<td style="width: 100px">
<% if(pageinfo.PageIndex <=0){ %>
首页
<%}else{%>
<a href="<%HFSoft.MVC.Extends.BindPaging.ChangePage(this.FormContext,0); %>">首页</a>
<% } %>
</td>
<td style="width: 100px">
<% if(pageinfo.PageIndex <=0){ %>
上一页
<%}else{%>
<a href="<%HFSoft.MVC.Extends.BindPaging.ChangePage(this.FormContext,pageinfo.PageIndex-1); %>">上一页</a>
<%} %>
</td>
<td style="width: 100px">
<% if(pageinfo.PageIndex >= pageinfo.PageCount-1){ %>
下一页
<%}else{%>
<a href="<%HFSoft.MVC.Extends.BindPaging.ChangePage(this.FormContext,pageinfo.PageIndex+1); %>">下一页</a>
<%} %>
</td>
<td style="width: 100px">
<% if (pageinfo.PageIndex >= pageinfo.PageCount - 1){ %>
未页
<%}else{%>
<a href="<%HFSoft.MVC.Extends.BindPaging.ChangePage(this.FormContext,pageinfo.PageCount-1); %>">未页</a>
<%} %>
</td>
</tr>
</table>
通过这种方式实现一个分页控件是不是一件很简单的事件,使用者并不需要了解服务器控件事件、生命周期等机制;只需要了解HTML表现就完全能够胜任。
当所有基础的构建完成后,就使用相关功能应用到实现开发中去。以下通过的例程介绍相关使用,例程描述: 数据表:MSSQL的NorthWind.Customers,根据Companyname,city,region等字段进行模糊匹配、分页和排序查询功能。
根据框架处理规则制定相关客户查询表现接口描述:
/// <summary>
/// 客户信息查询描述接口
/// </summary>
public interface ICustomerView : HFSoft.MVC.Extends.IDataList, HFSoft.MVC.Extends.IDataParam
{
/// <summary>
/// 查询结果的客户信息
/// </summary>
IList<NorthWind.Entities.Customers> Customers
{
get;
set;
}
/// <summary>
/// 查询相关条件
/// </summary>
CustomerFilter Filter
{
get;
set;
}
}
接口相关实现(后期打算如果程序集中没有该接口实现类,由组件内部构造实现):
public class CustomerViewImpl:mvc_PagingSample.Controller.ICustomerView
{
#region ICustomerView 成员
public IList<NorthWind.Entities.Customers> mCustomers = new List<NorthWind.Entities.Customers>();
public IList<NorthWind.Entities.Customers> Customers
{
get
{
return mCustomers;
}
set
{
mCustomers = value;
}
}
public mvc_PagingSample.Controller.CustomerFilter mFilter = new mvc_PagingSample.Controller.CustomerFilter();
[HFSoft.MVC.Bind()]
public mvc_PagingSample.Controller.CustomerFilter Filter
{
get
{
return mFilter;
}
set
{
mFilter = value;
}
}
#endregion
}
表现逻辑处理完成后就编写相关处理的Controller(根据接口描述的信息进行数据的查询):
[HFSoft.MVC.FormMapper("~/CustomerView.aspx")]
public void CustomerView(ICustomerView view)
{
IExpression exp = view.Filter.GetExpression();
FieldAdapter[] orderby = null;
if (!StaticFunction.IsEmpty(view.DataPaging.OrderField))
orderby = new FieldAdapter[] { new FieldAdapter(view.DataPaging.OrderField, null) };
view.DataPaging.PageSize = 10;
view.DataPaging.RecordCount = exp.CountOf<NorthWind.Entities.Customers>();
view.Customers = exp.List<NorthWind.Entities.Customers>(new Region(view.DataPaging.PageIndex*view.DataPaging.PageSize,
view.DataPaging.PageSize),orderby);
}
到这里整个查询表现的描述和处理已经完成,可能有朋友会奇怪CustomerViewImpl是如何创建并专入到CustomerView(ICustomerView view)中, CustomerViewImpl实例的成员值又是怎么创建?这一切你都不用关心,HFSoft.MVC框架已经帮你完成以上所有一切事情。
最后来看一下相关HTML页面的描述:
<table width="100%">
<tr>
<td>
<table>
<tr>
<td style="width: 100px">
CompanyName</td>
<td style="width: 100px">
<input id="Text1" name="CompanyName" type="text" /></td>
<td style="width: 100px">
City</td>
<td style="width: 100px">
<input id="Text3" name="City" type="text" /></td>
</tr>
<tr>
<td style="width: 100px">
Region</td>
<td style="width: 100px">
<input id="Text2" name="Region" type="text" /></td>
<td style="width: 100px">
<input id="Submit1" type="submit" value="查询" /></td>
<td style="width: 100px">
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td >
<table width="100%">
<tr>
<td style="width: 100px"><a href="<%BindPaging.ChangePage(context,datainfo.PageIndex,"CompanyName");%>">CompanyName</a>
</td>
<td style="width: 100px"><a href="<%BindPaging.ChangePage(context,datainfo.PageIndex,"ContactName");%>">ContactName</a>
</td>
<td style="width: 100px"><a href="<%BindPaging.ChangePage(context,datainfo.PageIndex,"ContactTitle");%>">ContactTitle</a>
</td>
<td style="width: 100px"><a href="<%BindPaging.ChangePage(context,datainfo.PageIndex,"City");%>">City</a>
</td>
<td style="width: 100px"><a href="<%BindPaging.ChangePage(context,datainfo.PageIndex,"Region");%>">Region</a>
</td>
<td style="width: 100px"><a href="<%BindPaging.ChangePage(context,datainfo.PageIndex,"Country");%>">Country</a>
</td>
</tr>
<%
foreach( NorthWind.Entities.Customers item in items)
{
%>
<tr>
<td style="width: 100px"><%context.ToValue(item.CompanyName); %>
</td>
<td style="width: 100px"><%context.ToValue(item.ContactName); %>
</td>
<td style="width: 100px"><% context.ToValue(item.ContactTitle); %>
</td>
<td style="width: 100px"><% context.ToValue(item.City); %>
</td>
<td style="width: 100px"><% context.ToValue(item.Region); %>
</td>
<td style="width: 100px"><% context.ToValue(item.Country);%>
</td>
</tr>
<%} %>
</table>
</td>
</tr>
<tr>
<td >
<uc1:Pagebar id="Pagebar1" runat="server">
</uc1:Pagebar></td>
</tr>
</table>