首页 > Python基础教程 >
-
C#实现简单成绩管理系统的完整步骤
前言
这周跟C#打了一周的交道(本周是学校安排的实验周,然后用到了C#实现想要的程序和功能)
一共七个实验,选择三个,我就选择我进步最大的一个来分析一下吧。
效果
先来看一下效果吧
从txt文本中读取数据后展示出来
点击目标后选中,然后点击“修改”,弹出修改界面,然后进行编辑即可
点击“统计”按钮,弹出窗口显示各分数段的信息
点击“查询”后,弹出界面,输入后,点击“确定”即可显示信息
实现
一、准备工作
在写方法之前,首先就是先把界面规划好,就是通过添加按钮和输入框或显示框组成一个符合要求的窗口或多个窗口
在这个程序中我们用到的主要是这几个组件
对文件进行操作要进行引用的声明,即“using”
我们添加的是这两行
然后我们还要写一些代码来实现其他功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public Form1() { InitializeComponent(); this .listView1.Columns.Add( "学号" , 100, HorizontalAlignment.Center); this .listView1.Columns.Add( "姓名" , 100, HorizontalAlignment.Center); this .listView1.Columns.Add( "数学" , 100, HorizontalAlignment.Center); this .listView1.Columns.Add( "英语" , 100, HorizontalAlignment.Center); this .listView1.Columns.Add( "政治" , 100, HorizontalAlignment.Center); this .listView1.Columns.Add( "总分" , 100, HorizontalAlignment.Center); this .listView1.Columns.Add( "平均分" , 100, HorizontalAlignment.Center); this .listView1.Columns.Add( "名次" , 100, HorizontalAlignment.Center); this .listView1.View = System.Windows.Forms.View.Details; this .listView1.FullRowSelect = true ; //是否可以选择行 } |
“listview1”就是按钮上方实现显示的控件,“this”指的就是Form1这个窗口,“Columns”指的是“栏”,也就是上方的内容,“add”指的是把后面的内容作为“Columns”的内容,后面的“100”等都是“Columns”的属性,可以通过修改它的属性来修改它的大小和位置,还有一种生成“Column”的方法是通过属性栏来添加。
点击listview一次选中它,然后右键单击一次,点击属性,会发现有“Column”这个属性,点进去后就可以进行编辑和修改了。
不得不说确实挺方便的,不过实验报告手册中给了部分必须的源码,再加上自己第一次接触C#,所以就没使用后面的方法,不过在后面的操作中使用了一下,确实挺爽。
二、读取操作
这里的“读取”按钮读取的是统计之后的内容,并非成绩等信息,双击“读取”按钮后即可进行编辑(在按钮的属性中我修改了name属性为load,所以此处的方法名为“load_Click”)
1
2
3
4
|
private void load_Click( object sender, EventArgs e) { this .load_data(); } |
此处调用了“load_data()”这个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public void load_data() { string file = File.ReadAllText( "Score.txt" , UTF8Encoding.Default); //把txt文件中按行存储的信息利用regex.Split按行存入string数组中 string [] records = Regex.Split(file, "\r\n" ); //开始更新视图 this .listView1.BeginUpdate(); //清空原有视图 this .listView1.Items.Clear(); // records.Length为数组的元素个数 for ( int index = 0; index < records.Length; index++) { //分割每行记录的各个字段 string [] components = Regex.Split(records[index], " " ); //生成listview的一行 ListViewItem lvi = new ListViewItem(components); //添加背景色 lvi.SubItems[0].BackColor = Color.Red; //把新生成的行加入到listview中 this .listView1.Items.Add(lvi); } //视图更新结束 this .listView1.EndUpdate(); } |
这个方法就是以“/r/n”为分界线定义一个数组1,然后再以空格为分界线定义一个数组2,同时生成一个 ListViewItem 来显示数组2,然后再设置一下背景色,此处设置的为红色
1
2
3
4
|
//生成listview的一行 ListViewItem lvi = new ListViewItem(components); //添加背景色 lvi.SubItems[0].BackColor = Color.Red; |
lvi是新生成的listview的命名
三、查询操作
1
2
3
4
5
|
private void Search_Click( object sender, EventArgs e) { Form3 f3 = new Form3(); f3.Show(); } |
在查询的方法中我们调用了一个窗口Form3,同Form1一样,先规划好窗口的格局,然后再写方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
private void go_Click( object sender, EventArgs e) { string file = File.ReadAllText( "Score.txt" , UTF8Encoding.Default); //把txt文件中按行存储的信息利用regex.Split按行存入string数组中 string [] records = Regex.Split(file, "\r\n" ); //开始更新视图 this .listView1.BeginUpdate(); //清空原有视图 this .listView1.Items.Clear(); // records.Length为数组的元素个数 for ( int index = 0; index < records.Length; index++) { //分割每行记录的各个字段 string [] components = Regex.Split(records[index], " " ); Regex r = new Regex( this .textBox1.Text); // 定义一个Regex对象实例 Match m = r.Match(components[0]); // 在字符串中匹配 if (m.Success) { //生成listview的一行 ListViewItem lvi = new ListViewItem(components); //添加背景色 lvi.SubItems[0].BackColor = Color.White; //把新生成的行加入到listview中 this .listView1.Items.Add(lvi); } else if (components.Length > 1) { Match n = r.Match(components[1]); if (n.Success) { //生成listview的一行 ListViewItem lvi = new ListViewItem(components); //添加背景色 lvi.SubItems[0].BackColor = Color.White; //把新生成的行加入到listview中 this .listView1.Items.Add(lvi); } } } //视图更新结束 this .listView1.EndUpdate(); } |
主要的方法,甚至是唯一的方法,就是上方的“go_Click()”方法,“Score.txt”是存放成绩等信息的,在姓名和学号之中匹配,匹配到了就新建一个listview的item展示信息
这里的查询用到了匹配的函数Match() ,在此给出官方的链接
Match()
四、删除
删除的思想就是选中要删除的那一行,之后从文件读取所有内容,新建string数组存放每一行,一行一行的遍历,直到遇见选中的那一行,从数组中移除,然后把剩余的数组元素写入文本中,WriteAllLines()方法定义中调用了replace()方法,因此省去了我们删除文件原有内容的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
private void Delate_Click( object sender, EventArgs e) //删除 { foreach (ListViewItem lvi in listView1.SelectedItems) { //把txt文件内容读入到file中,然后对string对象file进行相关处理 string file = File.ReadAllText( "Score.txt" , UTF8Encoding.Default); List< string > lines = new List< string >(Regex.Split(file, "\r\n" )); lines.RemoveAt(lvi.Index); File.WriteAllLines( "Score.txt" , lines.ToArray(), UTF8Encoding.Default); } this .load_data(); } |
五、录入
1
2
3
4
5
|
private void Input_Click( object sender, EventArgs e) //录入 { Form2 f = new Form2( this ); f.Show(); } |
这里的录入方法调用了一个新的窗口Form2,与之前不同的是,这里给Form2传了一个参数“this”,即Form1这个窗口,因为之后我们要在Form2的方法中调用Form1的方法。
再来看一下Form2的界面和方法
1、接收参数
1
2
3
4
5
6
7
8
9
10
|
private Form1 form; public Form2() { InitializeComponent(); } public Form2(Form1 form){ this .form = form; InitializeComponent(); } |
首先进行定义,然后进行赋值,这样的话在之后就可以用定义时使用的名称进行Form1的方法的调用了。
2、保存信息
在点击“保存”按钮后,我们要实现文本的写入以及界面的更新,文本的写入我们要写新的方法,界面的更新我们可以调用Form1的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
if ( this .textBox1.Text != string .Empty && this .textBox2.Text != string .Empty && this .textBox3.Text != string .Empty && this .textBox4.Text != string .Empty && this .textBox5.Text != string .Empty) { //利用string的加法操作构造新纪录,作为一行,写入txt文件中 string newrecord = this .textBox1.Text + " " + this .textBox2.Text + " " + this .textBox3.Text + " " + this .textBox4.Text + " " + this .textBox5.Text + "\r\n" ; File.AppendAllText( "Score.txt" , newrecord, UTF8Encoding.Default); //结束form2的调用 this .Dispose( false ); //调用Form1加载界面的方法 this .form.load_data(); } else { Form5 f5 = new Form5(); f5.Show(); } |
首先判空,如果都不为空,定义新的字符串,赋值,写入文本,为了保证程序的严谨性,加一个Form5窗口进行错误的提示
(由于时间的限制,这里也不算很完善,有能力的可以考虑进行各个条件的判断,例如“学号”为空,则弹出的窗口显示“学号不能为空”)
值得注意的是,这里调用的Form1中的方法必须是公有的,即“public”。
六、运算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
private void operate_Click( object sender, EventArgs e) { //把txt文件内容读入到file中,然后对string对象file进行相关处理 string file = File.ReadAllText( "Score.txt" , UTF8Encoding.Default); //把txt文件中按行存储的信息利用regex.Split按行存入string数组中 string [] records = Regex.Split(file, "\r\n" ); //开始更新视图 this .listView1.BeginUpdate(); //清空原有视图 this .listView1.Items.Clear(); System.IO.File.WriteAllText( "Score.txt" , string .Empty); double [] score1 = new double [records.Length]; double [] score2 = new double [records.Length]; int num = 0; // records.Length为数组的元素个数 for ( int index = 0; index < records.Length; index++) { if (records[index].Length != 0) { //分割每行记录的各个字段 string [] components = Regex.Split(records[index], " " ); score1[index] = Convert.ToDouble(components[2]) + Convert.ToDouble(components[3]) + Convert.ToDouble(components[4]); score2[index] = (Convert.ToDouble(components[2]) + Convert.ToDouble(components[3]) + Convert.ToDouble(components[4])) / 3.0; num++; } else break ; } //冒泡法排序 int temp; for ( int i = 0; i < num; i++) { temp = i; for ( int j = i + 1; j < num; j++) { if (score1[j] > score1[temp]) temp = j; } double t = score1[temp]; score1[temp] = score1[i]; score1[i] = t; } for ( int index = 0; index < records.Length; index++) { if (records[index].Length != 0) { //分割每行记录的各个字段 string [] components = Regex.Split(records[index], " " ); for ( int i = 1; i <= num; i++) { if (score1[i - 1] == Convert.ToDouble(components[2]) + Convert.ToDouble(components[3]) + Convert.ToDouble(components[4])) { //利用string的加法操作构造新纪录,作为一行,写入txt文件中 string newrecord = components[0] + " " + components[1] + " " + components[2] + " " + components[3] + " " + components[4] + " " + score1[i - 1] + " " + score2[index] + " " + i + "\r\n" ; File.AppendAllText( "Score.txt" , newrecord, UTF8Encoding.Default); } } } else break ; } //视图更新结束 this .listView1.EndUpdate(); //刷新界面 this .load_data(); } |
题目的要求是计算各学生总分、平均分以及排名,思想就是,先读取文件,存储数据,清空文件,定义总成绩和平均成绩两个数组,分别计算每个人的总成绩与平均成绩,然后用冒泡法对总成绩进行排序,得到排名,然后构造新数组拼接这些内容,再把数组写入文本,更新视图即可。
七、修改
修改的思想很简单,虽说实现了想要的功能,但是也有缺陷,也没想到特别好的解决办法,先来说一下思路吧,首先选中,然后点击“修改”,弹窗,在这里尤其注意得是,弹窗里对应位置要显示选中的内容
在这个图中,我选中的是第二行,那么我就要把它对应的数据显示上去,在实验过程中很多人所谓的修改就是输入新的数据,里面所有的内容都得再写一遍,这样的系统,自己用着指定难受。
(说到这我想插上一件事,吐槽一下最近使用的二课系统吧,是一个微信小程序,这是学校某个团队开发的,总体上还是可以的,最大的不足就是每次打开都得登录,也就是说每次使用都得重新输入账号和密码,用户的体验真的挺差的,但是也不能因为它有不足就否认它,总体上也算挺方便的,值得去学习)
因为以前参与过项目的开发,而且项目的受众就是我们自己,也知道什么样的效果更能使用户满意,所以对修改这个功能也算是有所了解,在实验中,绝大多数人的“修改”功能都没能达到满意的效果,所以说经验积累绝对不是毫无用处的。
继续回到我们的功能实现中来,点击“确定”后,写入文本,那么我们肯定要把之前的数据删掉,在传参后立即删除数据,当然这也就产生了瑕疵,用户不修改数据,那怎么办,先来看代码吧,看完就知道了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
private void modify_Click( object sender, EventArgs e) //修改 { if (listView1.SelectedItems.Count != 0 ) { ListViewItem all = this .listView1.SelectedItems[0]; Form6 f6 = new Form6(all, this ); f6.Show(); //把数据传输过去之后立即删除数据 foreach (ListViewItem lvi in listView1.SelectedItems) { //把txt文件内容读入到file中,然后对string对象file进行相关处理 string file = File.ReadAllText( "Score.txt" , UTF8Encoding.Default); List< string > lines = new List< string >(Regex.Split(file, "\r\n" )); lines.RemoveAt(lvi.Index); File.WriteAllLines( "Score.txt" , lines.ToArray(), UTF8Encoding.Default); } } else { Form4 f4 = new Form4(); f4.Show(); } } |
如果选中的内容不为空,那就把它的数据存到ListViewItem类型的数组中,然后传给Form6(修改数据窗口的类),同时再把Form1这个类传过去,以便调用它的方法,然后使用一个删除方法,如果内容为空,弹出Form4,提示错误。
同样的,在Form6这边先定义,然后接收参数,赋值
1
2
3
4
5
6
7
8
9
|
private void Form6_Load( object sender, EventArgs e) { this .textBox1.Text = this .content.SubItems[0].Text; this .textBox2.Text = this .content.SubItems[1].Text; this .textBox3.Text = this .content.SubItems[2].Text; this .textBox4.Text = this .content.SubItems[3].Text; this .textBox5.Text = this .content.SubItems[4].Text; } |
在加载方法中为textBox赋值,以此来显示数据,修改后点击“确定”,确定的方法和“录入”功能的“确定”的方法相同,说白了就是写入,如果“取消”怎么办,数据已经删除了,既然textBox里本来就有数据,那么就把那些数据再写入文本,这样虽然实现了我的功能,但是有瑕疵,就是无论修改数据与否,重新加载后数据都会显示在最后一行。
“确定”方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
private void Determine_Click( object sender, EventArgs e) { if ( this .textBox1.Text != string .Empty && this .textBox2.Text != string .Empty && this .textBox3.Text != string .Empty && this .textBox4.Text != string .Empty && this .textBox5.Text != string .Empty) { //利用string的加法操作构造新纪录,作为一行,写入txt文件中 string newrecord = this .textBox1.Text + " " + this .textBox2.Text + " " + this .textBox3.Text + " " + this .textBox4.Text + " " + this .textBox5.Text + "\r\n" ; File.AppendAllText( "Score.txt" , newrecord, UTF8Encoding.Default); var lines = File.ReadAllLines( "Score.txt" ).Where(arg => ! string .IsNullOrWhiteSpace(arg)); File.WriteAllLines( "Score.txt" , lines); //结束form6的调用 this .Dispose( false ); } this .form.load_data(); } |
八、统计
所谓的统计就是计算各科各分数段人数以及各科平均分
1
2
3
4
5
6
7
|
private void Sta_Click( object sender, EventArgs e) //统计 { this .Output(); Form7 f7 = new Form7(); f7.Show(); } |
调用一个output()方法,用Form7显示数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public string Output() //写入数据 { string add = string .Empty; add += "统计情况:" + "\r\n" + "分数段" + "," + "数学" + "," + "英语" + "," + "政治" + "\r\n" ; add += "<60" + "," + GetGradeSection(1, 0, 59).ToString() + "," + GetGradeSection(2, 0, 59).ToString() + "," + GetGradeSection(3, 0, 59).ToString() + "\r\n" ; add += "60~69" + "," + GetGradeSection(1, 60, 69).ToString() + "," + GetGradeSection(2, 60, 69).ToString() + "," + GetGradeSection(3, 60, 69).ToString() + "\r\n" ; add += "70~79" + "," + GetGradeSection(1, 70, 79).ToString() + "," + GetGradeSection(2, 70, 79).ToString() + "," + GetGradeSection(3, 70, 79).ToString() + "\r\n" ; add += "80~89" + "," + GetGradeSection(1, 80, 89).ToString() + "," + GetGradeSection(2, 80, 89).ToString() + "," + GetGradeSection(3, 80, 89).ToString() + "\r\n" ; add += "90~100" + "," + GetGradeSection(1, 90, 100).ToString() + "," + GetGradeSection(2, 90, 100).ToString() + "," + GetGradeSection(3, 90, 100).ToString() + "\r\n" ; add += "平均分" + "," + GetAve(1).ToString() + "," + GetAve(2).ToString() + "," + GetAve(3).ToString() + "\r\n" ; System.IO.File.WriteAllText( "List.txt" , string .Empty); File.AppendAllText( "List.txt" , add, UTF8Encoding.Default); return add; } |