I have the following two classes generated by Entity Framework..
public partial class Datagrid
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public Nullable<bool> IsChecked { get; set; }
}
public partial class SampleDbContext : DbContext
{
public SampleDbContext()
: base("name=SampleDbContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Datagrid> Datagrids { get; set; }
}
i have a WPF application where I have a list view containing checkboxs for each column.. I have written the to logic to find what are the checkboxes that are checked in the list view.. But when I iterate through the checked boxes and try to save to the database, database is not updating please help..
dg is object of type Datagrid..
db is object of type SampleDbContext
foreach (CheckBox cb in myBoxes)
{
dg.IsChecked = cb.IsChecked; //I want to update each column with the checked value in the listview. cb.Ischecked works perfectly..It says true for the checkboxes that are checked else false
db.SaveChanges(); //NOT updating the database
}
Your variable 'dg' will need to be part of the databaseContext's ('db' in your code) DataGrid's Property. An example:
foreach (CheckBox cb in myBoxes)
{
dg.IsChecked = cb.IsChecked;
db.DataGrids.Add(dg);
db.SaveChanges();
}
.SaveChanges() will only flush items assigned to the DataGrid property to the database.
Based on your comments, I'm not entirely clear on if the checkboxes need to be created each time or just updated. If created, it will be somewhat like the other answer. However, you were likely getting the System.Data.Entity.Validation.DbEntityValidationException due to attempting to add a new Datagrid without providing a Name and Location, which are not nullable and are not (presumably) automatically populated like Id:
foreach (CheckBox cb in myBoxes)
{
Datagrid dg = new Datagrid
{
Name = "NameGoesHere"; // Maybe you're wanting to use cb.Name or cb.Text?
Location = "Location Here";
IsChecked = cb.IsChecked;
};
db.DataGrids.Add(dg);
db.SaveChanges();
}
If you're just updating instead, I'm still not sure where you're setting dg, but perhaps you might want to look it up from :
foreach (CheckBox cb in myBoxes)
{
Datagrid dg = db.DataGrids.Where(g => g.Name == cb.Name).SingleOrDefault();
// etc.
Edit based on comments:
If you're doing two-way binding and have directly bound CheckBoxes to the IsChecked values of Datagrid entities, you shouldn't need to do anything other than simply call db.SaveChanges(); when you're ready to save. The bound Datagrid entities will be updated on their own.
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 have an issue trying to get a Checkbox working with ObjectListview.
My model looks like this:
public class object
{
public string name {get; set;}
public int age {get; set;}
public bool inuse {get; set;}
}
And I added a FastObjectListView via the Designer in Visual Studio to a Win Forms Application.
Then, I added the Columns and set the AspectName for each column to the Models Property (First column: AspectName: name, Second Column: AspectName: age, Third Column: AspectName: inuse).
Afterwards, I filled the ListView with this:
using (var context = new objectDb())
{
var objectlist = context.objects.ToList();
fastoLV_Clean.SetObjects(objectlist);
fastoLV_Clean.Refresh();
}
That works, and I can see my Database entries in the ListView.
Now I want to add a CheckBox column where someone can check or uncheck the items to delete them and I can not get the checkbox to work.
I have added a Column and set CheckBox to true, changed the CheckedAspectName of the ListView and now I can see the Checkboxes but nothing happens if I click them to check.
I think I'm on the completely wrong track, what do I have to do to make it work?
Thank you very much!!
I don't know a way with the ObjectListView to include any items which are not part of your model.
So then the simple way is to change your model to include a "Delete" property which you can then show in your ObjectListView.
Of course, this is not always possible! Especially if you are dealing with items that are written to/from Database or another persistence layer.
Then the trick is to write a derived class with you model being the base class and then you just add the delete column to this. But then you would need to convert from your Base to a derived class before showing in the ObjectListView.
The following code can help with that.
You keep your columns set-up as you have already done.
Assuming your (now base) class is defined like this
public class MyClass
{
public string name { get; set; }
public int age { get; set; }
public bool inuse { get; set; }
}
Your derived class inherits from this, adds the delete property and a new constructor
public class MySecondClass : MyClass
{
public bool Delete { get; set; }
public MySecondClass(MyClass other)
{
//Copy from MyClass
this.name = other.name;
this.age = other.age;
this.inuse = other.inuse;
//Set default for new properties
this.Delete = false;
}
}
Your code to retrieve the objects and set them then looks like this
using (var context = new objectDb())
{
var objectlist = context.objects.ToList();
//Now we need to convert to the derived class type
var secondlist = list.ConvertAll(x => new MySecondClass(x));
//Then we setobjects using this new list
fastoLV_Clean.SetObjects(secondlist);
}
I have a small caliburn micro MVVM project with a DataGrid. The columns will consist of x amount of 'setup's and the rows will consist of 'CustomRow'. I would like to use a ObservableCollection where CustomRow has a function property and a collection of setup property. For each setup in this collection a column should exist with the value of setup.
class CustomRow
{
public string Function { get; set; }
public ObservableCollection<Setup> Setups { get; set; }
}
// example class
class Setup
{
public string Name { get; set; }
public object Content { get; set; }
}
So I need to be able to add columns and rows dynamically depending on the itemssource (all the Setups collections will have the same size).
My problem is that I don't know how to translate the Setups property into multiple columns.
I have spend quit some time on what should be a mundane problem in my opinion. But I am missing something.
Any help is much appreciated.
Bind or set the ItemsSource to the ObservableCollection<CustomRow> and then get the Setups property of the first CustomRow in the source collection in the view, e.g.:
private void Window_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var vm = DataContext as YourViewModel;
dataGrid.Columns.Clear();
if (vm.Rows != null && vm.Rows.Count > 0)
{
var setups = vm.Rows[0].Setups;
foreach (var setup in setups)
{
dataGrid.Columns.Add(new DataGridTextColumn { Header = setup.Name, Binding = new Binding("Content ") });
}
}
dataGrid.ItemsSource = vm.Rows;
}
There is no way to bind the Columns property of the DataGrid directly to a source property so you need to create the columns yourself one way or another.
You should do this in the view, in the control or in an attached behaviour that is attached to either of them. A view model should not create any columns.
The DataSource of my DataGridView is a BindingList<Filter> which contains an enum. The gridview contains only two columns: a regular textbox column for a string, and a combobox (drop down) column for the enum. If I bind the combobox column to the enum variable of my object I get an error. Here is the code for my objects:
public class FilterProfile
{
public FilterProfile()
{
filters = new BindingList<Filter>(); // the list that gets bound to gridview
}
public string name { get; set; }
public BindingList<Filter> filters { get; set; }
}
public class Filter
{
public string keyword { get; set; }
public FilterType type { get; set; } // the enum in question
}
public enum FilterType : int
{
SESSION = 1,
ORDER = 2,
SHIPMENT = 3
}
I have a form where the user selects a FilterProfile from a dropdown menu and then I find the appropriate FilterProfile from a global list and bind it:
foreach (PlvFilterProfile filterProfile in _filterProfiles)
{
// find the correct filter profile
if (filterProfile.name.Equals(lstFilterProfiles.Text))
{
// bind it
grdFilters.DataSource = filterProfile.filters;
break;
}
}
In order for the changes made in the DataGridView to be reflected in filterProfile.filters I need to set the DataPropertyName attribute of both columns to their respective variable (either keyword or type). This works correctly for the keyword string, but not with the type enum.
If I keep the line colFilterType.DataPropertyName = "type"; I get the error below whenever a new row is created or whenever I put my mouse over the dropdown. If I get rid of it, the type of every newly created Filter is set to 0 and never updated.
I'm not sure what causes the DataError event so don't know how to handle it or where to breakpoint.
The problem is when you focus on the new row (prepare to add a new row), a new object is required in the underlying list, this object is default by null, that value is bound to the new row and of course the ComboBoxCell can't accept that null value, causing the exception as you encountered. The solution is very simple indeed, we just need to handle the event AddingNew of the BindingList, set the default new object in there to a valid value and then it works just fine:
public FilterProfile()
{
filters = new BindingList<Filter>(); // the list that gets bound to gridview
filters.AddingNew += (s,e) => {
//the default value of FilterType is up to you.
e.NewObject = new Filter {type = FilterType.SESSION };
};
}
I have a windows form application with two Combo boxes (Dropdownlist style). The first combo box gets the data from a List of AppTable object, like so:
foreach (AppTable table in appTableList)
cbxSelectName.Items.Add(table.App);
I have set up a trigger for when the selected item in this dropdown is changed:
this.cbxSelectName.SelectedIndexChanged +=new EventHandler(cbxSelectName_SelectedIndexChanged);
And finally, here is the definition of the method the trigger calls. Please note, the value in tbxNewWikiWord textbox changes as the selected item is changed. However, the same does not happen in the second dropdown list ( cbxUpdateAppType):
private void cbxSelectName_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (AppTable table in appTableList)
{
if (table.App == cbxSelectName.SelectedItem.ToString())
{
this.tbxNewWikiWord.Text = table.WikiWord;
this.cbxUpdateAppType.SelectedItem = table.Type;
break;
}
}
}
This is how AppTable looks:
class AppTable
{
public string App { get; set; }
public string Type { get; set; }
public string WikiWord { get; set; }
}
Am I missing something?
Adding values to cbxUpdateAppType from the AppTable object fixed it. I'm not sure why since in either case, I was adding Strings.
I just needed to check for duplicates so I don't end up with multiple instances of the same value in my dropdown box.
foreach (AppTable table in appTableList)
{
if (!cbxUpdateAppType.Items.Contains(table.Type))
cbxUpdateAppType.Items.Add(table.Type);
}