To design this, we take the Student class defined earlier, and extract an interface. Here is the code:
interface IStudent
{
int Grade { get; set; }
Color GradeColor { get; }
string Name { get; set; }
}
public class Student : IStudent
{
// other code unchanged, following added:
internal new Student MemberwiseClone()
{
return new Student(this.name, this.grade);
}
}
We now add a new class, EditableStudent, that keeps one copy of the student data as current, and keeps as backup copy as necessary. It also has flag indicating if an edit is in process.
public class EditableStudent : IStudent, IEditableObject, INotifyPropertyChanged
{
#region Basics
Student m_currentData;
Student m_backupData;
bool m_inTx = false;
public EditableStudent()
{
m_currentData = new Student();
m_backupData = null;
}
public EditableStudent(string name, int grade)
{
m_currentData = new Student(name, grade);
m_backupData = null;
}
public override string ToString()
{
return m_currentData.ToString();
}
#endregion
#region IEditableObject Members
public void BeginEdit()
{
if (!m_inTx)
{
m_backupData = m_currentData.MemberwiseClone();
m_inTx = true;
}
}
public void CancelEdit()
{
if (m_inTx)
{
m_currentData = m_backupData;
m_inTx = false;
}
}
public void EndEdit()
{
if (m_inTx)
{
m_backupData = null;
m_inTx = false;
}
}
#endregion
/// ...
}
public class EditableStudent : IStudent, IEditableObject, INotifyPropertyChanged
{
// ...
#region IStudent Members
public int Grade
{
get
{
return this.m_currentData.Grade;
}
set
{
if (this.m_currentData.Grade != value)
{
this.m_currentData.Grade = value;
Notify("Grade");
}
}
}
public System.Drawing.Color GradeColor
{
get { return this.m_currentData.GradeColor; }
}
public string Name
{
get
{
return this.m_currentData.Name;
}
set
{
if (this.m_currentData.Name != value)
{
this.m_currentData.Name = value;
Notify("Name");
}
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void Notify(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
#endregion
}
Under the surface, the following code is being generated here and there:
private System.Windows.Forms.BindingSource editableStudentBindingSource;
this.tbName.DataBindings
.Add(new System.Windows.Forms.Binding("Text",
this.editableStudentBindingSource,
"Name",
true));
this.tbGrade.DataBindings
.Add(new System.Windows.Forms.Binding("BackColor",
this.editableStudentBindingSource,
"GradeColor",
true));
this.tbGrade.DataBindings
.Add(new System.Windows.Forms.Binding("Text",
this.editableStudentBindingSource,
"Grade",
true));
this.editableStudentBindingSource.DataSource
= typeof(BasicBindingSource.EditableStudent);
In our form code, we will add a load event to initialize the list.
private void Form1_Load(object sender, EventArgs e)
{
editableStudentBindingSource.Add(new EditableStudent("Fred", 95));
editableStudentBindingSource.Add(new EditableStudent("Ted", 80));
editableStudentBindingSource.Add(new EditableStudent("Ed", 65)); }
Add two buttons, btnNext and btnPrev. The following click handlers causes the current position to change.
private void btnNext_Click(object sender, EventArgs e)
{
editableStudentBindingSource.Position++;
}
private void btnPrev_Click(object sender, EventArgs e)
{
editableStudentBindingSource.Position--;
}
Add three buttons, btnAdd, btnSave, btnCancel.
private void btnAdd_Click(object sender, EventArgs e)
{
editableStudentBindingSource.AddNew();
}
private void btnSave_Click(object sender, EventArgs e)
{
editableStudentBindingSource.EndEdit();
}
private void btnCancel_Click(object sender, EventArgs e)
{
editableStudentBindingSource.CancelEdit();
}
Add a btnReport with the handler shown below. This shows the use of the Count, Position and Current properties of a BindingSource.
private void btnReport_Click(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Number of records = {0}", editableStudentBindingSource.Count);
sb.AppendLine();
sb.AppendFormat("Current record is #{0}: {1}",
editableStudentBindingSource.Position,
(EditableStudent)editableStudentBindingSource.Current);
sb.AppendLine();
foreach (EditableStudent s in editableStudentBindingSource)
sb.AppendLine(s.ToString());
MessageBox.Show(sb.ToString(), "Current status of students");
}
Enjoy.