I have some objects which are loaded from an SQLite database into a separate list. The user can select an item from that list via combobox and edit its data in a subform. I work with Binding
so that the list and its item is immediately updated on every change. Furthermore, I have a LastModified field (DateTime
) in order to see the time of the last change which is set via my item SaveToDB()
method.
Now, I am wondering how to handle the database update as well as the display of updated bound values correctly and have 3 open questions:
How am I able to store the old item values in order to compare those with the final edit (after leaving the edit control, like a
TextBox
) without reading the database entry again? As all bound variables are immediately changed with the edit I think I would need a copied item object when the object is loaded and saved inbetween but which is not bound itself. How can I create such a copy?What is the best event to fire the comparison and database update? I think it is
Validated()
but I am not sure. It definitely is not theChanged()
event as here a database update would be triggered after every keystroke, even when the changes are undone again before leaving the edit control (for which I want to use the comparison in point 1 to get rid of unnecessary database updates).Why is my bound
DateTime
label not updated when I call the entitySaveToDB()
method? Do you need to rebind every control after each code internal change of object properties? Seriously? I find it also a bit stupid to explicitely update the combobox.Text while the updated data is correctly displayed when I dropdown the combobox. Really strange behaviour.
Code:
public class Entity {
public int ID { get; set; }
public string DateTime { get; set; }
public string Name { get; set; }
...
public void SaveToDB() {
DateTime = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
if (DB.Read(QueryRead()) == null) {
DB.Write(QueryAdd());
} else {
DB.Write(QueryUpdate());
}
}
}
public class Entities : SortableBindingList<Entity> {
...
}
public class EntityStore {
public Entities Entities;
public BindingSource Source; // used for combobox.datasource in selector-form
// singleton
...
public EntityStore() {
Entities = new Entities();
Source = new BindingSource() { DataSource = Entities };
ReadAllFromDB(); // fills Entities
}
...
public void Update(Entity entity) {
entity.SaveToDB();
}
}
public partial class FormSelector : Form {
// FormEntity gets the selected entity via its parent form:
FormEntity formEntity;
...
cbxEntity.DataSource = EntityStore.Current.Source;
formEntity.SetEntity(EntityStore.Current.Entities.FirstOrDefault(x => x.ID == ((Entity)((GridViewRowInfo)(cbxEntity.SelectedItem)).DataBoundItem).ID));
...
}
public partial class FormEntity : Form {
Entity entity;
...
public void SetEntity(Entity entity) {
this.entity = entity;
tbxID.DataBindings.Add(new Binding("Text", entity, "ID", false));
lblDateTime.DataBindings.Add(new Binding("Text", entity, "DateTime", false));
tbxName.DataBindings.Add(new Binding("Text", entity, "Name", false));
}
private void tbxName_Validated(object sender, EventArgs e) {
// missing comparison logic
// ...
// I could also directly call entity.SaveToDB(); here but I would like
// to keep the logic flow separated as follows:
// DB <- Entity <- EntityStore <- Form(s)
EntityStore.Current.Update(entity);
}
}
Sidenote/Question: I also stumbled over the INotifyPropertyChanged event but I do not understand if its used in winforms and for what purpose.
To prevent a stack overflow exception by implementing INotifyPropertyChanged you additionally need to use private properties when getting and setting public properties.