I need help with data binding. Imagine this situation. I have two classes, one named Isotope, another named Photon. Class Isotope contains BindingList Photons. I also have a static class StaticVariables, where I put BindingList Isotopes. Now, I want to make a form which will allow me to browse the list of isotopes. I created a combobox CBIsotopes, that I bound to StaticVariables.Isotopes:
CBIsotope.DataSource = StaticVariables.Isotopes;
CBIsotope.ValueMember = "IsotopeName";
CBIsotope.DisplayMember = "IsotopeName";
So far, everything works. Now I want to create a datagridview DGVPhotons that will show all the photons of the selected isotope. My first instinct was to do something along the way of
DGVPhotons.DataSource = StaticVariables.ListOfIsotopes.Photons
which of course, doesn't work. Another thing I tried is to use SelectedItem property of the ComboBox:
(1)
DGVPhotons.DataSource = (CBIsotope.SelectedItem as Isotope).Photons;
This works, but not as well as I would like. If I do it on load time, nothing happens, because ComboBox is empty. If I do it when an item is actually selected in ComboBox, then it works, but as I change the selection in ComboBox, DataGridView stays the same. The solution would be to put line (1) in SelectedIndexChanged of the ComboBox, but it seems like a brute force method to me, and I feel that my approach is fundamentally wrong... Is there some more elegant solution?
Ok, the key here is to use DataBind solution. After you change source of your element don't forget to use DataBind method after, in order to bind new data.
And also, on PageLoad event, don't forget to use IsPostBack sign in order to initialize page only when request is handled for the first time.
Related
I am struggling some time with checkbox control in WPF. What I am trying is to to make some kind of filter.
1. So first I need to fill the checkbox control with the database items
2. Second I need to check if anything is checked
3. Third if anything is checked I want to take those values and use them to search through the database
4. Should I use only checkbox control for this or should I put it in some container or a listBox?
I have find many topics here that are mentioning this stuff, but I couldn't manage to find the complete answer, only lot of parts that are not compatible with each other. I would really appreciate if someone explain to me how checkbox in WPF works.
If you have multiple values and you want to display multiple CheckBoxes, one for each of them, then you will need to use a ListBox and have the TemplateItem a CheckBox. This way your collection of items is bound to the ListBox, and for each item in the ListBox a CheckBox is shown.
So to get each of the values after there is an additional step if your doing MVVM and don't want to touch the UI. What you do is create a wrapper class that sits around your class and has a extra property for the IsChecked data. This way you can get the checked state without touching the UI.
I have an example of this on my blog:
Checked ListBox in WPF
To check which items are checked you just need a simple LINQ query. So if you use the example there from my blog then you should be able to do something like this:
var checkedCustomers = Customers.Where(w=>w.IsChecked).ToList();
I am experimenting with WPF and MVVM in our system. However iam having a problem with keeping things selected in lists using only MVVM ( without doing extra CollectionViews ).
What i currently have is the list
ObservableCollection<ReservationCustomerList> Customers;
And then a property storing the selected Customer
ReservationCustomerList SelectedCustomer;
In my opinion now, when the list reloads (actually from another thread async), the selection should be able to be kept, however this does not happen.
Does someone have a nice clean way of achieving this ?
The way we did it was that we did not replace the collection. We added/removed the entries and updated existing entries if required. This maintains the selection.
You can use LINQ methods like Except to identify items that are new or removed.
In case the reloaded list still contains the last selected item and you want that item to be selected, then you can raise the PropertyChange event for the property SelectedCustomer after your collection gets reloaded.
Please make your sure your viewmodel class implements INotifyPropertyChanged interface.
you can use the ICollectionView to select the entity you want.
ICollectionview view = (ICollectionView)CollectionViewSource.GetDefaultView(this.Customers);
view.MoveCurrentTo(SelectedCustomer);
in your Xaml the itemsControl must have IsSynchronizedWithCurrentItem=true
or if the ItemsControl has a SelectedItem property you can simply bind it to your SelectedCustomer Property.
When you "reload" your collection you basically replace all values in it with new values. Even those that look and feel identical are in fact new items. So how do you want to reference the same item in the list when it is gone? You could certainly use a hack where you determine the item that was selected by its properties and reselect it (i.e. do a LINQ search through the list and return the ID of the matching item, then reselect it). But that would certainly not be using best practices.
You should really only update your collection, that is remove invalid entried and add new entries. If you have a view connected to your collection all the sorting and selecting and whatnot will be done automagically behind the scenes again.
Edit:
var tmp = this.listBox1.SelectedValue;
this._customers.Clear();
this._customers.Add(item1); this._customers.Add(item2);
this._customers.Add(item3); this._customers.Add(item4);
this.listBox1.SelectedValue = tmp;
in the method that does the reset/clear works for me. I.e. that is the code I put into the event handling method called when pressing the refresh button in my sample app. That way you dont even need to keep references to the customer objects as long as you make sure that whatever your ID is is consistent. Other things I have tried, like overwriting the collections ´ClearItems()´ method and overwriting ´Equals()´ and ´GetHashCode()´ didn't work - as I expected.
I've been working with the DataGrid in WPF with great results. However, it is now giving me unexpected results after some changes.
BEFORE: I had a DataGrid on a page. The DataContext was set to a List object that was created from a class that existed within the same WPF project. The empty row at the bottom of the DataGrid, to add new records, is visible
AFTER: Same page, same DataGrid. But now the List object is coming from a Class Library project within the same solution. EXACT same code, but it's now been extracted into a class library. The empty row at the bottom of the datagrid, to add new records is not visible.
WTF?
I think I finally have the answer. Basically, I was mistaken, I did change a tiny portion of the class. The "lightbulb" went on when I read the answers to this one: How do I create a new row in WPF DataGrid when it is bound to an XmlDataProvider?
Bottom Line: The class you are binding to needs to have a default constructor in order to display an editable row!
In my code, I did change the constructors (I completely forgot about that) which left no default. Adding the default constructor back into the class fixed the problem.
Maybe it is some security issue or even a bug. I just read this:
I found that if you access the
CanAddRow of ListCollectionView once
before you use the collection,
magically the CanUserAddRows of the
DataGrid becomes true. Strange!
IEditableCollectionView ecv = new ListCollectionView(myRecordCache);
bool b = ecv.CanAddNew; // dummy access
MyGrid.DataContext = ecv;
What kind of list is it? Does its publically visible interface allow to add items or is it a readonly list now (e.g. IEnumerable, ICollection?
I encountered the same problem when I set the DataGrid property IsReadOnly="True". Check if you have the same setting and try to remove it to see what happens.
I have a grid (DevExpress XtraGrid, if that matters) which is bound to a LINQ to SQL Entity property.
gridItems.DataSource = purchaseOrder.PendingItemsGrouped;
Well, the grid is being displayed properly,and I can see the purchase items that are pending.
The problem arises when purchaseOrder.PendingItemsGrouped gets changed ... once that happens, the grid does not reflect the changes.
The exact procedure is as following:
The user selects a row from the grid, inserts a serial number on a specific textbox, and then hits enter effectively receiving this item from the purchase order, and inserting it into stock.
inventoryWorker.AddItemToStock( userSelectedItem, serialNumber );
The item gets properly inserted to the inventory, but the grid still shows the item as if it is still awaiting it to be received.
How do I solve this problem?
Do I really need to re-bind the grid so the changes can be reflected?
I even tried instead of:
gridItems.DataSource = ...;
This:
gridItems.DataBindings.Add( new Binding( "DataSource", purchase, "PendingItemsGrouped" ) );
But couldn't solve the problem.
Thank you very much for your time,
Isaac.
OBS:
Re-Binding the Grid works, but my question is ... is that even the proper way of doing things? I feel like I'm miles off the right track.
Calling databind is actually the right approach when you think about how databinding in webforms works. In all examples when databinding to objects, calls to databind happen whenever the collection is modified.
The reason it feels wrong is because its a lot cleaner to use a DataSourceControl, such as a LinqDataSourceControl or ObjectDataSourceControl, where all that stuff is handled for you.
Two things that might help you along this path is when using a LinqDataSourceControl, you might need to override the various -ing methods (Selecting, Inserting, Deleting) etc, in order to add additional filtering and logic.
The other thing that springs to mind is http://multitierlinqtosql.codeplex.com/. Especially the section on Custom ObjectDataSource.
I have not tried it myself but Bindable LINQ allows to achieve it.
I have a List collection where UserPropDef is a custom class with a few public properties. I want to display the contents of this list in a DataGrid. So I set the DataGrid.DataSource property of my grid to the list and it displays the contents just as I want.
Now if I add or remove items from the list I want the DataGrid to update. I can't use a BindingList because this list needs to be serialized. I've tried calling the Update() and Refresh() methods of both the DataGrid, and the form and nothing seems to cause the DataGrid to refresh based on the current contents of the collection.
The only thing that works seems to be setting the DataSource property again. Yet when debugging my code I can see that after the collection has changed the DataSource property of the DataGrid is in fact still referencing the correct and updated collection.
Is there a better way to cause the DataGrid to refresh based on it's current DataSource?
My suggestion would be take what is behind door number 3:
Create a custom Serializable List that implements IBindingList (Or something that inherits from BindingList and fixes the Serializable issues. Check out Fixing BindingList... for ideas).