一、设计思路
要实现一个网格状的学生管理页面,必须要一个网格形状的控件用于展示,还需要导入学生的数据。要实现的功能是点击某一行,显示出三个按钮(查看,修改,删除)的功能。
数据库:Mysql
主窗口+3个子窗口
主窗口:listview控件,button控件
查看窗口 look:textbox控件
修改窗口 update:lable控件,textbox控件,button控件,radiobutton单选框控件,date日期控件
删除窗口 delate:messagebox控件(只需要在form1中弹出窗口即可,不需要建立新窗口)
二、具体实现
1、主窗口Form1
(1)、主窗口Form1的构造函数
1、首先将主窗口的名字命名
有一个Main函数,是整个项目的入口,往Application.Run(Form f)中,传入一个Form1类型的实例,用f(实参)去接收它。
这里有一个问题?为什么Application.Run(Form f)中需要的明明是Form类型的参数,你传的是Form1的实例对象,编译器为什么不报错呢?
答案是:Form1继承了Form类,所以子类中拥有了一份基类中完整的代码,即子类可以当做基类使用。( 这是面向对象中的 is a 法则)
2、手动拖拽控件之后,我们进入Form1的代码中,根据面向对象的三大原则(封装,继承,多态),我们先观察整个类的基本成员
(补:一个类最常用的三大成员有:属性,方法,事件)
(补:一个类最基本的成员有:属性—>字段,方法(构造函数、析构函数、普通方法、虚方法、抽象方法),事件 ——>委托类型的方法)
首先看Form1的构造函数,先进入InitializeComponent(),点击它,可以进入Form1的分部类中,在其中实例化这个类中的所以控件(一个类就是一个窗口),将字段或属性赋值,以及添加事件点击效果。截止为止,虽然构造函数还没有走完,但这个类的实例其实已经存在了,这个类中所有的字段已经赋完初值了,构造函数中剩下的的语句还是用来修改这个类中的字段或属性值,以及调用一些方法。
3、这个listview控件里面需要插入数据,所以我们需要连接MySql数据库
思考?当我们需要拿到数据库中的数据,用于展示时,应该干什么?
第一步:连接MySql数据库。我们需要找到连接的是哪一个数据库(数据库的username,password,ip,端口号等等信息)
第二步:查询数据库的某个表中的内容。需要写一条mysql的语句,去查询数据库某一个表里的信息
第三步:拿到数据库中的信息,并且用一个对象/字段 去保存信息。
第四步:将信息展示到页面上。
注意!数据库的连接要 有开有关。
Form1的构造函数代码
`
public Form1()
{
//将form1里面的[所有拖拽出来的控件],赋初值
InitializeComponent();
//连接数据库,获取数据
conn = new MySqlConnection(constr); //第一步:连接数据库。创建数据库连接类的对象
comm = new MySqlCommand("select * from student", conn); // 第二步:查询数据库的student表。创建数据库查询命令类的对象
// 打开数据库
conn.Open();
//第三步:用dr去接收查询出的内容
dr = comm.ExecuteReader();
// 调用Read(),循环遍历查询到的每一条数据
while (dr.Read()) //数据库有几条数据,dr.Read()执行几次。
{
//数据更新,UI暂时挂起,直到EndUpdate绘制控件,可以有效避免闪烁并大大提高加载速度
list_view.BeginUpdate();
ListViewItem lt = new ListViewItem();
//第四步:插入数据。将数据库数据转变成ListView类型的一行数据
lt.Text = dr["index"].ToString();
lt.SubItems.Add(dr["id"].ToString());
lt.SubItems.Add(dr["name"].ToString());
lt.SubItems.Add(dr["sex"].ToString());
lt.SubItems.Add(dr["birthday"].ToString());
lt.SubItems.Add(dr["reportday"].ToString());
lt.SubItems.Add(dr["handle"].ToString());
//将lt数据添加到listView1控件中
list_view.Items.Add(lt);
//结束数据处理,UI界面一次性绘制。
list_view.EndUpdate();
}
// 数据库使用完毕后关闭数据库连接
dr.Close();
conn.Close();
//设置 listview 的排序方法,在列标头事件中可以取出用来判断
list_view.Sorting = SortOrder.Ascending;
//设置按第1列排序
list_view.ListViewItemSorter = new ListViewItemComparer(list_view.Sorting, 1);
}
`
数据展示出后,我们需要建立3个button按钮,点击触发不同的事件
(2)主窗口的3个button按钮
思考!!这3个按钮不是一上来就显示在界面上的,并且按钮的位置,随着我们点击不同的行而改变。如何去实现呢?
第一步:需要在form1的load事件中,构造3个button实例对象,然后将可见属性visible设为false
第二步:当我们点击哪一行,这行就会出现按钮。所以要在对 list_view_SelectedIndexChanged()事件处理。
第三步:当我们点击某一行时,出现3个按钮以后。可以点击某一个按钮,触发响应的事件
`
private void list_view_SelectedIndexChanged(object sender, EventArgs e)
{
if (list_view.SelectedItems.Count > 0) //如果被选中行的数量>0,则确定button的位置,并且button的Visible属性设为 true
{
btn1.Location = new Point(list_view.SelectedItems[0].SubItems[6].Bounds.Left, list_view.SelectedItems[0].SubItems[6].Bounds.Top);
btn1.Visible = true;
// location的位置是(x,y)x=选中行的第1行,第7列 y=是这一列中第5项的高(任意一个项的高都可以)
btn2.Location = new Point(list_view.SelectedItems[0].SubItems[6].Bounds.Left + btn1.Width, list_view.SelectedItems[0].SubItems[5].Bounds.Top);
btn2.Visible = true;
btn3.Location = new Point(list_view.SelectedItems[0].SubItems[6].Bounds.Left + btn1.Width + btn2.Width, list_view.SelectedItems[0].SubItems[5].Bounds.Top);
btn3.Visible = true;
}
}
private void Form1_Load(object sender, EventArgs e)
{
btn1.Visible = false;
btn1.Text = "查看";
btn1.Click += button_Click;
list_view.Controls.Add(btn1);
btn1.Size = new Size(handle.Width / 3, list_view.Items[0].SubItems[0].Bounds.Height);
btn2.Visible = false;
btn2.Text = "修改";
btn2.Click += button_Click;
list_view.Controls.Add(btn2);
btn2.Size = new Size(handle.Width / 3, list_view.Items[0].SubItems[0].Bounds.Height);
btn3.Visible = false;
btn3.Text = "删除";
btn3.Click += button_Click;
list_view.Controls.Add(btn3);
btn3.Size = new Size(handle.Width / 3, list_view.Items[0].SubItems[0].Bounds.Height);
//插入滚动条
list_view.Scrollable = false;
ShowScrollBar((int)list_view.Handle, (int)ScrollBar.Vert , 1);
}
点击查看代码
private void button_Click(object sender, EventArgs e)
{
//用switch case 判断到底是哪一个button操作
string btn_Text = ((Button)sender).Text;
switch (btn_Text)
{
case "查看":
Look look = new Look(Convert.ToInt32(list_view.SelectedItems[0].SubItems[1].Text)); //构建一个Look类的实例对象,然后展示
look.Show();
break;
case "修改":
Update update = new Update(list_view.SelectedItems[0].SubItems[1].Text, this); //构建一个Update类的实例对象,然后展示
update.Show();
break;
case "删除":
DialogResult result = MessageBox.Show("确定删除吗?", "删除", MessageBoxButtons.YesNo, MessageBoxIcon.Question); //弹出对话框
if (result == DialogResult.Yes)
{
// 如果确定删除,去连接数据库,执行删除语句,然后关闭数据库
comm.CommandText = "delete from student where id = " + list_view.SelectedItems[0].SubItems[1].Text;
// 连接数据库
conn.Open();
comm.ExecuteNonQuery();
conn.Close();
// 删除成功之后,调用RefreshListView刷新页面
RefreshListView();
// 然后再次隐藏3个按钮
btn1.Visible = false;
btn2.Visible = false;
btn3.Visible = false;
}
break;
default:
break;
}
}
刷新方法
` public void RefreshListView()
{
// 刷新之前清空list_view中的已有数据
list_view.Items.Clear();
// 因为类的实例已经被new出来了,所以不需要再重新new实例了,只需要通过属性去修改字段即可
comm.CommandText = "select * from student";
// 连接数据库
conn.Open();
// 查询数据库
dr = comm.ExecuteReader();
// 调用Read(),循环遍历查询到的每一条数据
while (dr.Read())
{
//数据更新,UI暂时挂起,直到EndUpdate绘制控件,可以有效避免闪烁并大大提高加载速度
list_view.BeginUpdate();
ListViewItem lt = new ListViewItem();
//将数据库数据转变成ListView类型的一行数据
lt.Text = dr["index"].ToString();
lt.SubItems.Add(dr["id"].ToString());
lt.SubItems.Add(dr["name"].ToString());
lt.SubItems.Add(dr["sex"].ToString());
lt.SubItems.Add(dr["birthday"].ToString());
lt.SubItems.Add(dr["reportday"].ToString());
lt.SubItems.Add(dr["handle"].ToString());
//将lt数据添加到listView1控件中
list_view.Items.Add(lt);
//结束数据处理,UI界面一次性绘制。
list_view.EndUpdate();
}
// 数据库使用完毕后关闭数据库连接
dr.Close();
conn.Close();
}`
`
2 Look.cs查看页面
{
public partial class Look : Form
{
private string constr = "Database = db_student;Server = localhost;Port = 3306;Password = root;UserID = root;charset = utf8mb3";
//连接对象
private MySqlConnection conn = null;
//语句执行对象
private MySqlCommand comm = null;
//执行结果对象
private MySqlDataReader dr = null;
// 因为look是一个新的窗口(类),所以需要重新创建这三个对象
public Look(int id)
{
InitializeComponent();
// 因为look是一个新的窗口(类),要重新连接数据库,获取数据
conn = new MySqlConnection(constr); //创建数据库连接类的对象
comm = new MySqlCommand("select * from student where id = " + id.ToString(), conn); //创建数据库查询命令类的对象
// 连接数据库
conn.Open();
// 查询数据库
dr = comm.ExecuteReader();
// 预防脏数据
textBox1.Text = "";
// 调用Read(),循环遍历查询到的每一条数据
while (dr.Read())
{
//将数据库数据转变成ListView类型的一行数据
textBox1.Text += dr["index"].ToString()+"; ";
textBox1.Text += dr["id"].ToString()+"; ";
textBox1.Text += dr["name"].ToString() + "; ";
textBox1.Text += dr["sex"].ToString() + "; ";
textBox1.Text += dr["birthday"].ToString() + "; ";
textBox1.Text += dr["reportday"].ToString() + "; ";
}
// 数据库使用完毕后关闭数据库连接
dr.Close();
conn.Close();
}
}
}
3、update.cs更新页面
`
public partial class Update : Form
{
private string constr = "Database = db_student;Server = localhost;Port = 3306;Password = root;UserID = root;charset = utf8mb3";
//连接对象
private MySqlConnection conn = null;
//语句执行对象
private MySqlCommand comm = null;
//执行结果对象
private MySqlDataReader dr = null;
private Form1 form = null;
public Update(string id, Form1 form1)
{
// 获取主界面实例,用于更新数据以后刷新主界面
form = form1;
InitializeComponent();
// 连接数据库,获取数据
conn = new MySqlConnection(constr); //创建数据库连接类的对象
comm = new MySqlCommand("select * from student where id ="+ id , conn); //创建数据库查询命令类的对象
// 连接数据库
conn.Open();
// 查询数据库
dr = comm.ExecuteReader();
textBox_id.Text = "";
textBox_name.Text = "";
radioButton_male.Checked = false;
radioButton_female.Checked = false;
textBox_reportday.Text = "";
// 调用Read(),循环遍历查询到的每一条数据
while (dr.Read())
{
textBox_id.Text = dr["id"].ToString();
textBox_name.Text = dr["name"].ToString();
//对性别进行判断处理
if ("男" == dr["sex"].ToString())
{
radioButton_male.Checked = true;
}
else if ("女" == dr["sex"].ToString())
{
radioButton_female.Checked = true;
}
else
{
MessageBox.Show("性别错误!");
}
//对出生日期进行分隔处理
string birthday = dr["birthday"].ToString();
string[] birthdayArr = birthday.Split('/');
dateTime_birthday.Value = new DateTime(Convert.ToInt32(birthdayArr[0]),Convert.ToInt32(birthdayArr[1]),Convert.ToInt32(birthdayArr[2]));
textBox_reportday.Text = dr["reportday"].ToString();
}
dr.Close();
}
private void button_confirm_Click(object sender, EventArgs e)
{
try
{
// 对性别修改进行判断
string temp_sex = "";
if (radioButton_male.Checked)
temp_sex = "男";
else if (radioButton_female.Checked)
temp_sex = "女";
// 保存时根据控件特性需要对出生日期进行处理,将2020/10/22 19:20以空格为分隔
string[] arr = dateTime_birthday.Value.ToString().Split(' ');
string updateContent = string.Format("UPDATE student SET name='{0}',sex='{1}',birthday='{2}' WHERE id={3};",
textBox_name.Text.ToString(),
temp_sex,
arr[0],
textBox_id.Text);
// 执行数据库带条件查询命令
comm = new MySqlCommand(updateContent, conn);
int num = 0;
// 成功返回的是一受影响的行数,
num = comm.ExecuteNonQuery();
if (num > 0)
{
MessageBox.Show("修改成功");
}
else
{
MessageBox.Show("修改失败");
}
}
catch (Exception)
{
throw;
}
finally
{
//更新完毕,关闭数据库连接
conn.Close();
form.RefreshListView();
//关闭窗口
Close();
}
}
private void Update_Load(object sender, EventArgs e)
{
}
private void button_cancel_Click(object sender, EventArgs e)
{
this.Close();
}
private void radioButton_female_CheckedChanged(object sender, EventArgs e)
{
}
}
}
`
4.删除控件,用message.box写在主窗体中即可(在button.Click中)
5.点击列表排序 list_view_ColumnClick(重点!!!)
鼠标单击每一项的列头,实现列表顺序排序。鼠标再点一次,则为逆序排序。
笔者最开始的想法是,先设置一个flag标志位,用点击次数判断,true为正序,false为逆序。
然后拿到这一行的所有数据,比如说点击index编号的列头,利用id值去查数据库,找到这一列的所有index,然后存入数组中。然后对数组进行升序或降序,然后从数组中依次取出按照index排好的每一条数据。
但是经过试验,明明将数据排好序插入了,但发现在listview中数据不更新,还是按照原来的顺序排列。疑惑!猜想这可能是listview这个控件的排序规则问题,可能不是从上到下,按照查询的顺序,依次将列表填满的。
于是上网查询,发现listview中有一个ListViewItemSorter属性,它接受一个实现了IComparer接口类型的值。
在list_view_ColumnClick中以SortOrder这个枚举值做判断
所以定义了一个ListViewItemComparer类,继承了IComparer接口,续写了Compare(object x, object y)方法。
将ListViewItemComparer类的实例 赋给 list_view.ListViewItemSorter
在Compare(object x, object y)中,定义排序方法
`
//定义一个类,这个类实现 IComparer 接口
class ListViewItemComparer : IComparer
{
private int col;
private SortOrder sort = SortOrder.None;
public ListViewItemComparer(SortOrder sortOrder)
{
col = 0;
}
public ListViewItemComparer(SortOrder sortOrder, int column)
{
sort = sortOrder;
col = column;
}
//实现接口的 Compare 方法,x、y 为要比较的两个对象
public int Compare(object x, object y)
{
//默认升序。判断传入的排序枚举,如果为降序就对换要比较的对象
if (sort == SortOrder.Descending)
{
object temp = x; //x、y是第几行
x = y;
y = temp;
}
string xx = ((ListViewItem)x).SubItems[col].Text; // xx 是第x行的第col列的文本
string yy = ((ListViewItem)y).SubItems[col].Text; // yy 是第y行的第col列的文本
int xxx = 0;
int yyy = 0;
//判断是否可以转换为数字,如果可以就按数字比较
if (int.TryParse(xx.ToString(), out xxx) && int.TryParse(yy.ToString(), out yyy))
return xxx.CompareTo(yyy);//按数字比较
return string.Compare(xx, yy);//按字符比较
}
}
`