This has been bugging me for a while now, what I would like to be able to do is update an existing record on the database by making a change to the ObservableCollection or the record class SquirrelDataGridActiveView.
This is my DataAccessService:
public interface IDataAccessService
{
ObservableCollection<SquirrelDataGridActiveView> GetEmployees();
void UpdateRecord(SquirrelDataGridActiveView Emp);
int CreateEmployee(SquirrelDataGridActiveView Emp);
}
/// <summary>
/// Class implementing IDataAccessService interface and implementing
/// its methods by making call to the Entities using CompanyEntities object
/// </summary>
public class DataAccessService : IDataAccessService
{
Drive_SHEntities context;
public DataAccessService()
{
context = new Drive_SHEntities();
}
public ObservableCollection<SquirrelDataGridActiveView> GetEmployees()
{
ObservableCollection<SquirrelDataGridActiveView> Employees = new ObservableCollection<SquirrelDataGridActiveView>();
foreach (var item in context.SquirrelDataGridActiveViews)
{
Employees.Add(item);
}
return Employees;
}
public int CreateEmployee(SquirrelDataGridActiveView Emp)
{
context.SquirrelDataGridActiveViews.Add(Emp);
context.SaveChanges();
return Emp.ID;
}
public void UpdateRecord(SquirrelDataGridActiveView temp)
{
}
}
As you can see there is already a GetEmployees() method and a CreateEmployee() method however I'm finiding it very difficult to update the database with the new values.
Any suggestions would be much appreciated.
Looks like the problem maybe be in your EF layer. Do you have any unit tests to check that the creation of a new Employee record works properly? Can you break into the CreateEmployee and inspect the Emp.ID field? See if it's set to something. If it is, the creation of the new record works properly. One observation, if I may: your data access service does not need to return an ObservableCollection. I assume you already have an observable collection in your view-model class. If you want to keep alive the binding between the data grid in the view and the observable collection property in the view-model class, you should not reassign the OC property of the view-model class. The pattern you should follow is this:
return an IEnumeration from your data access service's GetEmployees() method;
Optional, but recommended is to make GetEmployees awaitable;
in your view-model class, clear the content of the OC property and then add, one by one, all the items in the IEnumeration collection returned by your GetEmployees() method. This way your binding will continue to work and any updates in the database will be reflected in the view's data grid.
You can keep the OC return from your GetEmployees, but in your view- model you still need to transfer all the items, one by one, into the property that is, most likely, bound to the data grid in the view. If you just assign the GetEmployees() returned OC list to the the view-model's property, your binding will be gone.
Perhaps your data grid does not refresh properly and that's why you conclude that the database is not updated when in fact it is.
HTH,
Eddie
Related
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 the Changed() 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 entity SaveToDB() 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.
I want to store a temporary version of my Company model within WPF MVVM however I am having issues where even though the temp I create isn't bound to my UI elements, it is still being updated.
Here is what happens when the ModifyCompanyViewModel is instantiated:
public ModifyCompanyViewModel(Company passedCompany)
{
SelectedCompany = passedCompany;
_tempCompany = passedCompany;
CloseWindowCommand = new CloseableCommand<Window>(CloseWindow);
}
So I have a readonly Company named _tempCompany. The UI elements are bound like so:
<TextBox Grid.Row="1" Grid.Column="1" x:Name="NameTextBox" Text="{Binding SelectedCompany.Name, Mode=TwoWay}"/>
Clearly they're bound to the SelectedCompany. If I then type something different in the TextBox that contains the Company's Name, but return the _tempCompany the _tempCompany's name reflects that which I have typed.
How can I store the _tempCompany in a way that no matter what is typed it's name stays the same? I have started using this approach which works:
_tempCompany = new Company
{
Id = passedCompany.Id,
Name = passedCompany.Name
//Other properties..
};
But this seems very cumbersome and that I am overlooking an easier way.
The SelectedCompany property and the _tempCompany field reference the same Company object. If you want to store "a temporary version" of the Company object you need to create a temporary version, i.e. you need to create another instance of the Company class like you are currently doing:
_tempCompany = new Company
{
Id = passedCompany.Id,
Name = passedCompany.Name
//Other properties..
};
This is not cumbersome.
As suggested in the comments you could implement the ICloneable interface but this just moves the creation of the other instance to a method within the class that implements the interface. You still need to create another instance somewhere.
How can I implement ICloneable when the model is generated by EF?
Create a partial class and implement the Clone method in this one.
You have to create a viewmodel. Currently Company is a model. Attempting to use it as viewmodel (to bind to its properties) sooner or later will cause you problem, since you mention it's generated.
Consider a simple viewmodel wrapping Company:
public class CompanyViewModel: INotifyPropertyChanged
{
readonly Company _company;
public CompanyViewModel(Company company)
{
_company = company;
}
// now expose something
public string Address
{
get { return _company.Address }
set
{
// tracking changes
// note: you aren't tracking changes made to model instance!
_company.Address = value;
OnPropertyChanged();
}
}
// and here is what you actually want, read-only name
public string Name => _company.Name;
// you can optinally expose model and bind to it properties
// but that wouldn't let you track the changes
// unless model implements INotifyPropertyChanged
public Company Company => _company;
...
}
If you want to edit company name, then just make another property (call it NewName), set its initial value in constructor and decide for yourself when its value will replace _company.Name (e.g. in some method AcceptChanges() which will be called when user finish editing). You will be able to access both: NewName and not yet changed _company.Name to compare them and display confirmation button.
I know this is very similar to other questions that have been asked, and I have looked at them but in one way or another the solutions don't work for this specific scenario.
I have a collection of objects in a BindingList:
private BindingList<PathologyModel> _patientPathologies;
public BindingList<PathologyModel> PatientPathologies { get { return _patientPathologies; } }
This collection needs to be bound to different XamDataGrid controls (Infragistics' version of a DataGrid). I need to use BindingList because I need to know when a user edits a particular item in the collection, which other types of collections don't seem to support.
I need the collection to be sorted, so for now I just have to do this every time an item is added/removed from the list:
private void Pathologies_ListChanged(object sender, ListChangedEventArgs e)
{
if (e.ListChangedType == ListChangedType.ItemAdded || e.ListChangedType == ListChangedType.ItemDeleted)
{
_pathologies = new BindingList<PathologyModel>(_pathologies.OrderBy(x => x.Tooth.ToToothNumber()).ThenBy(x => x.DiagnosisDate).ToList());
}
}
It would be nice if this could be done automatically without the extra copying. But that's not my biggest problem right now.
I need the different XamDataGrids to have different filtered views of this same collection, which currently I am achieving with this:
public ICollectionView AllPathologies { get { return CollectionViewSource.GetDefaultView(PatientPathologies); } }
public ICollectionView TodaysPathologies { get { return CollectionViewSource.GetDefaultView(PatientPathologies.Where(x => x.DiagnosisDate.Date == DateTime.Today.Date)); } }
This almost works... I say almost because the views are showing the correct data, but the final requirement is that I also need to track the CurrentItemChanged event, so that I can enable/disable certain operations depending on which record the user is on. This works fine with the AllPathologies view, but does not ever get raised with TodaysPathologies, I'm guessing because it gets a different copy of the collection source every time you access the property? Strangely enough, the ListItem_Changed event still works properly against the original collection source.
I have tried making private CollectionViewSource objects to back the ICollectionView properties as I've seen in other articles, such as this:
private CollectionViewSource _todaysPathologies;
public ICollectionView TodaysPathologies { get { return _todaysPathologies.View; } }
...
_todaysPathologies = new CollectionViewSource{Source= _patientPathologies}.View;
But since the source is a BindingList I can't apply a filter predicate:
TodaysPathologies.CanFilter <--- false
So now I'm stuck. I place my fate in your hands, dear StackOverflowers.
"I need to know when a user edits a particular item in the collection, which other types of collections don't seem to support."
This isn't entirely true. A Collection used within a Class that inherits from INotifyPropertyChanged would solve this.
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx
I would recommend making an inner class in your window page. With a Property for each value and the editable properties would call the NotifyPropertyChanged event. You would then have a collection of these inner class objects. The inner class would represent a row in the grid.
Another way I've solved this before is to specify text column where users enter information:
In XAML:
<DataGridTextColumn //... Binding="{Binding Path=Value, Mode=TwoWay}">
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox"/>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
In code:
private string value;
public string Value
{
get
{ return value; }
set
{
this.value = value;
NotifyPropertyChanged("Value");
}
}
The inner class contains this Value Property as well as a reference to an object of the class I used to have a collection of.
Now I have a collection of objects of this inner class, which stores both. I use this to display info of the class and have an extra column to add a value. I then make a new object out of the old class information + this value.
If you've found this helpful and would like me to go into more detail, let me know. I would be more than happy to. That or someone will post :P
I have a form, which shows three items in a combo box.
Continents, Countries and Cities
If I select an item, e.g. if I select Cities and then if I click on the "Get Results" button, I send a select command to the database via business and data layer which then retrieves a list of type Cities.
The List are then bound to the grid on the UI form.
The classes: Continents, Countries and Cities implement IEntities interface with property string "Name".
The button click event calls Business layer using:
click(object sender, EventArgs e)
{
string selectedItem = comboBox.SelectedItem;
IEntities entity = null;
List<IEntities> list = null;
if (selectedItem == "Cities")
{
entity = new Cities("City");
}
if (selectedItem == "Continents")
{
entity = new Continents("Continents");
}
if (selectedItem == "Countries")
{
entity = new Countries("Countries");
}
//Then I call a method in Business Layer to return list
BL bl = new BL(entity);
list = bl.GetItems();
myDataGrid.DataContext = list;//to bind grid to the list
}
Business Layer looks like this:
public class BL
{
public IEntities _entity;
//constructor sets the variable
public BL(IEntity entity)
{
_entity = entity;
}
public IList<Entities> GetItems()
{
//call a method in data layer that communicates to the database
DL dl = new DL();
return dl.CreateItemsFromDatabase(_entity.Name);//name decides which method to call
}
}
I want to use Unity as the IOC so instead of using factory (sort of) pattern in the button click event with if then elses and using hardcoded class names, I want to use the container's configration that creates the relevant class instance. And when the IEntities instance is passed to the constructor of the BL class, I want to pass the object using Unity. Can you please advice how to do it?
As it exists, this design is not well suited to incorporating an IoC container.
As long as your ComboBox still contains strings, you're going to have to compare that against hardcoded values in a switch statement or set of if blocks somewhere.
Furthermore, the BL class takes a constructor parameter of type IEntity, but that can be an object of any among many different types at runtime. There is no way to configure Unity at startup to instantiate BL without also telling it what to use as that parameter (and nothing to gain by it, really).
Interestingly, though, you seem to be instantiating these Entity objects for the sole purpose of passing their string name to the CreateItemsFromDatabase method; you're not using its type for anything. It seems that you can skip the constructor parameter altogether and simply pass the selected string from the ComboBox directly to the GetItems method and achieve the same result. If you have some other reason for doing this, you should at least not supply the name in the constructor; make it a const within each class declaration.
What might be better suited is to make GetItems a generic method. Instead of passing an IEntity to the BL constructor, you would pass the concrete type to the method:
var bl = new BL();
var countries = bl.GetItems<Countries>();
var cities = bl.GetItems<Cities>();
var continents = bl.GetItems<Continents>();
I've defined the following view:
<CollectionViewSource x:Key="PatientsView" Source="{Binding Source={x:Static Application.Current}, Path=Patients}"/>
Where Patient is the following property:
public IEnumerable<Patient> Patients
{
get
{
return from patient in Database.Patients
orderby patient.Lastname
select patient;
}
}
Somewhere in my code, I change the Patients database, and I want to have the controls that display this data (using the "PatientsView") to be automatically notified. What's a proper way to do this?
Can the CollectionViewSource be invalidated or something?
How to invalidate a CollectionViewSource in code behind:
CollectionViewSource patientsView = FindResource("PatientsView") as CollectionViewSource;
patientsView.View.Refresh();
I think this is a bit more complex than it seems. Notifying your client application about changes in database is a non-trivial task. But your life is easier if the database is changed only from your application - this makes you able to put "refreshing logic" whenever you change the database.
Your "Patients" property seems to be present in one class (maybe a little more than one? :) ). And you probably bind some ListBox to the CollectionViewSource. So instead of calling Refresh on the CollectionViewSource you can make WPF re-call the getter. For this the class that has Patients property has to implement INotifyPropertyChanged interface.
The code would look like this:
public class TheClass : INotifyPropertyChanged
{
public IEnumerable<Patient> Patients
{
get
{
return from patient in Database.Patients
orderby patient.Lastname
select patient;
}
}
#region INotifyPropertyChanged members
// Generated code here
#endregion
public void PatientsUpdated()
{
if (PropertyChanged != null)
PropertyChanged(this, "Patients");
}
}
Now, call PatientsUpdated() on an instance of TheClass to trigger update of the binding.
P.S. Having said all that it just feels like a bad design somehow.
Table<T> does not support IListChanged events, you will have to do this yourself (I had to do the same earlier today).