您的问题是,因为您将数据源与显示的数据混合在一起。
MVVM:将数据处理与显示方式分开
在现代编程中,倾向于将数据及其操作(= 模型)与其显示方式(= 视图)分开。这样做的好处是您可以将模型重用于完全不同的视图。例如,您可以在图表中显示数据。同样,您可以将视图重用于其他类似数据。
在没有任何显示类的情况下对数据处理进行单元测试也更容易:您不需要表单来测试数据。最后:虽然还没有真正的数据,但您可以使用模拟数据来测试视图。
我们需要一个特殊的适配器类来将模型附加到视图:ViewModel 类。这三个类一起缩写为 MVVM。考虑阅读一些关于此的背景信息。
回到你的问题
假设您已从表单中删除数据,并将其放入单独的类中。这个类知道如何以及在哪里获取数据、如何更新数据、在哪里存储数据、如何添加新元素等。
您的应用程序需要显示问题。也许还有一些答案?此处未处理,但在您将模型与视图分离后,您可以添加答案,而不会干扰您当前的视图。
方法可能是这样的:
interface IQuestionBank
{
IEnumerable<Question> GetQuestions(...);
void AddQuestion(Question question);
}
具体的界面在这里并不重要,你似乎有章节和主题的概念,可能还有随机问题。调整您的界面,使您的表单可以获取必须显示的信息。
在您的表单中,您决定要在 DataGridView 中显示获取的数据。使用 Visual Studio 设计器,您添加了 DataGridView 和列。在构造函数中,您决定哪一列应该显示哪个 Question 属性:
public MyForm()
{
InitializeComponent();
// which column shows which property
this.columnQuestionId.DataPropertyName = nameof(Question.Id);
this.columnQuestionText.DataPropertyName = nameof(Question.Text);
this.columnQuestionDate.DataPropertyname = nameof(Question.Date);
...
}
// the Model is outside the form:
private IQuestionBank QuestionBank {get; } = ...
根据表单的其他几个属性(章节、主题?考官?学生姓名?),表单有一个程序可以从 QuestionBank 中获取必须显示的问题。
private IEnumerable<Question> FetchQuestionsToDisplay(...)
{
this.QuestionBank. ... // TODO implement out of scope of this question
}
要显示问题,您需要做的就是获取它们,并将它们添加到 DataGridView 的 DataSource 中
this.DataGridView1.DataSource = this.FetchQuestionsToDisplay(...).ToList();
虽然这会显示问题,但不会记录操作员所做的更改。如果您想允许操作员更改显示的信息、编辑某些单元格、添加新问题或删除问题,您需要将问题放在 BindingList 中:
private BindingList<Question> DisplayedQuestions
{
get => (BindingList<Question>)this.DataGridView1.DataSource;
set => this.DataGridView1.DataSource = value;
}
操作者编辑完问题,添加了一些问题,删除了几个问题后,他通过点击确定按钮表示他已经完成了编辑:
private void OnButtonOk_Clicked(object sender, ...)
{
// fetch the displayed questions:
ICollection<Question> displayedQuestions = this.DisplayedQuestions;
// Find out which Questions are changed and process them:
this.ProcessQuestions(displayedQuestions);
}
过了一会儿,显示了一些问题,您想显示更多问题。您的问题是原始问题已被删除。因为我们将 Model 从 View 中分离出来,所以很容易在 View 中添加更多 Questions。
private IEnumerable<Question> FetchAdditionalQuestions(...)
{
// fetch the additional questions that must be displayed.
// out of scope of this question
...
}
private void ShowAdditionalQuestions()
{
IEnumerable<Question> originalQuestions = this.DisplayedQuestions;
IEnumerable<Question> additionalQuestions = this.FetchAdditionalQuestions();
IEnumerable<Question> allQuestions = originalQuestions.Concate(additionalQuestions);
this.DisplayedQuestions = new BindingList<Question>(allQuestions.ToList());
}
我创建了一个新的 BindingList。或者,您可以将附加问题一一添加到具有原始问题的 BindingList 中。不确定哪种方法更好。
其他一些有用的方法
由于您将模型与视图分开,因此很容易获得选定的问题:
private Question CurrentQuestion => (Question)this.DataGridView1.CurrentRow.DataBoundItem;
private IEnumerable<Question> SelectedQuestions =>
this.DataGridView1.SelectedRows
.Cast<DataGridViewRow>()
.Select(row => row.DataBoundItem)
.Cast<Question>();