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.
Related
Okay so I hope it's okay if I don't post my code since it contains private parts.
I will describe it as good as I can. So I did an observablecollection without onpropetychange and a databind to the grid. Everything works well if I'm adding something to the collection, the datagrid updates. But if I get the observablecollection from the xml save file, it doesn't update.
So far I checked if the observablecollection loads everything (it does) and tried to update the datagrid manual (nothing). I'm glad if someone could give me advice without seeing the code. :)
If you are changing the whole collection, the UI can not recognize the change since you are not using the INotifyPropertyChanged. You have two options:
Implement the INotifyPropertyChanged and raise the event after setting a new collection as source.
Clear the old collection, and fill in the new items to the old collection.
My best guess is that you should set the DataGrid's ItemsSource property again after loading the ObservableCollection.
Use this:
datagrid.ItemsSource = null;
datagrid.ItemsSource = yourObservableCollection;
I am working on a winforms app with a DataGridView control on it, and I am experiencing some frustrating things.
First off, I want to turn off AutoColumnGeneration, but it's not listed in the properties. No problem, I can do that with a line of code...and this is where it gets weird:
In my code, the DataGridView is inaccessible. Its like it doesnt exist on the form. Looking into this, its because the designer is declaring it as part of the InitializeComponent() method instead of where it initializes all the other controls.
Because its in the designer, any change I make there gets reversed so I can't fix this.
Is there any way to stop visual studio from doing this? I found a hack around it by using one of the datagrid columns (which ARE accessible) to create a reference to the datagridview its associated with and access it that way. It works, but its ugly and not intuitive at all.
I think I found it:
In the designer, click on the DataGridView control, and change the property of GenerateMember to true. I'm guessing it is set to false.
That property is used to do just that: hide the control from the code windows. It's useful for Labels or ToolStripSeparators that you don't need to deal with in code.
I personally use the binding source as the datasource which can even be an object and then under columns it will list all of the available columns and you can pick and choose which ones are visible as well as a slew of other options including formatting.
Click the drop down on the datasource and Add a new data source and select the necessary object, in my case an order detail object. Here is some of my designer code which is created by VS2010
this.dgvOrderDetails.DataSource = this.orderDetailBindingSource;
this.orderDetailBindingSource.DataSource = typeof(OrderDetail);
And the binding source code that sets up the data to fill the datagridview (I coded this part)
orderDetailBindingSource.DataSource = orderDetList;
Then just click the ellipses on the Columns property of the datagridview and it will have all the columns listed that are available from the object and I set the ones I want visible, the order, format etc.
As for the 2nd issue I don't think you'll have that problem once you use the designer to make the changes I listed above.
In my case, I declared a private property in the Form's partial class (the file for my code, not the Designer's file) to return the control by navigating through the Controls hierarchy.
private DataGridView MyGrid
{
get { return (DataGridView)this.Controls[0].Controls[1].Controls[0].Controls[1].Controls[0]; }
}
I agree, there ought to be a better way, such as Visual Studio Designer declaring the control like it does most other controls on the form. In the meantime, this works.
Warning!
If the form's control hierarchy is ever changed, the property's definition will have to be manually updated.
Edit
I have a better solution - at least in Visual Studio 2012.
While in the form Designer, with the DataGridView selected, open its properties and look for the GenerateMember property (under the Design node) and ensure it is set to True. Once set to True, the Designer will declare a member variable for the DataGridView control.
The strange thing is that the default value appears to be True, so I'm curious how it was changed to False? Perhaps I mis-clicked when setting the name?
By the way #LarsTech's answer is the same as this update.
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.
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 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).