Проблема:
Этот подход вызывает видимое мерцание, теряет текущую позицию выбора/прокрутки и кажется неэффективным по мере роста списка.
Чего я хочу достичь:
Я хочу использовать дополнительные надежный способ (например, BindingSource или BindingList) для синхронизации списка и DataGridView. Я слышал о привязке данных, но не знаю, как правильно ее реализовать, сохраняя при этом возможность:
- Форматировать определенные значения (например, добавлять «%» к процентам).
- Фильтровать данные (например, по теме).
- Обрабатывать логические значения как текст «Да/Нет» вместо флажков.
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();
}
}
}