C# Winforms TestGradingSystem [закрыто]C#

Место общения программистов C#
Anonymous
C# Winforms TestGradingSystem [закрыто]

Сообщение Anonymous »

Недавно я снова начал изучать и практиковать C# WinForms после довольно длительного перерыва и восстанавливаю свои основы с помощью небольших практических проектов, прежде чем перейти к более крупным приложениям.
Этот проект немного сложнее, чем мои предыдущие практические проекты. Это простая система выставления оценок за тесты учащихся, в которой я попытался объединить несколько общих функций WinForms в одно приложение.
В настоящее время приложение поддерживает:
  • добавление результатов учащихся через отдельную форму
  • автоматический расчет процентов и оценок
  • обработку бонусов расширенного уровня
  • Подсчет сдачи/неудачки
  • отображение данных в DataGridView
  • фильтрация и поиск учащихся
  • сортировка по процентам
  • статистика и расчет наилучшего результата
  • Экспорт TXT и CSV
  • функция сохранения/загрузки
  • действия на основе меню
Некоторые элементы управления и концепции WinForms, которые я использовал:
  • DataGridView
  • Поле со списком
  • NumericUpDown
  • Флажок
  • Радиокнопки
  • Групповые поля
  • Полоса меню
  • SaveFileDialog / OpenFileDialog
  • Проверка MessageBox
  • Общение форм
  • Коллекции списков
  • LINQ
  • обработка файлов
  • обработка событий
  • циклы и условная логика
Я загружу полные файлы проекта вместе со скриншотами приложения и меню.
Особенно буду признателен за отзывы о:
  • общая структура и организация
  • является ли использование List разумным подходом
  • обновление DataGridView вручную
  • разделение логики пользовательского интерфейса и логики вычислений
  • сохранить/загрузить реализация
  • именование и проверка
  • более чистые методы WinForms в целом
Я не ищу кого-то, кто полностью перепишет за меня проект. Поскольку в настоящее время я заново изучаю WinForms после длительного перерыва, я в основном хочу понять, какие привычки, структурные решения или практики мне следует улучшить на раннем этапе.
StudentResult.cs:

Код: Выделить всё

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestGradingSystem
{
public class StudentResult
{
public string Name { get; set; }
public string Subject { get; set; }
public int Score { get; set; }
public int MaxScore { get; set; }
public double Percentage { get; set; }
public int Grade { get; set; }
public bool Passed { get; set; }
public bool AdvancedLevel { get; set; }
public string TestType { get; set; }
}
}
Form1.cs:

Код: Выделить всё

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TestGradingSystem
{
public partial class Form1 : Form
{
List results = new List();
public Form1()
{
InitializeComponent();
dataGridView1.Columns.Clear();

dataGridView1.Columns.Add("Name", "Name");
dataGridView1.Columns.Add("Subject", "Subject");
dataGridView1.Columns.Add("Score", "Score");
dataGridView1.Columns.Add("MaxScore", "Max Score");
dataGridView1.Columns.Add("Percentage", "Percentage");
dataGridView1.Columns.Add("Grade", "Grade");
dataGridView1.Columns.Add("Passed", "Passed");
dataGridView1.Columns.Add("AdvancedLevel", "Advanced Level");
dataGridView1.Columns.Add("TestType", "Test Type");

dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView1.MultiSelect = false;
dataGridView1.ReadOnly = true;
dataGridView1.AllowUserToAddRows = false;
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;

comboBox1.Items.Clear();
comboBox1.Items.Add("All Subjects");
comboBox1.Items.Add("Mathematics");
comboBox1.Items.Add("Computer Science");
comboBox1.Items.Add("History");
comboBox1.Items.Add("English");
comboBox1.SelectedIndex = 0;

}

private void RefreshGrid(List listToShow = null)
{
dataGridView1.Rows.Clear();

List list = listToShow ?? results;

foreach (StudentResult item in list)
{
dataGridView1.Rows.Add(
item.Name,
item.Subject,
item.Score,
item.MaxScore,
item.Percentage.ToString("0.00") + "%",
item.Grade,
item.Passed ? "Yes" : "No",
item.AdvancedLevel ? "Yes" : "No",
item.TestType
);
}

RefreshSummary();
}
private void RefreshSummary()
{
int count = results.Count;

label4.Text = count.ToString();

if (count == 0)
{
label5.Text = "0%";
label6.Text = "0";
label10.Text = "0";
label11.Text = "0";
label12.Text = "-";
return;
}

double avgPercentage = results.Average(x => x.Percentage);
double avgGrade = results.Average(x => x.Grade);
int passedCount = results.Count(x => x.Passed);
int failedCount = results.Count(x => !x.Passed);

StudentResult best = results.OrderByDescending(x =>  x.Percentage).First();

int totalStudents = passedCount + failedCount;

double passRate = 0;

if (totalStudents > 0)
{
passRate = (double)passedCount / totalStudents * 100;
}

label5.Text = avgPercentage.ToString("0.00") + "%";
label6.Text = avgGrade.ToString("0.00");
label10.Text = passedCount.ToString();
label11.Text = failedCount.ToString();
label12.Text = passRate.ToString("0.00") + "%";
}

private void newStudentToolStripMenuItem_Click(object sender, EventArgs e)
{
Form2 form = new Form2();

if (form.ShowDialog() == DialogResult.OK)
{
results.Add(form.Result);
RefreshGrid();
}
}

private void deleteSelectedToolStripMenuItem_Click(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count == 0)
{
MessageBox.Show("Select a row first!");
return;
}

int index = dataGridView1.SelectedRows[0].Index;

results.RemoveAt(index);

RefreshGrid();
}

private void deleteAllToolStripMenuItem_Click(object sender, EventArgs e)
{
DialogResult answer = MessageBox.Show(
"Are you sure you want to delete everything?",
"Confirmation",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning
);

if (answer == DialogResult.Yes)
{
results.Clear();
RefreshGrid();
}
}

private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}

private void bestResultToolStripMenuItem_Click(object sender, EventArgs e)
{
if (results.Count == 0)
{
MessageBox.Show("No data!");
return;
}

StudentResult best =
results.OrderByDescending(x => x.Percentage).First();

label16.Text = best.Name;
label17.Text = best.Percentage.ToString("0.00") + "%";
label18.Text = best.Grade.ToString();
}

private void calculateAvaragesToolStripMenuItem_Click(object sender, EventArgs e)
{
RefreshSummary();

MessageBox.Show("Statistics updated!");
}

private void filterPassedStudentsToolStripMenuItem_Click(object sender, EventArgs e)
{
List passedOnly = results
.Where(x => x.Passed)
.ToList();

RefreshGrid(passedOnly);
}

private void sortByPercentageToolStripMenuItem_Click(object sender, EventArgs e)
{

}

private void ascendingToolStripMenuItem_Click(object sender, EventArgs e)
{
results = results.OrderBy(x => x.Percentage).ToList();
RefreshGrid();
}

private void descendingToolStripMenuItem_Click(object sender, EventArgs e)
{
results = results.OrderByDescending(x => x.Percentage).ToList();
RefreshGrid();
}

private void statisticsToolStripMenuItem_Click(object sender, EventArgs e)
{
if (results.Count == 0)
{
MessageBox.Show("No data!");
return;
}

int total = results.Count;
int passed = results.Count(x => x.Passed);
int failed = results.Count(x => !x.Passed);
double avgPercentage = results.Average(x => x.Percentage);
double avgGrade = results.Average(x => x.Grade);

MessageBox.Show(
"Total students: " + total +
"\nPassed students: " + passed +
"\nFailed students: " + failed +
"\nAverage percentage: " + avgPercentage.ToString("0.00") + "%"  +
"\nAverage grade: " + avgGrade.ToString("0.00"),
"Statistics"
);
}

private void button1_Click(object sender, EventArgs e)
{
string nameSearch = textBox1.Text.Trim().ToLower();
string subjectFilter = comboBox1.SelectedItem?.ToString();
bool passedOnly = checkBox1.Checked;

List filtered = results;

if (nameSearch != "")
{
filtered = filtered
.Where(x => x.Name.ToLower().Contains(nameSearch))
.ToList();
}

if (subjectFilter != null && subjectFilter != "All Subjects")
{
filtered = filtered
.Where(x => x.Subject == subjectFilter)
.ToList();
}

if (passedOnly)
{
filtered = filtered
.Where(x => x.Passed)
.ToList();
}

if (filtered.Count == 0)
{
MessageBox.Show("No matching result!");
}

RefreshGrid(filtered);
}

private void searchByNameToolStripMenuItem_Click(object sender, EventArgs e)
{
textBox1.Focus();
}

private void exportToTXTToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog save = new SaveFileDialog();

save.Filter = "Text File|*.txt";

if (save.ShowDialog() == DialogResult.OK)
{
using (StreamWriter writer = new StreamWriter(save.FileName))
{
foreach (StudentResult item in results)
{
writer.WriteLine(
item.Name + " | " +
item.Subject + " | " +
item.Score + "/" + item.MaxScore + " | " +
item.Percentage.ToString("0.00") + "% | " +
item.Grade
);
}
}

MessageBox.Show("TXT export completed!");
}
}

private void exportToCSVToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog save = new SaveFileDialog();

save.Filter = "CSV File|*.csv";

if (save.ShowDialog() == DialogResult.OK)
{
using (StreamWriter writer = new StreamWriter(save.FileName))
{
writer.WriteLine(
"Name,Subject,Score,MaxScore,Percentage,Grade,Passed,AdvancedLevel,TestType"
);

foreach (StudentResult item in results)
{
writer.WriteLine(
item.Name + "," +
item.Subject + "," +
item.Score + "," +
item.MaxScore + "," +
item.Percentage.ToString("0.00") + "," +
item.Grade + "," +
item.Passed + "," +
item.AdvancedLevel + "," +
item.TestType
);
}
}

MessageBox.Show("CSV export completed!");
}
}

private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog save = new SaveFileDialog();

save.Filter = "Data File|*.dat";

if (save.ShowDialog() == DialogResult.OK)
{
using (StreamWriter writer = new StreamWriter(save.FileName))
{
foreach (StudentResult item in results)
{
writer.WriteLine(
item.Name + ";" +
item.Subject + ";"  +
item.Score + ";" +
item.MaxScore + ";" +
item.Percentage + ";" +
item.Grade + ";" +
item.Passed + ";" +
item.AdvancedLevel + ";" +
item.TestType
);
}
}

MessageBox.Show("Data saved!");
}
}

private void loadToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog open = new OpenFileDialog();

open.Filter = "Data File|*.dat";

if (open.ShowDialog() == DialogResult.OK)
{
results.Clear();

string[] lines = File.ReadAllLines(open.FileName);

foreach (string line in lines)
{
string[] parts = line.Split(';');

StudentResult item = new StudentResult
{
Name = parts[0],
Subject = parts[1],
Score = int.Parse(parts[2]),
MaxScore = int.Parse(parts[3]),
Percentage = double.Parse(parts[4]),
Grade = int.Parse(parts[5]),
Passed = bool.Parse(parts[6]),
AdvancedLevel = bool.Parse(parts[7]),
TestType = parts[8]
};

results.Add(item);
}

RefreshGrid();

MessageBox.Show("Data loaded!");
}
}
}
}
Form2.cs:

Код: Выделить всё

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TestGradingSystem
{
public partial class Form2 : Form
{
public StudentResult Result { get; private set; }

public Form2()
{
InitializeComponent();
numericUpDown1.ValueChanged += (s, e) => UpdateInfo();
numericUpDown2.ValueChanged += (s, e) => UpdateInfo();
checkBox1.CheckedChanged += (s, e) => UpdateInfo();

comboBox1.Items.Clear();
comboBox1.Items.Add("Mathematics");
comboBox1.Items.Add("Computer Science");
comboBox1.Items.Add("History");
comboBox1.Items.Add("English");

numericUpDown1.Minimum = 0;
numericUpDown1.Maximum = 500;

numericUpDown2.Minimum = 1;
numericUpDown2.Maximum = 500;
numericUpDown2.Value = 100;

UpdateInfo();
}

private void UpdateInfo()
{
int score = (int)numericUpDown1.Value;
int maxScore = (int)numericUpDown2.Value;

double percentage = (double)score / maxScore * 100;

if (checkBox1.Checked)
{
percentage += 5;
}

if (percentage > 100)
{
percentage = 100;
}

int grade;

if (percentage < 40)
grade = 1;
else if (percentage < 55)
grade = 2;
else if (percentage < 70)
grade = 3;
else if (percentage < 85)
grade = 4;
else
grade = 5;

bool passed = percentage >= 40;

label9.Text = percentage.ToString("0.00") + "%";
label10.Text = grade.ToString();
label11.Text = passed ? "Yes"  : "No";
}

private void button2_Click(object sender, EventArgs e)
{
if (textBox1.Text.Trim() == "")
{
MessageBox.Show("Name is required!");
return;
}

if (comboBox1.SelectedItem == null)
{
MessageBox.Show("Select a subject!");
return;
}

if (numericUpDown1.Value > numericUpDown2.Value)
{
MessageBox.Show("Score cannot be greater than max score!");
return;
}

if (!radioButton1.Checked && !radioButton2.Checked &&!radioButton3.Checked)
{
MessageBox.Show("Select a test type!");
return;
}

string testType = "";

if (radioButton1.Checked)
testType = "Quiz";
else if (radioButton2.Checked)
testType = "Final Test";
else if (radioButton3.Checked)
testType = "Exam";

int score = (int)numericUpDown1.Value;
int maxScore = (int)numericUpDown2.Value;

double percentage = (double)score / maxScore * 100;

if (checkBox1.Checked)
{
percentage += 5;
}

if (percentage > 100)
{
percentage = 100;
}

int grade;

if (percentage < 40)
grade = 1;
else if (percentage < 55)
grade = 2;
else if (percentage < 70)
grade = 3;
else if (percentage < 85)
grade = 4;
else
grade = 5;

bool passed = percentage >= 40;

Result = new StudentResult
{
Name = textBox1.Text.Trim(),
Subject = comboBox1.SelectedItem.ToString(),
Score = score,
MaxScore = maxScore,
Percentage = percentage,
Grade = grade,
Passed = passed,
AdvancedLevel = checkBox1.Checked,
TestType = testType
};

DialogResult = DialogResult.OK;
Close();
}

private void button1_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
}
}
введите здесь описание изображения

введите здесь описание изображения

введите здесь описание изображения

Вернуться в «C#»