前言:
ObjectDataSource数据源控件优点甚多,确实令人爱不惜手,但不支持重绑定这一项确实让人失望。下面的实战二将通过ObjectDataSource配合GridView来实现删、改、分页、排序,并分析使用cache后排序失灵的原因。
实战:
1.效果:
图1.显示状态
图2.编辑状态
2.代码:
.aspx
<asp:ObjectDataSource runat="server" ID="ods" EnablePaging="true" TypeName="OdsDataManager"
SelectCountMethod="GetRecordCount" SelectMethod="GetRecord" UpdateMethod="UpdateRecord"
DeleteMethod="DelRecord" OnUpdating="ods_OnUpdating" SortParameterName="sortExpression">
</asp:ObjectDataSource>
<asp:GridView runat="server" ID="gv" DataSourceID="ods" AutoGenerateColumns="false"
AllowPaging="true" PageSize="1" AllowSorting="true" DataKeyNames="ID"
OnRowDataBound="gv_OnRowDataBound">
<HeaderStyle BackColor="graytext" />
<Columns>
<asp:TemplateField>
<HeaderStyle Width="20%" />
<HeaderTemplate>
<asp:LinkButton runat="server" ID="lbtnSortName" Text="Name" CommandName="Sort" CommandArgument="Name">
</asp:LinkButton>
</HeaderTemplate>
<ItemTemplate><%#Eval("Name") %></ItemTemplate>
<EditItemTemplate>
<asp:TextBox runat="server" ID="tbxName" Text='<%#Bind("Name") %>'></asp:TextBox>
<asp:RegularExpressionValidator runat="server" ID="revName" ControlToValidate="tbxName"
ValidationExpression="[a-zA-Z]+" ErrorMessage="Please input your English name!" Display="Dynamic">
</asp:RegularExpressionValidator>
<asp:RequiredFieldValidator runat="server" ID="rfvName" ControlToValidate="tbxName"
ErrorMessage="Please input your name" Display="Dynamic">
</asp:RequiredFieldValidator>
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<HeaderStyle Width="10%"/>
<HeaderTemplate>
<asp:LinkButton runat="server" ID="lbtnSortSex" Text="Sex" CommandName="Sort" CommandArgument="Sex">
</asp:LinkButton>
</HeaderTemplate>
<ItemTemplate>
<asp:RadioButtonList Enabled="false" runat="server" ID="rblSexShow" RepeatDirection="Horizontal"
RepeatColumns="2">
</asp:RadioButtonList>
</ItemTemplate>
<EditItemTemplate>
<asp:RadioButtonList runat="server" ID="rblSexEdit" RepeatDirection="Horizontal" RepeatColumns="2">
</asp:RadioButtonList>
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<HeaderStyle Width="20%"/>
<HeaderTemplate>
<asp:LinkButton runat="server" ID="lbtnSortCountry" Text="Country" CommandName="Sort" CommandArgument="Country">
</asp:LinkButton>
</HeaderTemplate>
<ItemTemplate><%#Eval("Country")%></ItemTemplate>
<EditItemTemplate>
<asp:DropDownList runat="server" ID="ddlCountry"></asp:DropDownList>
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<HeaderStyle Width="20%"/>
<HeaderTemplate>Hobby</HeaderTemplate>
<ItemTemplate><%#Eval("Hobby") %></ItemTemplate>
<EditItemTemplate>
<asp:CheckBoxList runat="server" ID="cbxlHobby" RepeatDirection="Horizontal" RepeatColumns="5">
</asp:CheckBoxList>
</EditItemTemplate>
</asp:TemplateField>
<asp:CommandField ShowDeleteButton="true" DeleteText="Delete" ShowEditButton="true" EditText="Edit" />
</Columns>
<PagerSettings Visible="true" />
<PagerStyle Font-Size="12px"/>
<PagerTemplate>
<div style="float:left;margin-left:15px;color:#999;line-height:20px">
当前第<%#this.gv.PageIndex+1 %>/<%#this.gv.PageCount %>页
</div>
<div style="float:right;margin-right:15px;color:#999;line-height:20px">页</div>
<div style="float:right">
<asp:DropDownList runat="server" ID="ddlPaging" AutoPostBack="true"
OnSelectedIndexChanged="ddlPaging_OnSelectedIndexChanged">
</asp:DropDownList>
</div>
<div style="float:right;color:#999;line-height:20px">跳转到第</div>
</PagerTemplate>
</asp:GridView>
说明:
1.因用了数据源控件,所以Name在编辑状态时使用<%#Bind("Name")%>来实现双向通讯的绑定
2.因为没有添加的功能,所以用了asp:CommandField来实现编辑、删除等按钮的功能。
3.排序功能上只要在ods上设定SortParameterName,它的值就是SelectMethod中关于排序的参数的名称,然后设定 GridView的AllowSorting为true就ok了。排序按钮上依然用到GridView内置的CommandName——Sort,然后 CommandArgument设为要排序的字段名,至于排序的方向由ObjectDataSource负责,省心多了。
.aspx.cs代码
public partial class Default2 : System.Web.UI.Page
{
private OdsDataManager dm = new OdsDataManager();
protected void Page_Load(object sender, EventArgs e)
{
}
protected void gv_OnRowDataBound(object sender, GridViewRowEventArgs e)
{
DataRowView drv = e.Row.DataItem as DataRowView;
if (e.Row.RowType == DataControlRowType.DataRow)
{
//显示时
if (this.gv.EditIndex == -1)
{
//设置性别
RadioButtonList rbl = e.Row.FindControl("rblSexShow") as RadioButtonList;
rbl.Items.Add(new ListItem("Male", "M"));
rbl.Items.Add(new ListItem("Female", "F"));
if ((drv["Sex"] as string).ToLower().Equals("m"))
rbl.Items[0].Selected = true;
else
rbl.Items[1].Selected = true;
}
//修改时:
else if (e.Row.RowIndex == this.gv.EditIndex)
{
//性别
RadioButtonList rbl = e.Row.FindControl("rblSexEdit") as RadioButtonList;
rbl.Items.Add(new ListItem("Male", "M"));
rbl.Items.Add(new ListItem("Female", "F"));
if ((drv["Sex"] as string).ToLower().Equals("m"))
rbl.Items[0].Selected = true;
else
rbl.Items[1].Selected = true;
//国籍
DropDownList ddlCountry = e.Row.FindControl("ddlCountry") as DropDownList;
DataTable countryDt = dm.GetCountry();
ListItem li = null;
for (int i = 0; i < countryDt.Rows.Count; ++i)
{
string cn = countryDt.Rows[i]["cn"] as string;
li = new ListItem(cn, cn);
if (cn.Equals(drv["Country"] as string))
li.Selected = true;
ddlCountry.Items.Add(li);
}
//兴趣
CheckBoxList cbl = e.Row.FindControl("cbxlHobby") as CheckBoxList;
DataTable hobbyDt = dm.GetHobby();
string hobbys = drv["Hobby"] as string;
ListItem hobbyLi = null;
string hstr = string.Empty;
for (int i = 0; i < hobbyDt.Rows.Count; i++)
{
hstr = hobbyDt.Rows[i]["hobby"] as string;
hobbyLi = new ListItem(hstr, hstr);
if (hobbys.IndexOf(hstr) >= 0)
hobbyLi.Selected = true;
cbl.Items.Add(hobbyLi);
}
}
}
else if (e.Row.RowType == DataControlRowType.Pager)
{
//绑定分页控件
DropDownList ddlPaging = e.Row.FindControl("ddlPaging") as DropDownList;
for (int i = 0; i < this.gv.PageCount; i++)
{
ddlPaging.Items.Add(new ListItem(Convert.ToString(i + 1), Convert.ToString(i)));
}
ddlPaging.SelectedIndex = this.gv.PageIndex;
}
}
/// <summary>
/// 分页控件的OnSelectedIndexChanged
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ddlPaging_OnSelectedIndexChanged(object sender, EventArgs e)
{
this.gv.PageIndex = (sender as DropDownList).SelectedIndex;
}
protected void ods_OnUpdating(object sender, ObjectDataSourceMethodEventArgs e)
{
string Sex = (this.gv.Rows[this.gv.EditIndex].FindControl("rblSexEdit") as RadioButtonList).SelectedValue;
string Country = (this.gv.Rows[this.gv.EditIndex].FindControl("ddlCountry") as DropDownList).SelectedValue;
System.Text.StringBuilder hobbys = new System.Text.StringBuilder();
foreach (ListItem li in (this.gv.Rows[this.gv.EditIndex].FindControl("cbxlHobby") as CheckBoxList).Items)
{
if (li.Selected)
hobbys.Append(li.Value+",");
}
if (hobbys.Length >= 2)
hobbys.Remove(hobbys.Length - 1, 1);
e.InputParameters.Add("Sex", Sex);
e.InputParameters.Add("Country", Country);
e.InputParameters.Add("Hobby",hobbys.ToString());
}
}
说明:
1.看到behind code是不是发现代码量少了很多呢?这就是用ods的好处了。
2.在更新操作时,因为Country、Sex和Hobby都没有和ods作双向绑定,所以要自己获取并写入到ods的InputParameters中,然后ods就会调用已经设置好的UpdateMethod了。
数据操作类
public class OdsDataManager
{
private static DataTable dt = null;//用户记录
private static DataTable countryDt = null;//国籍
private static DataTable hobbyDt = null;//兴趣
public OdsDataManager()
{
if (dt == null)
{
dt = new DataTable();
dt.Columns.Add("ID");
dt.Columns.Add("Name");
dt.Columns.Add("Sex");
dt.Columns.Add("Country");
dt.Columns.Add("Hobby");
//Default Data
dt.Rows.Add(new object[] { 1, "Mary", "F", "China", "Cooking,Music" });
dt.Rows.Add(new object[] { 2, "John", "M", "China", "Tennis" });
}
if (countryDt == null)
{
countryDt = new DataTable();
countryDt.Columns.Add("cn");
//Default Data
countryDt.Rows.Add(new object[] { "China" });
countryDt.Rows.Add(new object[] { "French" });
countryDt.Rows.Add(new object[] { "America" });
countryDt.Rows.Add(new object[] { "Afria" });
countryDt.Rows.Add(new object[] { "Japan" });
}
if (hobbyDt == null)
{
hobbyDt = new DataTable();
hobbyDt.Columns.Add("hobby");
//Default Data
hobbyDt.Rows.Add(new object[] { "Cooking" });
hobbyDt.Rows.Add(new object[] { "Music" });
hobbyDt.Rows.Add(new object[] { "Reading" });
hobbyDt.Rows.Add(new object[] { "Movies" });
hobbyDt.Rows.Add(new object[] { "Tennis" });
}
}
public DataTable GetRecord(int maximumRows, int startRowIndex, string sortExpression)
{
//排序
if(!string.IsNullOrEmpty(sortExpression))
{
dt.DefaultView.Sort = sortExpression;
}
DataRow[] drs = dt.Select();
DataTable dt1 = dt.Clone();
for (int i = startRowIndex; i < startRowIndex+maximumRows && i<drs.Length; i++)
{
dt1.Rows.Add(drs[i].ItemArray);
}
return dt1;
}
public int GetRecordCount()
{
return dt.Rows.Count;
}
public bool UpdateRecord(int ID, string Name, string Sex, string Country, string Hobby)
{
bool result = false;
DataRow[] drs = dt.Select("ID=" + ID);
if (drs.Length == 1)
{
drs[0]["Name"] = Name;
drs[0]["Sex"] = Sex;
drs[0]["Country"] = Country;
drs[0]["Hobby"] = Hobby;
result = true;
}
return result;
}
public bool DelRecord(int ID)
{
bool result = false;
DataRow[] drs = dt.Select("ID=" + ID);
if (drs.Length == 1)
{
dt.Rows.Remove(drs[0]);
result = true;
}
return result;
}
public DataTable GetCountry()
{
return countryDt;
}
public DataTable GetHobby()
{
return hobbyDt;
}
}
说明:
1.GetRecord方法绑定到ods的SelectMethod上,因为启用分页和排序功能,所以参数数组中必须有maximumRows(每 页记录数), startRowIndex(当前页首条记录在整个数据集中的索引), sortExpression(排序表达式,首次加载页面时为空字符串,postback时含排序字段和排序方向)。
3.数据缓存
ods可以启用cache,该cache为应用程序级的,就是多个画面的ods只要SelectMethod和 SelectCountMethod、Select参数一样就可以共享缓存中的数据,在Cache有效时进行Select操作将会先根据前面说的三个要素 从Cache中获取数据,如果没有才执行SelectMethod方法。注意不同的要素组合会各自对应一份缓存的数据,当第二次请求时就直接读缓存。
就是因为这样问题就来了,如果启用了cache那么上面的排序功能就会失效,而其他功能依然正常。原因在于排序操作是在 SelectMethod中实现,而在Cache生效时程序根本就不执行SelectMethod方法,除非说内存不足或其他原因令cache不够大来保 存数据而被迫执行SelectMethod方法。对于该问题目前还没找到解决的方法,望大哥们来告诉我啦^_^