0


C#学习记录——图书馆管理系统

参考《零基础学C#3.0》学习实现。
本篇练习记录一个开发图书馆管理系统示例。进行中ing

1、实现功能简介

图书管理系统利用VS2019集成开发环境进行程序的编程和调试,数据库采用SQL Server 2008。图书管理系统框图如图:
图书馆管理系统框图
普通用户模块具有以下功能:
1>图书查询:根据输入条件进行查询,可以选择多项,也可以查询所有图书。
2>图书借阅:提供图书借阅证号,可以进行图书借阅。
管理员登录成功后,具有以下功能:
1>添加图书:添加新图书。
2>删除图书:删除过时的、不能借阅的图书。
3>修改图书:对图书信息进行修改。
4>查询图书:查询图书信息。

2、数据库设计

数据库设计是系统建设中非常重要的环节,合理有效的数据库设计对系统的性能至关重要,也是创建系统的基础。

2.1、数据库结构设计

【该样例的数据库结构在上传的资源中有备份,感兴趣的可以下载学习】

根据需求分析进行数据库设计,数据库名称为BookManage,共需要三张表:用户表(userinfo)、图书信息表(bookInfo)、图书借阅信息表(IssueInfo)。用户表的结构如下:
(1)用户表(userinfo)结构
用户结构表
用户表存放用户账号的信息,一个用户可以对应一个账号,包括用户名、密码、借阅证号和用户权限。通过用户表查询用户权限和借阅证号,判断是否是管理员,是否拥有修改的权限。
(2)图书信息表(bookInfo)的结构
图书信息表
(3)图书借阅信息表(IssueInfo)的结构
图书借阅信息表

2.2、创建数据库和表

创建数据库和表的步骤如下:
(1)打开SQL Server 2008数据库,连接到服务器的界面,其中的服务器名称是当前计算机的名称,可修改。此处使用默认即可。
连接到服务器
(2)单击“连接”按钮,连接成功会出现数据库管理界面。左边是“对象资源管理器”窗口,右边显示详细信息窗口。
数据库管理界面
(3)在“对象资源管理器”中右击“数据库”节点,打开上下文菜单,在菜单中单击“新建数据库”菜单,打开“新建数据库”界面。在“数据库名称”文本框中输入数据库名称“BookManage”,其他可采用默认值。单击“确定”按钮,即完成数据库的创建。
新建数据库界面
(4)在“对象资源管理器”中单击数据库“BookManage”前面的“+”号,展开数据库节点,右击“表”节点,在弹出的菜单中单击“新建表”选项,打开“创建表”窗口:
创建表窗口
(5)根据前面设计的数据库表结构,添加表中的字段,在下面的属性窗口中确定每一列的属性值。设定“BookID”为主键。完成后单击保存按钮,会出现保存数据库文件的对话框,在其中输入表名称,单击“确定”按钮,完成数据表的创建。
保存表文件对话框
(6)右击刚才创建的表明,在显示的上下文菜单中单击“打开表”菜单,在该表中添加字段。添加完成后保存。

用相同的方法完成数据库中其他表的设计。
【该样例的数据库结构在上传的资源中有备份,感兴趣的可以下载学习】

3、界面设计

3.1、主界面

主界面是一个比较简单的界面,主要作用是根据用户的不同选择,在该窗体中打开不同的子窗体。主界面设计如下图所示:

在这里插入图片描述
普通用户只能查询图书信息,借阅图书;管理员拥有窗体中的所有功能。窗体的最上边是菜单栏,下面是工具栏,该窗体是所有子窗体的父窗体。
在项目中修改Form1.cs的名称为frmMain.cs,窗体的Text属性设置为“图书管理系统”,IsMdiContainer属性值设置为“true”,Size属性值设置为“800,500”。
在主窗体中添加MenuStrip控件和ToolStrip控件,用户管理菜单包括“管理员登录”和“退出”子菜单。图书管理菜单包括“图书入库”、“图书更新”、“图书检索”子菜单。

3.2、用户登录界面

本系统的用户分为普通用户和管理员用户,管理员用户具有系统提供的所有权限,普通用户可以查询图书、借阅图书。系统登录界面是判断用户身份的一个交互窗体,在其中输入正确的用户名和密码后,单击“登录”按钮,可根据用户角色在主界面中拥有相应的权限。用户登录界面如下图所示:

用户登录界面

用户类型分为管理员和普通用户,在组合框中添加这两项。其他按照界面设计即可。

3.3、图书查询界面

系统中的图书查询提供组合查询的方式,界面设计如下图,此窗体名称为“frmSearchBook.cs”,Text属性设置为“图书管理”。图书类别组合框中的项是从数据库中获取,在设计时不需要添加内容。在条件组合框中输入“or”和“and”,来动态实现条件组合。
图书管理

在右边的分组框中添加DataGridView控件,单击右上角的黑色三角,选择“在父容器中停靠”选项,单击“编辑列”选项,打开“编辑列”选项卡,设置下图所示内容,将列BookID设置为不可见。
在这里插入图片描述

注意:
一定要将每列的"DataPropertyName"属性设置为相对应的数据表中的列名,否则数据显示不出来。
添加控件及属性设置
控件类型属性值说明Size777,405设置窗口尺寸Text图书管理设置名称GroupBox1Text搜索条件设置名称GroupBox2Text搜索结果设置名称Lable1Text图书类别:标签名称Lable2Text图书名称:标签名称Lable3Text主要内容:标签名称ComboBox1(name)cboType控件名称ComboBox2(name)cboOR控件名称ComboBox3(name)cboAnd控件名称TextBox1(name)txtName控件名称TextBox2(name)txtContent控件名称Button1(name)btnSerch设置名称Button1Text搜索设置名称Button2(name)btnClose设置名称Button2Text关闭设置名称DataGridView(name)dgvSearchBook设置name

3.4、图书入库界面

管理员有权限可以将图书添加到数据库中,图书入库界面如下图。在左边的分组框中添加标签和文本框,用来接收管理员输入的图书信息,以便保存到数据库中。在右边的分组框中添加DataGridView控件,可以显示数据库中的图书信息,新添加的图书信息也可以显示出来。

图书入库界面
1)、添加frmAddBook窗体;
2)、添加控件及属性设置
控件类型属性值说明Size777,405设置窗口尺寸Text图书入库设置名称GroupBox1Text插入详细信息设置TextGroupBox2Text图书详细信息设置TextTextBox1(name)txtType标签名称Lable1Text类别:标签nameTextBox2(name)txtName标签名称Lable2Text书名:标签名称TextBox3(name)txtAuthor标签名称Lable3Text作者:标签名称TextBox4(name)txtprice标签名称Lable4Text价格:标签名称TextBox5(name)txtPic标签名称Lable5Text封面:标签名称TextBox6(name)txtContent标签名称Lable6Text内容简介:标签名称TextBox7(name)txtlssue标签名称Lable7Text指定访问码:标签名称TextBox1(name)txtName控件名称TextBox2(name)txtContent控件名称Button1(name)btnInsertBkDt设置名称Button1Text插入(I)设置名称Button2(name)btnExit设置名称Button2Text退出(E)设置名称

3.5、图书更新界面

图书更新界面用于方便管理员管理图书信息,实现图书信息修改、删除等操作。界面设计如下图:
图书更新界面

1)、添加frmUpdateBook窗体;
2)、添加控件及属性设置
控件类型属性值说明Size777,405设置窗口尺寸Text图书更新设置名称GroupBox1Text图书详细信息设置TextGroupBox2Text更新图书信息设置TextLable1Text图书编号:标签名称Lable2Text图书类型:标签名称Lable3Text图书名字:标签名称Lable4Text图书作者:标签名称Lable5Text图书价格:标签名称Lable6Text图书封面:标签名称Lable7Text图书内容:标签名称Lable8Text访问码:标签名称TextBox1(name)txtbID图书编号:输入框TextBox2(name)txtbType图书类型:输入框TextBox3(name)txtbName图书名字:输入框TextBox4(name)txtAuthor图书作者:输入框TextBox5(name)txtbPrice图书价格:输入框TextBox6(name)txtbPic图书封面:输入框TextBox7(name)txtbContent图书内容:输入框TextBox8(name)txtIssueID访问码:输入框Button1(name)btnUpdatePic更新封面按钮名Button1Text更新封面设置名称Button2(name)btnUpdate设置名称Button2Text更新设置名称Button3(name)btnDel设置名称Button3Text删除设置名称Button4(name)btnInsertBkDt设置名称Button4Text关闭设置名称Button5(name)btnSave设置名称Button5Text保存更改设置名称DataGridView1(name)dgvBookInfo数据库显示

3.6、图书借阅界面

根据图书馆已有的图书信息,用户可以通过图书证号实现图书借阅。界面设计如下图所示:
图书借阅界面
左边书名组合框中的项来自数据库中的书名,只有数据库中存有的书,才可以借阅,选择该组合框中的书名,该书的图书访问码、作者会显示在文本框中。输入借阅证号和姓名,可实现图书借阅,借阅信息显示在右边的DataGridView控件中。
1)、添加frmlssueBook窗体;
2)、添加控件及属性设置
控件类型属性值说明Size777,405设置窗口尺寸Text图书借阅设置名称GroupBox1Text借阅详细信息:设置TextGroupBox2Text借阅详细信息:设置TextLable1Text借阅证号:标签名称Lable2Text姓名:标签名称Lable3Text图书访问码:标签名称Lable4Text书名:标签名称Lable5Text作者:标签名称Lable6Text借阅日期:标签名称TextBox1(name)txtIssID借阅证号:输入框名称TextBox2(name)姓名:输入框名称TextBox3(name)txtBookAccessCode图书访问码:输入框名称TextBox4(name)txtAuthor作者:输入框名称ComboBox1(name)cboBookName书名:下拉框ComboBox2(name)dateTimePicker1借阅日期:下拉框DataGridView1(name)dgvIssInfo数据库显示Button1(name)btnIssueBook设置名称Button1Text借阅设置名称Button2(name)btnInsertBkDt设置名称Button2Text退出设置名称

3.7、图书封面查看窗体

在这里插入图片描述

1)、添加frmBookPic窗体;
2)、添加控件及属性设置
控件类型属性值说明Size440,400设置窗口尺寸Text图书封面查看设置名称Lable1Text图书封面路径标签名称TextBox1(name)txtPic封面路径:输入框名称PictureBox(name)pbBook封面路径:图片显示Button1(name)btnPic设置名称Button1Text修改封面设置名称Button2(name)btnUpdatePic设置名称Button2Text更新封面设置名称Button3(name)btnCancle设置名称Button3Text取消设置名称OpenFileDialog(name)opfFile设置名称

4、通用类的生成

本实例系统的主要操作都需要与数据库发生交互,操作数据库是必不可少的,为了减少重复代码,提高代码的重用性和规范性,把与数据库交互的功能单独放在一个类中,在该类中实现数据库的增加、删除、修改、查询等同用功能。

4.1、连接数据库

数据库的连接是数据库操作首先要实现的。本实例采用的数据库是Microsoft SQL Server 2008版本,所以要引入命名空间SqlClient。
(1)定义数据库连接字符串,代码如下:

string ConnectString ="server=(local)\\sqlexpress;database=Book; user=sa; pwd=1234";

(2)创建Connection对象,代码如下:

SqlConnection con=newSqlConnection(ConnectString);

(3)打开连接,代码如下:

con.Open();

(4)关闭连接,代码如下:

con.Close();

4.2、操作数据库中的数据

在通用类中实现了数据库的增加、删除、修改、查询功能。示例代码如下:

usingSystem;usingSystem.Collections.Generic;usingSystem.Data;usingSystem.Data.SqlClient;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespaceL019_BookMange{classDataAccess{privatestaticstring ConnectString ="server=(local)\\sqlexpress;database=Book; user=sa; pwd=1234";//数据库连接字符串///<summary>///根据表名获取数据集的表///</summary>///<param name="table">表名</param>///<returns>DataTable</returns>>publicstaticDataTableGetDataSetByTableName(string table){using(SqlConnection con=newSqlConnection(ConnectString))//创建数据库连接对象{string sql="select*from"+ table +"";//查询SQL语句try{SqlDataAdapter adapter =newSqlDataAdapter(sql, con);//创建适配器对象DataSet ds =newDataSet();//创建数据集对象
                    adapter.Fill(ds,"table");//填充数据集return ds.Tables[0];//返回数据表}catch(SqlException ex){//异常处理thrownewException(ex.Message);;}}}///<summary>///根据Sql语句获取数据集对象///</summary>///<param name="sql">sql</param>///<returns>DataSet</returns>>publicstaticDataSetGetDataSetBySql(string sql){using(SqlConnection con=newSqlConnection(ConnectString))//创建数据库连接对象{SqlDataAdapter adapter =newSqlDataAdapter(sql, con);//创建适配器对象DataSet ds =newDataSet();//创建数据集对象try{
                    adapter.Fill(ds);//填充数据集return ds;//返回数据集}catch(SqlException ex){thrownewException(ex.Message);}}}///<summary>///根据id值获取DataReader对象///</summary>///<param name="id">id</param>///<returns>SqlDataReader</returns>>publicstaticSqlDataReaderGetDataReaderByID(int id){using(SqlConnection con=newSqlConnection(ConnectString)){string sql ="select*from bookinfo where bookid="+ id;//sql语句try{SqlCommand comm =newSqlCommand(sql, con);//创建Command对象
                    con.Open();//打开连接SqlDataReader reader = comm.ExecuteReader();//创建DataReader对象
                    reader.Read();//读取数据return reader;//返回DataReader}catch(SqlException ex){thrownewException(ex.Message);}}}///<summary>///更新数据///</summary>///<param name="sql"></param>///<returns></returns>>publicstaticboolUpdateDataTable(string sql){using(SqlConnection con=newSqlConnection(ConnectString)){try{
                    con.Open();//打开连接SqlCommand comm =newSqlCommand(sql, con);//创建Command对象if(comm.ExecuteNonQuery()>0)//执行更新{returntrue;}else{returnfalse;}}catch(SqlException ex){thrownewException(ex.Message);}}}///<summary>///根据数据集和sql语句更新数据库///</summary>publicstaticvoidUpdateDataSet(DataSet ds,string sql){using(SqlConnection con=newSqlConnection(ConnectString)){try{SqlDataAdapter adapter =newSqlDataAdapter(sql, con);//创建适配器SqlCommandBuilder builder =newSqlCommandBuilder(adapter);//根据适配器自动生成表单
                    adapter.Update(ds,"table");//更新数据库}catch(SqlException ex){thrownewException(ex.Message);}}}}}

此类主要实现了数据库操作的一些通用方法,把对数据库操作的代码放在using语句块中,这里using的作用是作为一个临时对象的生存区域,因为.NET是托管代码机制,在程序中有时要求非托管资源,如文件句柄或SQL连接。将使用此类资源的代码放在using语句块中,可以确保这些资源的释放。using块可以分为获取、使用和释放3部分。
(1)获取表示创建变量并将其初始化,以便引用系统资源。
(2)使用资源并执行操作。using{}之间的语句代表资源的使用过程。
(3)调用Dispose方法释放资源。
本例把创建数据连接对象放在using()中,using{}中的代码执行完毕即自动释放数据库连接对象,可以节约内存使用。
注意:
使用using关键字创建数据库连接对象时,可自动释放该资源,不需要调用Close()方法关闭连接。
上边代码中的变量和方法均定义为static型,使用时不需要实例化该类,直接用类名访问即可。

5、代码实现和分析

在通用类中已经实现了与数据库的交互,界面中的功能实现就不需要直接访数据库,只需要调用通用类中的相关方法即可。

5.1、主窗口代码实现和分析

【本节示例参考:】
主窗口主要实现其他界面的管理和导航,如果是普通用户,图书入库和图书更新的功能不可以。管理员登录后拥有所有权限。单击每一个菜单或者按钮,都可以打开相应的子窗口。
为了避免打开已经存在的窗口,下面设计了一个检测窗口是否存在的方法,如果存在返回true,不存在返回false。该方法示例代码如下:

//查询子窗体是否存在publicboolcheckchildfrm(string childfrmname){foreach(Form childFrm inthis.MdiChildren)//遍历子窗体{if(childFrm.Name == childfrmname)//判断子窗体的名称{//如果子窗体处于最小化的状态,即恢复正常显示if(childFrm.WindowState == FormWindowState.Minimized)
                        childFrm.WindowState = FormWindowState.Normal;
                    childFrm.Activate();//激活窗体returntrue;//返回真值}}returnfalse;//返回假值}

主窗体载入时,首先设定图书入库和图书更新的菜单和按钮不可用,示例代码如下:

privatevoidFrmMain_Load(object sender,EventArgs e){this.tsbtnAddBook.Enabled =false;this.图书入库ToolStripMenuItem.Enabled =false;this.图书修改ToolStripMenuItem.Enabled =false;}

单击每一个按钮或者菜单,打开相应的窗体。下面为主窗体的完整代码示例。

usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;namespaceL019_BookMange{publicpartialclassFrmMain:Form//主窗体,继承自Form类{publicstaticDialogResult result;//声明对话框返回对象publicFrmMain()//构造函数{InitializeComponent();}//查询子窗体是否存在publicboolcheckchildfrm(string childfrmname){foreach(Form childFrm inthis.MdiChildren)//遍历子窗体{if(childFrm.Name == childfrmname)//判断子窗体的名称{//如果子窗体处于最小化的状态,即恢复正常显示if(childFrm.WindowState == FormWindowState.Minimized)
                        childFrm.WindowState = FormWindowState.Normal;
                    childFrm.Activate();//激活窗体returntrue;//返回真值}}returnfalse;//返回假值}//用户登录菜单的事件处理privatevoid 用户登录ToolStripMenuItem_Click(object sender,EventArgs e){//检测该窗口是否处于打开状态if(this.checkchildfrm("frmLogin")==true)return;//窗口已经打开,返回frmLogin login =newfrmLogin();//实例化登录窗体
            login.ShowDialog();//登录窗体以模式对话框的方式打开           //判断是否登录成功,登录成功则启用相应的菜单和按钮if(result == DialogResult.OK){this.tsbtnAddBook.Enabled =true;this.图书入库ToolStripMenuItem.Enabled =true;this.图书修改ToolStripMenuItem.Enabled =true;}}//登录按钮事件处理privatevoidtsbtnLogin_Click(object sender,EventArgs e){if(this.checkchildfrm("frmLogin")==true)return;frmLogin login =newfrmLogin();//login.MdiParent = this;//login.Show();
            login.ShowDialog();if(result == DialogResult.OK){this.tsbtnAddBook.Enabled =true;this.图书入库ToolStripMenuItem.Enabled =true;this.图书修改ToolStripMenuItem.Enabled =true;}}//图书查询按钮Click事件处理privatevoidtsbtnSearch_Click(object sender,EventArgs e){if(this.checkchildfrm("frmSearchBook")==true)return;frmSearchBook book =newfrmSearchBook();
            book.MdiParent =this;//设置为当前窗体的子窗体
            book.Show();}//图书入库菜单Click事件处理privatevoid 图书入库ToolStripMenuItem_Click(object sender,EventArgs e){if(this.checkchildfrm("frmAddBook")==true)return;frmAddBook objbook =newfrmAddBook();
            objbook.MdiParent =this;
            objbook.Show();}//图书入库按钮Click事件处理privatevoidtsbtnAddBook_Click(object sender,EventArgs e){if(this.checkchildfrm("frmAddBook")==true)return;frmAddBook objbook =newfrmAddBook();
            objbook.MdiParent =this;
            objbook.Show();}//图书更新菜单Click事件处理privatevoid 图书修改ToolStripMenuItem_Click(object sender,EventArgs e){if(this.checkchildfrm("frmUpdateBook")==true)return;frmUpdateBook objbook =newfrmUpdateBook();
            objbook.MdiParent =this;
            objbook.Show();}//图书借阅按钮事件处理privatevoidtsbtnlssue_Click(object sender,EventArgs e){//检查该窗体是否处于打开状态if(this.checkchildfrm("frmlssueBook")==true)return;//返回frmlssueBook issueBook =newfrmlssueBook();//实例化窗体
            issueBook.MdiParent =this;//设置为当前窗体的子窗体
            issueBook.Show();//打开窗体}//退出按钮事件处理privatevoidtsbtnExit_Click(object sender,EventArgs e){
            Application.Exit();//退出应用程序}//窗体载入时事件处理privatevoidFrmMain_Load(object sender,EventArgs e){this.tsbtnAddBook.Enabled =false;this.图书入库ToolStripMenuItem.Enabled =false;this.图书修改ToolStripMenuItem.Enabled =false;}}}

打开窗体的代码基本上使用以下步骤:
(1)调用checkchildfrm()方法检测窗体是否打开,如已经打开,则激活该窗口。
(2)检测没有打开,实例化窗体类。
(3)设置窗体对象为当前窗体的子窗体。
(4)调用Show()方法打开子窗体。
代码第15行,定义了静态的DialogResult对象result,用来记录登录窗体状态。代码第45行使用该值来判断管理员登录是否成功,值为DialogResult.OK。

5.2、用户登录代码实现和分析

用户登录代码比较简单,主要是通过sql语句调用DataAccess类中的GetDataSetBySql()方法来实现的。示例代码如下:

usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;namespaceL019_BookMange{publicpartialclassfrmLogin:Form{publicfrmLogin(){InitializeComponent();}privatevoidbtnLogin_Click(object sender,EventArgs e){//验证通过           if(Validate()){string state =this.cboUserType.Text;int num;if(state.Equals("管理员"))//判断用户角色
                    num =2;else
                    num =1;//定义查询语句string sql =string.Format("select * from userinfo where uname='{0}'and upwd='{1}'and ustate={2}",this.txtName.Text.Trim(),this.txtPwd.Text.Trim(), num);DataSet ds = DataAccess.GetDataSetBySql(sql);if(ds.Tables[0].Rows.Count >0){
                    MessageBox.Show("登录成功");
                    FrmMain.result = DialogResult.OK;//为变量result赋值this.Close();}else
                    MessageBox.Show("用户名或密码错误,请重新输入");}}privatevoidfrmLogin_Load(object sender,EventArgs e){this.cboUserType.SelectedIndex =0;}privateboolValidate(){if(this.txtName.Text !=string.Empty &&this.txtPwd.Text !=string.Empty)returntrue;else
                MessageBox.Show("用户名或密码不能为空");returnfalse;}privatevoidbtnCancle_Click(object sender,EventArgs e){this.Close();}}}

代码第46~53行定义了数据验证的方法,检测用户名和密码是否为空。代码第20行调用验证方法,如果验证通过,则继续执行后面的代码,否则给出提示信息。
代码第29行定义了查询数据库的SQL语句,代码第30行调用通用类DataAccess中的GetDataSetBySql(sql)方法操作数据库。代码第31行对结果进行判断,如果用户名和密码与存储在数据库中的信息一致,则登录成功,为result变量赋值,否则提示用户名或密码错误,请重新输入。

5.3、图书查询代码实现和分析

图书查询可以实现组合查询,将查询结果显示在DataGridView控件中。示例代码如下所示:

usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;namespaceL019_BookMange{publicpartialclassfrmSearchBook:Form{publicfrmSearchBook(){InitializeComponent();}privatevoidbtnSerch_Click(object sender,EventArgs e){string cbo1 =this.cboOR.Text;string cbo2 =this.cboAnd.Text;string booktype =this.cboAnd.Text;string bookname =this.txtName.Text;string bookcontent =this.txtContent.Text;//定义sql语句string sql ="select*from bookInfo where BookType='"+ booktype +"'"+ cbo1 +"BookName like'%"+ bookname +"%'"+ cbo2 +"BookContent like'%"+ bookcontent +"%'";//调用DataAccess.GetDataSetBySql方法DataSet Myds = DataAccess.GetDataSetBySql(sql);DataTable table = Myds.Tables[0];//指定数据源this.dgvSearchBook.DataSource = table;}privatevoidfrmSearchBook_Load(object sender,EventArgs e){//图书类别组合框初始化DataSet Myds = DataAccess.GetDataSetBySql("select distinct BookType from bookInfo");DataTable table = Myds.Tables[0];for(int i=0;i<table.Rows.Count;i++){this.cboType.Items.Add(table.Rows[i][0].ToString().Trim());}this.cboType.SelectedIndex =0;this.cboOR.SelectedIndex =0;this.cboAnd.SelectedIndex =0;}privatevoidbtnClose_Click(object sender,EventArgs e){this.Close();}}}

这个功能关键的是查询语句的编写。根据查询条件,动态构建查询的sql语句,注意查询语句中“like”和“%”的用法。
窗体载入时,对图书类别组合框中的项进行了初始化,通过查询语句“select distinct BookType from bookInfo”,调用DataAccess类中的GetDataSetBySql方法获取数据库BookInfo表中的图书类别,使用distinct关键字去掉重复项。代码第37行,使用for循环将查询获取的结果添加到组合框中。

5.4、图书入库代码实现和分析

管理员登录后可以为数据库添加图书信息,由于通用类中已经完成了与数据库交互的功能,在此最关键的是插入数据的SQL语句编写。在执行插入功能前,通常都会进行数据验证,数据验证通过,才可以执行数据库的操作。图书入库完整代码如下:

usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;namespaceL019_BookMange{publicpartialclassfrmAddBook:Form{publicfrmAddBook(){InitializeComponent();}///<summary>///插入按钮单击事件处理///</summary>privatevoidbtnInsertBkDt_Click(object sender,EventArgs e){//定义变量接收控件的值string booktype =this.txtType.Text.ToString();string bookname =this.txtName.Text.ToString();string bookauthor =this.txtAuthor.Text.ToString();Double bookprice = Convert.ToDouble(this.txtprice.Text);string bookpic =this.txtPic.Text.ToString();string bookcontent =this.txtContent.Text.ToString();int bookissue = Convert.ToInt32(this.txtIssue.Text);//如果数据验证通过则调用DataAccess类的方法实现添加功能if(Validate()){//sql语句string sql =string.Format("insert into bookinfo values('{0}','{1}','{2}',{3},'{4}','{5}',{6})", booktype, bookname, bookauthor, bookprice, bookpic, bookcontent, bookissue);if(DataAccess.UpdateDataTable(sql))//调用更新方法{
                    MessageBox.Show("添加成功","提示", MessageBoxButtons.OK);}else{
                    MessageBox.Show("添加失败","提示", MessageBoxButtons.OK);}//DataGidView控件显示数据DataSet ds = DataAccess.GetDataSetBySql("select*from BookInfo");this.dataGridView1.DataSource = ds.Tables[0];}}///<summary>///退出按钮单击事件处理///</summary>privatevoidbtnExit_Click(object sender,EventArgs e){this.Close();//关闭窗体}//数据验证privateboolValidate(){if(this.txtType.Text !=string.Empty &&this.txtName.Text !=string.Empty &&this.txtAuthor.Text !=string.Empty &&this.txtContent.Text !=string.Empty &&this.txtIssue.Text !=string.Empty &&this.txtprice.Text !=string.Empty)returntrue;else
                MessageBox.Show("请输入完整的信息");returnfalse;}}}

由于数据库中BookID字段为自动增长列,在插入数据时不能插入此值,否则会出现异常。
说明:
本段代码中的数据验证仅仅验证了输入是否为空,没有对输入字符的有效性进行验证。

【运行错误】

错误1、练习过程中,出现:“未经处理的异常”
System.Exception:“不允许从数据类型varchar到varbinary的隐式转换。请使用CONVERT函数来运行此查询”
在这里插入图片描述
原因:经百度,原因是建表是把varchar 类型建成 varbinary类型了,将数据库bookinfo表中,varbinary类型的 改成varchar类型,就可以了。

错误2、“DataGridView默认错误”对话框
在这里插入图片描述
原因:将DataGridView绑定的数据源集合清空的同时,将Datasource置空(赋值为null)

5.5、图书更新代码实现和分析

图书更新分为两种方式,第一种通过更改DataGridView控件中的数据直接更改,第二种可以通过修改各字段值进行更改。单击“保存修改”按钮,将DataGridView控件中修改的数据保存到数据库中,代码如下所示:

privatevoidbtnSave_Click(object sender,EventArgs e){string sql ="select*from BookInfo";DialogResult result = MessageBox.Show("确实要将修改保存到数据库吗?","操作提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);if(result==DialogResult.OK){DataSet ds =null;
                DataAccess.UpdateDataSet(ds, sql);
                    MessageBox.Show("保存成功");}this.dgvBookInfo.DataSource = DataAccess.GetDataSetBySql(sql).Tables[0];}

在保存修改之前给出提示信息,确认修改,则将数据保存到数据库中,调用了通用类DataAccess中的UpdateDataSet(ds,sql)方法实现将数据集中的数据保存到数据库中的功能。这里的sql语句只是用于创建DataAdapter对象,并不是真正执行的语句,要注意区分。修改完成之后,重新给DataGridView控件绑定数据源,其作用是刷新数据显示。

单击DataGridView控件中任意单元格,可以将整条记录信息显示在右边的相应控件中。将代码编写在DataGridView控件的CellClick事件中,可以实现此功能。示例代码如下所示:

privatevoiddgvBookInfo_CellClick(object sender,DataGridViewCellEventArgs e){//获得当前鼠标单击时的行索引号int index =this.dgvBookInfo.CurrentCell.RowIndex;//通过索引号获得值并赋予相应的文本框显示this.txtbID.Text =this.dgvBookInfo.Rows[index].Cells[0].Value.ToString().Trim();this.txtbType.Text =this.dgvBookInfo.Rows[index].Cells[1].Value.ToString().Trim();this.txtbName.Text =this.dgvBookInfo.Rows[index].Cells[2].Value.ToString().Trim();this.txtAuthor.Text =this.dgvBookInfo.Rows[index].Cells[3].Value.ToString().Trim();this.txtbPrice.Text =this.dgvBookInfo.Rows[index].Cells[4].Value.ToString().Trim();this.txtbPic.Text =this.dgvBookInfo.Rows[index].Cells[5].Value.ToString().Trim();this.txtbContent.Text =this.dgvBookInfo.Rows[index].Cells[6].Value.ToString().Trim();this.txtIssueID.Text =this.dgvBookInfo.Rows[index].Cells[7].Value.ToString().Trim();}

注意:
DataGridView控件可以通过行和列的索引获取单元格的值,索引都是从0开始。
单击“更新”按钮,可以将修改后的数据保存到数据库中。示例代码如下所示:

privatevoidbtnUpdate_Click(object sender,EventArgs e){string booktype =this.txtbType.Text.ToString();string bookname =this.txtbName.Text.ToString();string bookauthor =this.txtAuthor.Text.ToString();Double bookprice = Convert.ToDouble(this.txtbPrice.Text);string bookpic =this.txtbPic.Text.ToString();string bookcontent =this.txtbContent.Text.ToString();int bookissue = Convert.ToInt32(this.txtIssueID.Text);string sql =string.Format("update bookInfo set BookType='{0}',BookName='{1}',BookAuthor='{2}',BookPrice={3},BookPic='{4}',BookContent='{5}',BookIssue={6}where BookID={7}", booktype, bookname, bookauthor, bookprice, bookpic, bookcontent, bookissue, Convert.ToInt32(this.txtbID.Text));if(DataAccess.UpdateDataTable(sql)){
                MessageBox.Show("更新成功","提示", MessageBoxButtons.OK);}else{
                MessageBox.Show("更新失败","提示", MessageBoxButtons.OK);}}

观察此段代码,发现难点还是SQL语句的编写,只要熟练掌握SQL语句,这个功能实现就很容易了。
注意:
sql语句中字符串和整型数据的不同写法。

单击“删除”按钮,可以删除图书信息,如果此书存在结束记录,则不能删除,示例代码如下:

privatevoidbtnDel_Click(object sender,EventArgs e){DataSet ds = DataAccess.GetDataSetBySql("select*from IssueInfo where BookID="+ Convert.ToInt32(this.txtbID.Text)+"");//查询是否存在借阅信息if(ds.Tables[0].Rows.Count>0){
                MessageBox.Show("此书有借阅,不能删除");return;}else{string sql ="delete from bookInfo where BookID="+this.txtbID.Text +"";if(DataAccess.UpdateDataTable(sql)){
                    MessageBox.Show("删除成功","提示", MessageBoxButtons.OK);}else{
                    MessageBox.Show("删除失败","提示", MessageBoxButtons.OK);}}this.txtAuthor.Text ="";this.txtbContent.Text ="";this.txtbID.Text ="";this.txtbName.Text ="";this.txtbPic.Text ="";this.txtbPrice.Text ="";this.txtbType.Text ="";}

代码第3行首先查询准备删除的图书是否存在借阅信息,如果有借阅记录,则不能删除该书,给出提示并终止当前操作。

删除成功后重置当前控件,将控件内容全部清空。代码第21~27行实现此功能。

单击“更新封面”按钮,可以打开更新封面窗口,为当前记录修改封面图片的路径。在打开更新封面窗口时,如果当前记录中存在图书封面的记录,则显示相应图片信息,否则可以不显示,重新添加。示例代码如下所示;

privatevoidbtnUpdatePic_Click(object sender,EventArgs e){string pic =this.txtbPic.Text.ToString();int bookid = Convert.ToInt32(this.txtbID.Text);//记录该条数据的主键值frmBookPic bookpic =newfrmBookPic();//实例化封面窗体
            bookpic.ShowContent(bookid, pic);//调用封面窗体中的方法
            bookpic.ShowDialog();//打开窗体}

代码第6行调用了“图书封面查看”窗体中的ShowContent方法来传递数据,该方法用于将此窗体中的图书封面路径信息显示在“图书封面查看”窗体的文本框中,并将图片显示在图片框中。ShowContent方法将在“图书封面查看”窗体中详细解释。

图书封面查看窗体如下图所示,其中“修改封面”按钮下面的方框是图片框控件,用于显示封面图片。
图书封面查看界面

为了能够实现单击“修改封面”按钮时,可以在本地磁盘中查找相应的图片,在此窗体中添加了OpenFileDialog控件,命名为“opfFile”。此窗体完整代码示例如下图:

usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;namespaceL019_BookMange{publicpartialclassfrmBookPic:Form{privateint id;//声明整型变量publicfrmBookPic(){InitializeComponent();}//将frmBookInfo中选定的图书信息显示出来publicvoidShowContent(int bookid,string pic){
            id = bookid;this.txtPic.Text = pic;if(pic!=string.Empty){this.pbBook.Image = Image.FromFile(this.txtPic.Text);}}privatevoidbtnPic_Click(object sender,EventArgs e){if(opfFile.ShowDialog()==DialogResult.OK){this.txtPic.Text = opfFile.FileName;//图片路径显示在文本框中this.pbBook.Image = Image.FromFile(txtPic.Text);//显示图片}}privatevoidbtnUpdatePic_Click(object sender,EventArgs e){if(DataAccess.UpdateDataTable("update bookInfo set BookPic='"+txtPic.Text+"'where BookID="+id+"")){
                MessageBox.Show("更新成功","提示", MessageBoxButtons.OK);}else{
                MessageBox.Show("添加失败","提示", MessageBoxButtons.OK);}}privatevoidbtnCancle_Click(object sender,EventArgs e){this.Close();}}}

代码第19~27行ShowContent方法将传入的两个参数赋值,并将图片显示在图片框中。代码第30行,通过调用OpenFileDialog控件选择图片,如果选择完成,则将图片的路径显示在文本框中,选择的图片显示在图片框中。
通过给“更新封面”的Click事件编写处理代码,实现将选择的图片信息保存到数据库中的功能。

5.6 、图书借阅代码实现和分析

图书借阅窗体中图书借阅功能实现比较简单,和前面的图书入库、图书更新基本相同,就不再赘述。下面分析窗体载入时在组合框中显示图书名称的代码实现。窗体载入事件处理代码如下:

privatevoidfrmlssueBook_Load(object sender,EventArgs e){DataSet ds = DataAccess.GetDataSetBySql("select BookInfo.BookID,BookName,IssBookID,IssDateTime from IssueInfo,BookInfo where BookInfo.BookID=IssueInfo.BookID");this.dgvIssInfo.DataSource = ds.Tables[0];//绑定数据源DataSet da = DataAccess.GetDataSetBySql("select*from bookinfo");//获取数据this.cboBookName.DataSource = da.Tables[0];//绑定数据源this.cboBookName.DisplayMember ="BookName";//组合框中显示的字段this.cboBookName.ValueMember ="BookID";//与显示字段对应的值}

第3、4行代码为DataGridView控件指定数据源。第5行代码,将查询BookInfo表获取的数据集赋给数据集对象“da”。第6行代码设定组合框的数据源为数据集“da”中的表,第7行代码使用组合框的“DisplayMember”属性指定组合框显示的内容。第8行代码使用组合框的“ValueMember”属性指定组合框的值,这些值的设定为后面代码处理时取值提供方便。

借阅不同的书籍,要选择不同的书。通过选择组合框中的书名,将该书的图书访问码和作者显示在相应的文本框中,此功能的实现需要为组合框的SelectedIndexChanged事件编写处理代码。示例代码如下所示:

privatevoidcboBookName_SelectedIndexChanged(object sender,EventArgs e){//遍历数据表中的数据,查找与组合框中相同的书名foreach(DataRow dataRow in da.Tables[0].Rows){if(string.Compare(cboBookName.Text, objRow["BookName"].ToString(),true)==0){this.txtBookAccessCode.Text = objRow["BookIssue"].ToString();this.txtAuthor.Text = objRow["BookAuthor"].ToString();}}}

第4行代码中的“da”是前面使用过的数据集,通过遍历数据表中的记录,查找与组合框中相同的“书名”,将该书的访问码和作者信息显示在文本框中。

单击“借阅”按钮,实现图书借阅的功能,并将借阅信息显示在DataGridView控件中。“借阅”按钮的Click事件处理代码如下所示:

privatevoidbtnIssueBook_Click(object sender,EventArgs e){int bookid = Convert.ToInt32(this.cboBookName.SelectedValue);//获取组合框中的值int issid = Convert.ToInt32(this.txtIssID.Text);//借阅证号DateTime date = Convert.ToDateTime(this.dateTimePicker1.Text);//借书时间string sql =string.Format("insert into IssueInfo value({0},{1},'{2}')", bookid, issid, date);if(DataAccess.UpdateDataTable(sql))//执行更新{
                MessageBox.Show("借阅成功");}DataSet data = DataAccess.GetDataSetBySql("select BookInfo.BookID,BookName,IssUbookID,IssDateTime from IssueInfo,BookInfo,BookInfo where BookInfo.BookID=IssueInfo.BookID");this.dgvIssInfo.DataSource = data.Tables[0];//绑定数据源}

将借阅信息添加到数据库中的操作比较简单,不再详细解释。在DataGridView控件中显示借阅信息时涉及多表查询,因为借阅表中只记录了借阅号和借阅图书的编号,没有记录借阅图书名称,需要通过图书编号获取图书名称,然后显示出来,此功能在编写SQL语句时需要使用多表查询才可以实现。

6、总结

本案例实现了一个简单的图书馆管理系统,这是一个完整的图书管理系统的简化版,代码结构比较简单,没有采用三层结构的方式组织项目,只是使用了通用类完成与数据库的交互,这种方法对于编写较小的项目比较实用。

有对C# 感兴趣的同学可以一起学习交流。

7、源码下载

可从上传的资源下载源码。

标签: c# 学习 数据库

本文转载自: https://blog.csdn.net/u013097500/article/details/127245820
版权归原作者 阿雷2023 所有, 如有侵权,请联系我们删除。

“C#学习记录——图书馆管理系统”的评论:

还没有评论