Как правильно синхронизировать список с DataGridView в WinForms без очистки строк вручную? [закрыто]C#

Место общения программистов C#
Anonymous
Как правильно синхронизировать список с DataGridView в WinForms без очистки строк вручную? [закрыто]

Сообщение Anonymous »

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