摘要:ASP.NET2.0编程技术正是得益于万维网WEB通信的快速、简洁等独特优势,逐渐被大多数程序员和管理者接受和青睐。文内试以一个简单的企业应用实例,阐述ASP(ACTIVE SERVER PAGE).NET V2.0技术的优点和技巧,通过应用的WEB控件详细说明如何传递数据,操作数据库,包括:连接、查询、插入、删除、更新等,并实现了自动化无闪刷新。系统由笔者独立开发,测试通过并投入实际应用,获得了较好的评价。
关键字:B/S结构,AJAX技术,OLEDB数据库引擎,网页局部无闪刷新,记录集自动刷新,JavaScript语言,动态数据绑定控件,GridView控件。
2008年10月起,天津港(集团)有限公司在全港范围内开始实施免费的统一就餐制度,员工可在工作岗位享受免费的午餐。为了促进这一工作的实施,同时也体现和发挥计算机系统管理、网络通信的安全快速等优越性,笔者独立开发了B/S结构的企业订餐系统。系统采用先进的ASP.NET2.0+C#2005语言编写,融入了AJAX技术,数据库暂时采用ACCESS2003,可移植到ORACLE数据库生产服务器。
“B/S”是英文Browser/Server的缩写,即:浏览器/服务器模式结构。随着Internet和Intranet的WEB模式诞生,B/S这种基于网络存储结构的读取访问模式也应运而生。它的客户端是WINDOWS操作系统的浏览器,大多数功能放在服务器端运行,简化了系统的开发、维护和使用。其最显著优点是,只要计算机联网,用户不必安装任何客户端程序就能进入系统,客户端实现了零维护,系统功能扩展更加方便。通过B/S结构,用户界面完全通过万维网WWW浏览器操作,少量任务在客户端实现,主要事务逻辑由服务器处理,系统效率极高!
随着B/S框架的普及应用,很大程度上推动了AJAX技术的发展。AJAX解释为异步JavaScript和XML,英文:Asynchronous JavaScript and XML,是一种创建交互式网页应用的网页开发技术。最大优点在于能在不更新整个页面的前提下操作数据,使Web应用程序更为迅捷地回应客户指令,避免了网络上发送没有任何变化的信息。它的程序在客户端电脑进行处理,增加了交互性,关键是可以局部实时刷新,进行无刷新请求数据处理,从而实现局部交换客户端和服务器的数据。尤其针对数据库记录的检索,可以避免整个网页的刷新闪动,虽然是和服务器的数据库进行数据交换,但用户永远感觉是在客户端执行操作,迅速直接。进而引用AJAX的Timer定时控件,可达到客户端记录集自动无闪刷新。
OLEDB就是对象链接和嵌入数据库,它位于ODBC层与应用程序之间。先进的ADO连接数据库基本分为三类:SYSTEM DSN Connection,DSN-LESS Connection 和 OLEDB Connection。SYSTEM DSN 和 DSN-LESS Connection 都是通过 ODBC 与数据库进行连接,效率不是特别高,因为ODBC要通过自己的ODBC Provider、 ODBC自身以及Driver三层才能连接;然而OLE DB直接通过自己的Provider连接到数据库。所以,OLE DB方式更直接,连接数据库比ODBC快,检索数据更快,服务器端的游标性能得到提升。
一. 用户登录的实现:
通过客户端的JavaScript语言,判断客户页面输入字符的有效性;使用OleDB数据库引擎打开ACCESS数据库,检查用户和密码是否合法。
1.利用JAVA语言在客户端检查输入的字符是否合法。 | |
HTML语言: <script language="javascript"> <!-- function IsEmpty() //客户端检查是否输入字符 { //获得输入的用户名 var name=document.getElementById("txtUserID"); if(name.value=="") { alert("用户不能为空!"); name.focus(); return false; } //获得输入的密码 var pwd=document.getElementById("txtPwd"); if(pwd.value=="") { alert("密码不能为空!"); pwd.focus(); return false; } return true; } | C#语句: protected void Page_Load(object sender, EventArgs e) { //设置控件焦点函数 setFOCUS();
//若首次打开网页,执行以下语句 if (!Page.IsPostBack) { //“登录”按钮“点击”事件调用IsEmpty()函数 this.btLogin.Attributes.Add("onclick", "javascript:return IsEmpty();"); //“退出”按钮加入关闭确认提示 this.btExit.Attributes.Add("onclick", "window.close()"); } }
//刷新网页,焦点指向文本框 protected void setFOCUS() { string script = "<script language='JavaScript'> document.getElementById('txtUserID').focus();</script>"; Page.RegisterStartupScript("", script); } |
解释:通过向WEB页的Button按钮加入onClick属性事件,执行客户端的 javaScript函数IsEmpty()判断是否输入数据。使用了RegisterStartupScript()方法调用Java语句,实现控件焦点指向,通过此方法执行的脚本位于 Web 窗体的尾端,要在脚本运行前定义需要操作的 HTML 元素,确保文本框 HTML 标记位于设置其焦点的脚本之前;与之对应的是RegisterClientScriptBlock() 方法,它是为响应客户端事件而执行的脚本代码,其发送的脚本位于 Web 页面的首端,和Response.Write()方法的含义相同。显然,它们区别在于“何处”和“何时”发送脚本! | |
注:WEB服务端,C#执行javaScript实现客户端交互的三种方法: 1) this.buttonID.Attributes.Add("onclick", "javascript:return function();"); 2) Response.Write("<script language=javascript>window.alert('message');</script>"); 3) Page.RegisterStartupScript("",脚本);或者Page.RegisterClientScriptBlock ("",脚本); |
2.通过OleDB方式动态连接数据库,判断用户名和密码是否合法。 |
Web.config的<configuration>内定义数据库连接字符串 <appSettings> <add key="connStr" value="provider=Microsoft.Jet.oledb.4.0;data source=|DataDirectory|/dinner.mdb;"/> </appSettings> C#语句: using System.Data.OleDb; //引入数据命名空间 protected static string connStr = ConfigurationSettings.AppSettings["connStr"]; //引用数据库连接字符串
protected void subLogin() //登录验证过程 { string userID = Request.Form["txtuserID"]; string userPWD = Request.Form["txtPWD"];
OleDbConnection connDB = new OleDbConnection(connStr); //OleDb数据链接的初始化 connDB.Open(); //打开数据库连接 string strSql="select user_id,user_pwd,user_nam,UI.dept_id as dept_id,dept_nam,level_id from user_info UI,dept_info DI where UI.user_id='" + userID + "' and UI.dept_id=DI.dept_id"; OleDbCommand cmd=new OleDbCommand(strSql,connDB); //执行查询语句 OleDbDataReader drd=cmd.ExecuteReader(); if (drd.Read()) //查出此用户 { if (userPWD == Convert.ToString(drd["user_pwd"])) //密码正确 { //赋值给SESSION变量,全局使用 Session["userID"] = Convert.ToString(drd["user_id"]); Session["userNam"] = Convert.ToString(drd["user_nam"]); Session["deptID"] = Convert.ToString(drd["dept_id"]); Session["deptNam"] = Convert.ToString(drd["dept_nam"]);
string levelID = Convert.ToString(drd["level_id"]); if (levelID == "IN") Response.Redirect("choice.aspx"); //订餐网页 else if (levelID == "AD") Response.Redirect("submit.aspx"); //审核网页 } else Response.Write("<script language=javascript>window.alert('密码错误!');</script>"); } else Response.Write("<script language=javascript>window.alert('无此用户!');</script>"); drd.Close(); //关闭数据集 connDB.Close(); //关闭数据库连接 } |
解释:通过OleDb技术连接数据库,使用SQL结构化查询语句判断用户、密码是否合法。按照登录者的级别,链接到不同的网页。下面,简介三种数据库操作方法: OleDbConnection Conn = new OleDbConnection(connStr); //建立数据库连接 Conn.Open(); //打开连接 string strSql = “SQL语句”; 1) OleDbCommand cmd = new OleDbCommand(strSql, Conn); //执行语句,无返回记录集 cmd.ExecuteNonQuery(); //如:delete,insert,update,drop table…… 2) OleDbCommand cmd = new OleDbCommand(strSql, Conn); //执行语句,返回只读的记录集 OleDbDataReader drd=cmd.ExecuteReader(); //从数据源读取数据行,如:select if (drd.Read()) {session[“userID”]=drd[“userID”];} //如果存在,赋值给session变量 3) OleDbDataAdapter rs = new OleDbDataAdapter(strSql, Conn); //执行语句,使用数据适配器 DataSet ds = new DataSet(); //定义本地表集合 rs.Fill(ds); //把服务器端的表映射到本地表集合,使用无连接传输模式 controlID.DataSource = ds.Tables[0].DefaultView; //绑定数据到WEB控件 controlID.DataBind(); 应用数据适配器,可以更新记录集,包括:InsertCommand、UpdateCommand、DeleteCommand和SelectCommand四种方法。如果数据量操作较大,OleDbCommand方法会比OleDbDataAdapter快速。 Conn.Close(); //关闭连接 |
注:用户权限“IN”代表订餐,“AU”代表审批和汇总。 |
二. 用户订餐的实现:
通过新型的WEB控件RadioButtonList和DropDownList绑定数据,进行选餐操作,可以显示对应的菜品图片。应用新型的GridView控件实时显示已订餐信息,并在其内嵌入了删除功能。利用AJAX技术的ScriptManager和UpdatePanel,实现无闪局部刷新,使GridView显示订餐数据时,不必整个网页提交刷新,进而可以使用Timer定时控件实现自动刷新记录。
1.应用新型的RadioButtonList、DropDownList数据控件和RequiredFieldValidator、RangeValidator验证控件实现选餐操作。 | |
HTML语言:定义上述三种WEB控件 <asp:RadioButtonList ID="rdlDinnerClass" runat="server" DataSourceID="AccessDataSource1" DataTextField="class_rmk" DataValueField="dinner_class" RepeatDirection="Horizontal" Width="166px" AutoPostBack="True" Font-Size="Medium" OnSelectedIndexChanged="rdlDinnerClass_SelectedIndexChanged" > </asp:RadioButtonList> <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="rdlDinnerClass" ErrorMessage="民族不能为空"></asp:RequiredFieldValidator>
<asp:DropDownList ID="ddlDinnerId" runat="server" Font-Size="Medium" Width="166px" AutoPostBack="True" OnSelectedIndexChanged="ddlDinnerId_SelectedIndexChanged" > </asp:DropDownList> <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ControlToValidate="ddlDinnerId" ErrorMessage="菜名不能为空"></asp:RequiredFieldValidator> <asp:TextBox ID="txtDinnerCnt" runat="server" Width="66px" Font-Size="Medium"></asp:TextBox> <asp:RangeValidator ID="RangeValidator1" runat="server" ControlToValidate="txtDinnerCnt" ErrorMessage="请输入合法数值" MaximumValue="99"MinimumValue="1"SetFocusOnError="True" Type="Integer"Width="113px"> </asp:RangeValidator> <asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" ControlToValidate="txtDinnerCnt" ErrorMessage="数量不能为空" Width="120px" SetFocusOnError="True"></asp:RequiredFieldValidator></td> | C#语句: protected void rdlDinnerClass_SelectedIndexChanged(object sender, EventArgs e) //选定民族 { string str1 = this.rdlDinnerClass.SelectedValue; //连接数据库 OleDbConnection Conn = new OleDbConnection(connStr); Conn.Open(); string strSql = "select * from dinner_info where dinner_class='" + str1 + "'";
//结果集 对象 OleDbDataAdapter rs = new OleDbDataAdapter(strSql, Conn ); //集合 DataSet ds = new DataSet(); //把rs给了ds rs.Fill(ds); //动态绑定dropdownlist控件 ddlDinnerId.DataSource = ds.Tables[0].DefaultView; ddlDinnerId.DataTextField ="dinner_rmk"; ddlDinnerId.DataValueField = "dinner_id"; ddlDinnerId.DataBind(); Conn.Close();
//赋值给dinnerId控件 ddlDinnerId.SelectedIndex = 0; string str2 = this.ddlDinnerId.SelectedValue; //显示对应的图片 this.imgDinner.ImageUrl = "~/bmp/" + str1 + str2 + ".jpg"; }
protected void ddlDinnerId_SelectedIndexChanged(object sender, EventArgs e) //选定餐名 { string str1 = this.rdlDinnerClass.SelectedValue; string str2 = this.ddlDinnerId.SelectedValue; //显示对应的图片 this.imgDinner.ImageUrl = "~/bmp/" + str1+str2 +".jpg"; } |
解释:通过单击控件rdlDinnerClass(民族)触发执行查出相应的菜名,动态绑定到ddlDinnerId(餐名)控件,同时通过两个控件的selectedValue显示出相应文件名的图片。应用各自对应的RequiredFieldValidator和RangeValidator控件,判断数据合法,才能进行提交操作。 | |
注:RadioButtonList和DropDownList的AutoPostBack设为True,才能触发SelectedIndexChanged事件。验证控件RequireFieldValidator判断是否输入数据;RangeValidator检查输入的数值是否符合某一范围,Type为Integer数值。 |
2.应用GridView控件显示当前用户的所有订餐信息,控件嵌入了删除功能,并且,页脚能显示订餐的合计量。 | |
HTML语言:定义GridView控件的数据列,加入了一列按钮“删除”,并提示用户确认执行。 <asp:GridView ID="gvChoice" runat="server" AutoGenerateColumns="False" OnRowCommand="gvChoice_RowCommand" OnRowDataBound="gvChoice_RowDataBound" CellPadding="4" ForeColor="Black" GridLines="None" Width="549px" EmptyDataText="没有任何数据可以显示。" ShowFooter="True"> <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" /> <Columns> <asp:BoundField DataField="ID" HeaderText="ID" /> <asp:BoundField DataField="dinner_rmk" HeaderText="餐名" /> <asp:BoundField DataField="dinner_cnt" HeaderText="数量" > <ItemStyle HorizontalAlign="Right" /> </asp:BoundField> <asp:BoundField DataField="class_rmk" HeaderText="民族" /> <asp:BoundField DataField="rec_tim" HeaderText="时间" DataFormatString="{0:yyyy-MM-dd HH:mm:ss}" HtmlEncode="False" /> <asp:BoundField DataField="dinner_status" HeaderText="状态" > <ItemStyle Font-Bold="True" /> </asp:BoundField> //删除确认提示功能 <asp:ButtonField CommandName="Del" HeaderText="功能" Text="<div id="de" οnclick="JavaScript:return confirm('确定删除吗?')">删除</div> " /> <asp:BoundField DataField="dinner_memo" HeaderText="备注" /> </Columns> <RowStyle BackColor="#EFF3FB" /> <EditRowStyle BackColor="#2461BF" /> <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" /> <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" /> <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" /> <AlternatingRowStyle BackColor="White" /> </asp:GridView> | C#语句: protected void gvChoice_RowCommand(object sender, GridViewCommandEventArgs e) //内嵌的删除功能 { //取得行的索引号 int idx = Convert.ToInt32(e.CommandArgument); GridViewRow selectedRow = gvChoice.Rows[idx]; //取得该行的ID号 TableCell tbID = selectedRow.Cells[0];
switch (e.CommandName) //判断按钮功能 { case " Del ": string strID = tbID.Text; OleDbConnection Conn = new OleDbConnection(connStr); Conn.Open(); string strSql = "delete from dinner_choice where dinner_status<>'01' and ID=" + strID; OleDbCommand cmd = new OleDbCommand(strSql, Conn ); cmd.ExecuteNonQuery(); Conn.Close(); break; } }
int cntTotal = 0; //初始化累计值变量 protected void gvChoice_RowDataBound(object sender, GridViewRowEventArgs e) { //页脚的合计数 if (e.Row.RowType == DataControlRowType.DataRow) //累计订餐数量 {cntTotal = cntTotal + Convert.ToInt32(e.Row.Cells[2].Text); } else if (e.Row.RowType == DataControlRowType.Footer) //如果页脚,则显示 e.Row.Cells[2].Text = "Total: " + cntTotal.ToString();
//如果是绑定数据行,则行变色 if (e.Row.RowType == DataControlRowType.DataRow) { //鼠标经过时,行背景色变 e.Row.Attributes.Add("onmouseover", "this.style.backgroundColor='#80FFFF'"); //鼠标移出时,行背景色恢复 e.Row.Attributes.Add("onmouseout", "this.style.backgroundColor='#FFFFFF'"); } } |
解释:删除时,程序加入“删除确认提示框”,避免误操作。利用GridView的RowCommand事件捕获当前操作记录的唯一ID号,进行删除。应用RowDataBound事件累计当前页的订餐总数,显示在GridView的页脚。设置Row的mouseover属性,加入赋值语句,实现指向行时变色。 | |
注:如果显示合计数,GridView的ShowFooter属性设置为True,并且,RowDataBound事件内累加计算的变量应该是全局变量。 |
3.利用AJAX技术实现局部无闪刷新和GridView定时刷新,使用户免操作实时可见订餐信息的反馈。 | |
HTML语言:定义AJAX的ScriptManager、UpdatePanel、Triggers以及Timer控件。 <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server"> <ContentTemplate>
GridView定义同上(2) </ContentTemplate>
<Triggers> <asp:AsyncPostBackTrigger ControlID="timChoice" EventName="Tick"/> </Triggers> </asp:UpdatePanel>
<asp:Timer ID="timChoice" runat="server" Interval="1000" OnTick="timChoice_Tick"> </asp:Timer> | C#语句: protected void Bind() //找出当前用户的订餐记录,显示到GridView { string strUserId = Convert.ToString(Session["userID"]); string strDeptId = Convert.ToString(Session["deptID"]); //连接数据库 OleDbConnection Conn = new OleDbConnection(connStr); Conn.Open(); string strSql = "select ID,dinner_rmk,dinner_cnt,class_rmk,rec_tim,iif(dinner_status='00','待确认',iif(dinner_status='01','已确认','不允许')) as dinner_status,dinner_memo from dinner_choice DC,dinner_info DI,dinner_class CR where DC.dinner_id=DI.dinner_id and DC.dinner_class=DI.dinner_class and DC.dinner_class=CR.dinner_class and DC.user_id='" + strUserId + "' and DC.dept_id='" + strDeptId + "' order by DC.rec_tim";
//创建结果集对象 OleDbDataAdapter rs = new OleDbDataAdapter(strSql, Conn ); //数据集合 DataSet ds = new DataSet(); //把rs给了ds rs.Fill(ds); //把ds记录集填充到GridView gvChoice.DataSource = ds.Tables[0].DefaultView; gvChoice.DataBind();
Conn.Close(); gvColor(); }
protected void timChoice_Tick(object sender, EventArgs e) //定时执行 { //定时查询出记录 Bind(); } |
解释:利用AJAX的ScriptManager和UpdatePanel相结合,实现页面无闪的局部GridView刷新记录,进一步设置Timer控件为每秒1次的定时刷新GridView显示内容。 | |
注:1)ScriptManager控件的EnablePartialRendering属性必须设置为True,实现页面的局部异步更新。 2)必须设置Timer控件为AsyncPostBackTrigger机制。UpdatePanel共有两个Trigger属性:AsyncPostBackTrigger和PostBackTrigger。前者指定某个控件的某个事件引发异步回传(asynchronous postback),即局部更新;属性有ControlID控件ID名和EventName执行事件名,若没有明确EventName,则采用控件的默认值,比如button就是Click,timer是Tick。如果ContorlID为UpdatePanel之外的控件,可以使外部控件也能控制 UpdatePanel更新。PostBackTrigger指定UpdatePanel内的某个控件引发整个页面的更新(即:Normal Postback)。 |
4.GRIDVIEW控件输出到Excel文件。 |
C#语句: protected void ctExl_Click(object sender, EventArgs e) { ToExcel(gvChoice, "Sum"+ System.DateTime.Now.ToShortDateString()); //调用导出函数 }
//输出到EXCEL public void ToExcel(System.Web.UI.WebControls.GridView ctl, string FileName) { Response.Clear(); Response.Charset = "UTF-8"; Response.ContentEncoding = System.Text.Encoding.Default; Response.ContentType = "application/ms-excel"; Response.AddHeader("content-disposition", "attachment;filename=" + FileName + ".xls"); System.IO.StringWriter stringWrite = new System.IO.StringWriter(); System.Web.UI.HtmlTextWriter htmlWrite = new HtmlTextWriter(stringWrite);
ctl.AllowPaging = false; //关闭分页 BindQuery(); //如果GRIDVIEW分页,必须调用过程刷新记录集 ctl.RenderControl(htmlWrite);
Response.Write(stringWrite.ToString()); Response.End(); ctl.AllowPaging = true; //启用分页 BindQuery(); //如果GRIDVIEW分页,必须调用过程刷新记录集 }
//必须重载VerifyRenderingInServerForm函数 public override void VerifyRenderingInServerForm(Control control) { // Confirms that an HtmlForm control is rendered for } |
解释:如果导出的GridView存在分页,需要增加以下内容: 1)HTML语言页标头必须增加EnableEventValidation的设置,否则,会出现异常错误。 <%@ Page Language="C#" EnableEventValidation="false" AutoEventWireup="true" CodeFile="summary.aspx.cs" Inherits="summary" %> 2)必须增加上面阴影所示的“关闭分页”、“刷新记录集”、“启用分页”、“刷新记录集”四项操作。 |
三. 订餐审批的实现:
利用新型的CheckBoxList、RadioButtonList、Calendar等WEB控件实现多条件的数据检索,并且GridView增加了CheckBox控件,可以多选或全选记录进行批量提交,GridView实现了分页显示。如果审批“不允许”,应用ShowModalDialog()函数调用页面窗口,选择备注内容。
1.利用C#语言灵活组成多条件的查询语句进行检索,使用户操作简单直观。 | |
HTML语言: <asp:CheckBoxList ID="cblDeptId" runat="server" DataSourceID="a1" DataTextField="dept_nam" DataValueField="dept_id" RepeatColumns="4" RepeatDirection="Horizontal" Width="339px" Height="19px"> </asp:CheckBoxList></td> <asp:Label ID="Label3" runat="server" Font-Bold="True" Text="状态" Width="20px"></asp:Label></td> <asp:CheckBoxList ID="cblDinnerStatus" runat="server" DataSourceID="a2" DataTextField="status_rmk" DataValueField="dinner_status" Width="90px"> </asp:CheckBoxList> <asp:Label ID="Label4" runat="server" Font-Bold="True" Text="范围" Width="19px"></asp:Label> <asp:RadioButtonList ID="rdlTab" runat="server" AutoPostBack="True" OnSelectedIndexChanged="rdlTab_SelectedIndexChanged" Width="90px"> <asp:ListItem Selected="True" Value="DAY">当天记录</asp:ListItem> <asp:ListItem Value="HIS">历史记录</asp:ListItem> </asp:RadioButtonList> <asp:CheckBoxList ID="cblDate" runat="server" AutoPostBack="True" Font-Bold="True" OnSelectedIndexChanged="cblDate_SelectedIndexChanged" Width="60px"> <asp:ListItem Value="BegT">开始日期</asp:ListItem> <asp:ListItem Value="EndT">结束日期</asp:ListItem> </asp:CheckBoxList> <asp:ImageButton ID="ibtBegT" runat="server" OnClick="ibtBegT_Click" Width="30px" Height="30px" ImageUrl="~/icon/CALEN.ICO" /><asp:TextBox ID="txtBegT" runat="server" Font-Bold="False" Font-Italic="False" Width="68px"></asp:TextBox><br /> <asp:ImageButton ID="ibtEndT" runat="server" Height="30px" Width="30px" ImageUrl="~/icon/CALEN.ICO" OnClick="ibtEndT_Click" /><asp:TextBox ID="txtEndT" runat="server" Width="68px"></asp:TextBox></td> <td align="right" style="width: 13992px; height: 28px"> <asp:Calendar ID="cldRecTim" runat="server" BackColor="#FFFFCC" BorderColor="#FFCC66" OnSelectionChanged="cldRecTim_SelectionChanged" ShowGridLines="True" Visible="False" Width="87px"> <SelectedDayStyle </asp:Calendar> | C#语句: protected void BindQuery() //组成查询条件 { int j = 0;
//部门 string strSql = " and DC.dept_id in ("; for (int i = 0; i < this.cblDeptId.Items.Count; i++) { if (cblDeptId.Items[i].Selected) { j = 1; strSql = strSql + "'" + cblDeptId.Items[i].Value + "',"; } }
strSql = strSql + "$$$"; strSql = strSql.Replace(",$$$", ")");
if (j == 0) { strSql = ""; }
//状态 string strSql2 = " and DC.dinner_status in ("; for (int i = 0; i < this.cblDinnerStatus.Items.Count; i++) { if (cblDinnerStatus.Items[i].Selected) { j = 2; strSql2 = strSql2 + "'" + cblDinnerStatus.Items[i].Value + "',"; } }
strSql2 = strSql2 + "$$$"; strSql2 = strSql2.Replace(",$$$", ")");
if (j == 2) { strSql = strSql + strSql2; }
//日期 if (cblDate.Items[0].Selected == true) { if (txtBegT.Text.Trim() == "" || ! IsDate(txtBegT.Text)) { return; } else strSql = strSql + " and DC.rec_tim>=#" + txtBegT.Text + "#"; }
if (cblDate.Items[1].Selected == true) { if (txtEndT.Text.Trim() == "" || ! IsDate(txtEndT.Text)) { return; } else strSql = strSql + " and DC.rec_tim<#" + txtEndT.Text + "#"; } //查询记录函数 Bind(strSql); }
protected void Bind(string Str) //查询记录 { string strTab; //判断查询当前记录还是历史记录 if (this.rdlTab.SelectedValue == "DAY") strTab = "dinner_choice"; else strTab = "dinner_choice_bak";
//连接数据库 OleDbConnection Conn = new OleDbConnection(connStr); Conn.Open();
string strSql = "select ID,dept_nam,user_nam,dinner_rmk,dinner_cnt,class_rmk,rec_tim,iif(dinner_status='00','待确认',iif(dinner_status='01','已确认','不允许')) as dinner_status,dinner_memo,app_tim from " + strTab + " DC,dinner_info DI,dinner_class CR,user_info UI,dept_info EI where DC.dinner_id=DI.dinner_id and DC.dinner_class=DI.dinner_class and DC.dinner_class=CR.dinner_class and DC.user_id=UI.user_id and DC.dept_id=EI.dept_id " + Str + " order by DC.rec_tim";
//结果集对象 OleDbDataAdapter rs = new OleDbDataAdapter(strSql, Conn ); //集合 DataSet ds = new DataSet(); //把rs给了ds rs.Fill(ds); //绑定到GridView gvSubmit.DataSource = ds.Tables[0].DefaultView; gvSubmit.DataBind();
Conn.Close();
gvColor(); } |
解释:使用AccessDataSource数据绑定控件向cblDeptId(部门)控件填充所有部门代码,向cblDinnerStatus(状态)控件填充所有状态码,提供日历格式的日期选择输入功能,最后通过BindQuery()函数将所有选定的条件(包括:部门代码、状态代码、日期范围等)组成SQL语句,交给Bind(string Str)数据查询函数,筛选记录并显示到GridView。而且,通过变量改变SQL语句中的表名,实现操作当前记录和查询历史记录。 |
2.用户输入的日期格式不正确,给出错误提示: | |
HTML语言: <asp:UpdatePanel ID="UpdatePanel3" runat="server"> <ContentTemplate>
<asp:Label ID="lblTimErr" runat="server" Font-Bold="True" ForeColor="Red" Width="120px"></asp:Label>
</ContentTemplate> </asp:UpdatePanel> | C#语句: //日期较验语句 if (cblDate.Items[0].Selected == true) { if (txtBegT.Text.Trim() == "" || ! IsDate(txtBegT.Text)) //调用日期判断函数 { lblTimErr.Text = "开始日期不合法,不能查询!"; return; } else strSql = strSql + " and DC.rec_tim>=#" + txtBegT.Text + "#"; }
if (cblDate.Items[1].Selected == true) { if (txtEndT.Text.Trim() == "" || ! IsDate(txtEndT.Text)) { lblTimErr.Text = "结束日期不合法,不能查询!"; return; } else strSql = strSql + " and DC.rec_tim<#" + txtEndT.Text + "#";
private bool IsDate(string s) //日期判断函数 { try { //利用时间函数的Parse方法 DateTime d = DateTime.Parse(s); return true; } catch { return false; } } } |
解释: 1)由于“查询”按钮已经作为AJAX的UpdatePanel1外部提交控件,所以不能应用response.write()提示“日期格式非法”。而且,页面布局使得日期错误提示不可能放在UpdatePanel1中,只能新增一个UpdatePanel3放置Label控件,利用UpdatePanel3的Label显示。需要注意,UpdatePanel3的UpdateMode必须为Always(总是更新:页面上任何一处发生的回发操作都会产生页局部更新),这才能被其它UpdatePanel的提交控件调用更新显示。 2)如果UpdateMode为Conditional,代表有条件更新,只能被自己的内外部控件执行更新。此外,UpdatePanel的ChildrenAsTriggers属性表明UpdatePanel内部控件引起的回发是否产生当前UpdatePanel控件的局部更新。如果UpdateMode是Always,那ChildrenAsTriggers必须为True,否则运行出错,该属性只在UpdateMode=Conditional时有意义,如果ChildrenAsTriggers=True,该UpdatePanel包括的控件所引发的异步回送都会更新该UpdatePanel,否则,不会实现异步回送。 3)所以,ChildrenAsTriggers和UpdateMode两个属性是互斥的。UpdateMode指定在UpdatePanel里面的提交控件是否总是用来触发刷新;而ChildrenAsTriggers用来表明UpdatePanel里面的提交控件是否用来刷新。 | |
注:此技术已经应用到订餐页面对于重复订餐的信息提示。 |
3.GridView实现分页显示和嵌入CheckBox复选框进行多选,实现批量提交。 | |
HTML语言: <asp:GridView ID="gvSubmit" runat="server" AutoGenerateColumns="False" CellPadding="4" onPageIndexChanging="gvSubmit_PageIndexChanging" EmptyDataText="没有任何数据可以显示。" ForeColor="Black" GridLines="None" Width="882px" OnRowDataBound="gvSubmit_RowDataBound" AllowPaging="True" ShowFooter="True" > //允许分页和显示页脚 <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" /> <Columns> <asp:TemplateField HeaderText="选择"> <ItemTemplate> <asp:CheckBox ID="chkGV" runat="server" /> //嵌入复选框 </ItemTemplate> <ControlStyle Font-Bold="True" Font-Size="Medium" /> </asp:TemplateField> <asp:BoundField DataField="ID" HeaderText="ID" /> <asp:BoundField DataField="dept_nam" HeaderText="部门" /> <asp:BoundField DataField="user_nam" HeaderText="姓名" /> <asp:BoundField DataField="dinner_rmk" HeaderText="餐名" /> <asp:BoundField DataField="dinner_cnt" HeaderText="数量" /> <asp:BoundField DataField="class_rmk" HeaderText="民族" /> <asp:BoundField DataField="rec_tim" DataFormatString="{0:yyyy-MM-dd HH:mm:ss}" HeaderText="订餐时间" HtmlEncode="False" /> <asp:BoundField DataField="dinner_status" HeaderText="状态" > <ItemStyle Font-Bold="True" /> <FooterStyle Font-Bold="True" /> </asp:BoundField> <asp:BoundField DataField="dinner_memo" HeaderText="备注" /> <asp:BoundField DataField="app_tim" DataFormatString="{0:yyyy-MM-dd HH:mm:ss}" HeaderText="审批时间" />
</Columns> <RowStyle BackColor="#EFF3FB" /> <EditRowStyle BackColor="#2461BF" /> <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" /> <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" /> <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" /> <AlternatingRowStyle BackColor="White" /> </asp:GridView> | C#语句: protected void gvSubmit_PageIndexChanging(object sender, GridViewPageEventArgs e) //分页显示 { gvSubmit.PageIndex = e.NewPageIndex; //显示所有页号 BindQuery(); //调用查询,刷新记录 }
protected void chkAll_CheckedChanged(object sender, EventArgs e) //全选和反选的实现 { if (chkAll.Checked != true) chkAll.Text = "全选"; else chkAll.Text = "取消";
for (int i = 0; i <= gvSubmit.Rows.Count - 1; i++) //遍历当前页所有记录 { CheckBox chk = (CheckBox)gvSubmit.Rows[i].FindControl("chkGV"); //“不允许”状态的记录不能操作 if (gvSubmit.Rows[i].Cells[0].Enabled == false) continue;
if (chkAll.Checked == true) { chk.Checked = true; } else { chk.Checked = false; } } }
//逐条记录判断GridView的复选框是否有效 private bool gvCheck(String swt) { bool j = false;
if (gvSubmit.Rows.Count == 0) { Response.Write("<script language=javascript>window.alert('当前没有任何记录!');</script>"); return (j); }
//开始逐条判断checkbox for (int i = 0; i <= gvSubmit.Rows.Count - 1; i++) { CheckBox chk = (CheckBox)gvSubmit.Rows[i].FindControl("chkGV"); //取出checkbox的值 if (chk.Checked ==true) { j=true ; break; } }
if (j == false) { Response.Write("<script language=javascript>window.alert('请至少选择一条记录!');</script>"); return (j); } } |
解释:通过GridView的PageIndexChanging事件触发显示页码,但每次必须刷新记录集,否则无法显示当前页记录。通过增加GridView一列为CheckBoxField,实现网格栏多选操作,使用循环语句逐一甄别是否选中,进行批量操作。 | |
注:1)GridView分页显示时,属性AllowPaging为True,PageSize设置每页显示的行数。2)页脚的合计数只是当前页的统计。 |
4.利用ShowModalDialog()函数弹出页面窗口,选择备注信息。 | |
HTML语言: <head runat="server"> <title>弹出窗体</title> <base target="_self"></base> </head>
<body> <form id="form1" runat="server" >
<div> <table style="width: 256px"> <tr> <td style="height: 2px" colspan="2"> <asp:AccessDataSource ID="AccessDataSource1" runat="server" DataFile="~/App_Data/dinner.mdb" SelectCommand="SELECT [dinner_memo], [memo_rmk] FROM [dinner_memo] ORDER BY [dinner_memo]"> </asp:AccessDataSource> </td> </tr> </table> <br /> <table style="width: 272px"> <tr> <td style="width: 18px"> </td> <td style="width: 28px"> </td> <td style="width: 28px"> </td> </tr> <tr> <td style="width: 18px; height: 9px"> </td> <td style="width: 28px; height: 9px"> <asp:Label ID="Label1" runat="server" Font-Bold="True" Font-Size="Medium" Text="请选择原因" Width="89px"></asp:Label></td> <td style="width: 28px; height: 9px"> <asp:DropDownList ID="ddlMemoTxt" runat="server" Font-Size="Medium" Width="141px" DataSourceID="AccessDataSource1" DataTextField="memo_rmk" DataValueField="memo_rmk"> </asp:DropDownList></td> </tr> <tr> <td align="left" style="width: 18px; height: 19px"> </td> <td align="left" style="width: 28px; height: 19px"> </td> <td align="left" style="width: 28px; height: 19px"> </td> </tr> <tr> <td align="left" style="width: 18px"> </td> <td align="left" style="width: 28px"> <asp:Button ID="btSubmit" runat="server" OnClick="btSubmit_Click" Text="确定" Font-Bold="True" Font-Size="Medium" /></td> <td align="left" style="width: 28px"> <asp:Button ID="btExit" runat="server" Font-Bold="True" Font-Size="Medium" OnClick="btExit_Click" Text="取消" /></td> </tr> </table>
</div> <br /> </form> </body> | C#语句: //弹出窗体函数 protected void showDialog() { StringBuilder s = new StringBuilder(); s.Append("<script language=javascript>" + "/n"); s.Append("var a=window.showModalDialog('submitBX.aspx',window,'dialogHeight:200px;dialogWidth:290px;edge:Raised;center:Yes;help:Yes;resizable:No;scroll:no;status:No;');" + "/n"); //弹出窗口 s.Append("if(a!=null)" + "/n"); s.Append("document.all('txtDialog').value=a;" + "/n"); //返回值填充 s.Append("</script>"); Type cstype = this.GetType(); ClientScriptManager cs = Page.ClientScript; string sname = "showD"; if (!cs.IsStartupScriptRegistered(cstype, sname)) cs.RegisterStartupScript(cstype, sname, s.ToString()); //执行命令集 }
//弹出的窗体返回值 protected void returnDX() { StringBuilder s = new StringBuilder(); s.Append("<script language=javascript>" + "/n"); s.Append("window.returnValue='" + this.GetSelectValue() + "';" + "/n"); //获得返回值 s.Append("window.close();" + "/n"); s.Append("</script>"); Type cstype = this.GetType(); ClientScriptManager cs = Page.ClientScript; string csname = "returnD"; if (!cs.IsStartupScriptRegistered(cstype, csname)) cs.RegisterStartupScript(cstype, csname, s.ToString()); //执行命令集 } private string GetSelectValue() { string rv = this.ddlMemoTxt.SelectedValue; //获得选定的备注内容 return rv; } |
解释:利用本地JAVA建类的方式,执行JavaScript指令集:调用showModalDialog()函数弹出窗口,并返回值填充到txtDialog文本框。 |
4.利用AJAX技术的Timer控件实时显示当前时间,类似电子秒表。 | |
HTML语言: <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager> <asp:Label ID="Label7" runat="server" Font-Bold="True" ForeColor="Red" Text="当前时间:" Width="95px"></asp:Label> <asp:UpdatePanel ID="UpdatePanel2" runat="server"> <ContentTemplate> <asp:Label ID="lblTim" runat="server" Width="392px" Font-Bold="True" ForeColor="Green" Font-Size="Medium"></asp:Label> </ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID="timShow" EventName="Tick"/> </Triggers> </asp:UpdatePanel> <asp:Timer ID="timShow" runat="server" OnTick="timShow_Tick" Interval="1000" ></asp:Timer> | C#语句: protected void timShow_Tick(object sender, EventArgs e) //定时显示日期和时间 { lblTim.Text = System.DateTime.Now.GetDateTimeFormats('T')[0].ToString() + " " + System.DateTime.Now.GetDateTimeFormats('D')[3].ToString(); } |
解释:Timer控件的Interval=1000代表每秒执行一次,UpdatePanel的Triggers必须设置为异步回送AsyncPostBackTrigger。 |
四. 订餐汇总的实现:
通过C#语言动态改变GridView控件的显示列数,使含有不同字段数的数据源可以填充到同一个GridView控件,简化了界面设计,程序更为灵活。可以分部门和分餐名统计总量,也可统计每个部门每一种餐的订餐量。
1.通过网页传递参数,使汇总界面可以统计当前表和历史表,并对当前表“完结”操作。 |
C#语句: //审批页面调出“汇总”页面 protected void btSum_Click(object sender, EventArgs e) { string strTab; if (this.rdlTab.SelectedValue == "DAY") strTab = "dinner_choice"; else strTab = "dinner_choice_bak"; Response.Redirect("summary.aspx?strTab="+ strTab); //将表名作为参数传递给汇总页面 }
//汇总页面启动 protected void Page_Load(object sender, EventArgs e) { if (Session["userID"] == null) //若session过期,则返回登录页面 Response.Redirect("logon.aspx"); if (Request.QueryString["strTab"]=="dinner_choice_bak") //获得网页传递的表名参数 btOver.Enabled =false; //如果是查询历史记录,“完结”功能被屏蔽 Response.Write("<font size=4 color='red'><b>您好!<B><font size=5 color='Green'>" + Session["userNam"] + "</font><font size=4 color='red'> 所属部门:" + Session["deptNam"] + "。</font>"); if (!this.IsPostBack) //若页面首次开启,为“完结”按钮加入确认提示 { BindQuery(); this.btOver.Attributes.Add("onclick", "javascript: return confirm('确定要清空所有记录,并将“已确认”保存为历史记录吗?此操作不能恢复,请慎重!!!');"); } } |
解释:通过网页之间传递参数,向汇总页面发送表名参数,决定查询历史记录,还是汇总当前记录。 |
注: 网页之间传递参数主要有三种: 1) 利用Session保存变量,可以交叉传递到多个页面,直到Session被removed,才会消失。 session["name"]=str1; session["dept"]=str2; 2) 通过QueryString传递变量,只能使用一次。 Response.Redirect(webfrm2.aspx?name=" + str1 + "&dept=" + str2 + "; //用&传递两个参数 webfrm2的page_load代码: private void Page_Load (object sender, System.EventArgs e) { string strName=Request.QueryString["name"]; string strDept=Request.QueryString["dept"]; } 3) 使用Server.Transfer,较难。 webfrm1的过程,返回变量值。 public string name { get { return str1; } } public string dept { get { return str2; } } Server.Transfer("webfrm2.aspx");
webfrm2的page_load代码: private void Page_Load (object sender,System.EventArgs e) { webfrm1 frm1; //创建窗体实例 frm1=(webfrm1)Context.Handler; //获得句柄 string strName=frm1.name; string strDept=frm1.dept; } |
2.通过“完结”操作,将所有“已确认”记录移入到历史表——dinner_choice_bak表保存备查,并清空当前表——dinner_choice。 |
C#语句: //页面首次打开,为“完结”按钮加入确认提示 if (!this.IsPostBack) { this.btOver.Attributes.Add("onclick", "javascript: return confirm('确定要清空所有记录,并将“已确认”保存为历史记录吗?此操作不能恢复,请慎重!!!');"); } protected void DataBak() //数据备份过程 { OleDbConnection Conn = new OleDbConnection(connStr); //连接数据库 Conn.Open(); string strSql = "insert into dinner_choice_bak select * from dinner_choice where dinner_status='01'"; OleDbCommand cmd = new OleDbCommand(strSql, Conn); //转移所有“已确认”记录 cmd.ExecuteNonQuery(); strSql = "delete from dinner_choice"; OleDbCommand cmd1 = new OleDbCommand(strSql, Conn); //清空当前表 cmd1.ExecuteNonQuery(); Conn.Close(); } |
解释:通过当前表和历史表的数据结构设计,使得查询、操作当前订餐信息时更加快速,从而在数据量方面保障了订餐页面的记录集自动刷新。 |
五. 程序流程图:
|
综上所述,ASP.NET2.0+VS2005编程技术为我们提供了强大的数据处理能力和广阔的探索空间,还有很多的WEB控件和网页访问技术需要深入学习和开发。
此篇论文是本人根据实际开发的《网络订餐系统》所撰写,希望能够为广大读者带来帮助,转载请注明出处。谢谢支持。