So the basics: I've got a window with a ListView on it, which is populated by my grid's datacontext:
mainGrid.SetBinding(Grid.DataContextProperty,
new Binding() {
Source = new DataView()
{ Table = SQLHandler.GetHandler[classType.ToString()] }
}
);
in xaml:
<ListView Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding}">
everything works fine, it's populated. As you can see above, i've got an SQLHandler class which can be accessed by Singleton, and I can access my tables with an indexer.
The problem: window loads up, i'm selecting a row, clicking the Edit button, new window loads up, where i get the selected row's details. when i delete this row via this new window and close it, the main window (where the complete datatable is shown) is not updated accordingly. i know what the solution should be, but I can't make it work. (inotifyproperty changed interface to SqlHandler class, Binding.IndexerName etc..)
here is the main thing: the dataset is not in my SqlHandler class, it's in SqlExecuter, where all of my sqlcommands are being executed.
public override DataTable this[string key]
{
get
{
if (sqlExecuter.GetDataSet.Tables.Contains(key))
return sqlExecuter.GetDataSet.Tables[key];
throw new KeyNotFoundException("The specified key was not found");
}
}
where GetDataSet is:
public DataSet GetDataSet
{
get { return ds; }
}
How can I make this work? When I delete a row in a different window and close that one, the mainwindow's listview doesn't update itself.
The only option I have is to put a refresh button up, and then rebind the datacontext property, then of course it's working, but my goal is to have a 'live' update system, that's what Binding is for after all.
What I've tried: GetDataSet in SqlExecuter: implemented the inotifypropertychanged interface, but nothing changed. and i can't have inotifypropertychanged implemented on my indexer in SqlHandler, because it doesn't have a setter, I'm always just accessing the tables from code-behind, my sqldataadapter is populating them (Fill method)
p.s: i don't really plan on creating an ObservableCollection, because 90% of my code should be rewritten and when i delete a row, i clear my dataset and fill it up again, so I'm not even expecting it to notice every change, just when i refill my datatable, my listview should know about it.. and refresh itself
I think using a second window as a popup might be the problem here. If you were doing the editing on the same page then you could use a simple
ListView1.DataBind()
To refresh the contents of the list at the end of the delete command. Or you could use the IsPostBack method to update the list if the page was bring redrawn rather than reloaded.
You could try calling the page name and then the list view from your other window but I'm not sure if you can perform that kind of command from another window.
Alternatively you could do the editing on a different page, rather than a separate window so when you return to the original page the listview is being redrawn.
Like yourself I'm sure there is a simpler solution to your problem, but unfortunately I don't know it.
You may need to set the binding mode to two way:
Mode = BindingMode.TwoWay
WPF for some non-obvious reasons, defaults to one way binding. Hope that helps. I'm not particularly well-informed, but I've seen this issue with simple data bindings to ObservableCollections, and the TwoWay binding fixes it.
Related
I have a DataGrid, which loads the data on start up. There are several buttons on which the user can click. Each button updates the same column. The problem is that when the new value of that column is saved, the old value is still shown in the data grid. It must be refreshed. I have tried several ways to do it, like: t_KlantenDataGrid.Items.Refresh() and CollectionViewSource.GetDefaultView(t_KlantenDataGrid.ItemsSource).Refresh(). None of them works.
The code which loads the data:
OV.AOVDataSet aOVDataSet = ((AOV.AOVDataSet)(this.FindResource("aOVDataSet")));
// Load data into the table t_Klanten. You can modify this code as needed.
AOV.AOVDataSetTableAdapters.t_KlantenTableAdapter aOVDataSett_KlantenTableAdapter = new AOV.AOVDataSetTableAdapters.t_KlantenTableAdapter();
aOVDataSett_KlantenTableAdapter.Fill(aOVDataSet.t_Klanten);
t_KlantenViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("t_KlantenViewSource")));
t_KlantenViewSource.View.MoveCurrentToFirst();
I use Entity Framework. Why doesn't those two solutions work for me. Are there any other solutions to refresh the DataGrid?
If you see the old value with that type of data binding, it means you don't update the initial source, but only the temporary one (in your case t_KlantenViewSource). Calling Refresh method on the data added by FindResource cause app to reload it from pre-compiled resource, which won't change in this case.
In other words, the problem is you use pre-compiled resource
On the same time, you fill the dataset with actual data, but your dataset is in memory, view source - inside application file.
You can try:
binding ItemsSource of the entire datagrid or separate column to the TableAdapter dataset
change resource compiling behaviour to Content, thus the app will reload it from external file each time you call Refresh method.
ADDITION:
You can find a solution for your case, just to save time. Check paragraph "performing updates" Other way is to provide NotifyPropertyChanged Event for each bound parameter, but I can't say it is better in this case, assuming you update columns via unique buttons.
To be short, you need a method like this on your button clicked / property changed:
aOVDataSett_KlantenTableAdapter.Update(aOVDataSet.t_Klanten)
after button click first set datagrid datasource to null then assign the datas
datagrid.datasource=null
Thank you all for your answers. As Jasmine suggested, I reloaded the dataset and rebounded the datagrid. Maybe it's not the best way to do it but it was the only solution I had then.
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 have a MainViewModel that contains a reference to an ObservableCollection:
public ObservableCollection<SomeClass> ListOfPeople
{
get
{
return MyClass.BaseCollection;
}
}
BaseCollection is also an instance of ObservableCollection<SomeClass>. ListOfPeople is bound to a ListBox control on the second page - the application starts with the first page, initiates the download process to populate BaseCollection and switches to the second page while the download is still in progress.
The problem is that when the binding occurs, BaseCollection is null and therefore the ListBox is not populated. However, even when the download process finishes, the ListBox still remains empty. I am assuming this is happening because BaseCollection isn't notifying the proper instance about existing changes to the collection, but I am not sure.
BaseCollection has items inside it - I confirmed it.
Any suggestions on how I can work around the issue? Anyone here binding to an ObservableCollection via MVVM Light just like I showed above?
If you donot want to instantiate an empty ListOfPeople in the constructor and use this instance for database-loading you have to do this:
After loading of ListOfPeople is completed, your MainViewModel must call RaisePropertyChanged("ListOfPeople"); to tell the view that the data has changed.
Background: Thanks to ObservableCollection MyClass.BaseCollection.Add() updates the gui. As soon as MyClass.BaseCollection = new Obser... is called there is no more update of the gui since the gui holds a reference to the old contend of MyClass.BaseCollection. mvvm-light-RaisePropertyChanged() tells the gui to update its reference to a new collection
I haven't worked on MVVM Light, so sorry if there is something specific about it that i am missing.
Looking at your implementation,
public ObservableCollection<SomeClass> ListOfPeople
{
get
{
return MyClass.BaseCollection;
}
}
This code should work, and the control which is binded to this source should get propert updated without being concerned about the actual source where the instance of observable is created.
Thus, the only possible problem here could be that your MyBase.BaseCollection is null in the begining. So, if you avoid that situation and create an empty collection where you have declared this observable item, and then trigger your downloading process the way it is, then everything should work fine.
Hope this would be of help.
Good afternoon,
I am trying to use as Linq to SQL datacontext for a ListBox in WPF.
Basically, I assign the Linq DataContext to the form's DataContext property. Then, I bind it to the list.ItemsSource.
Everything works fine, I can show the details of each of my elements in a textbox (master-details scheme).
The thing is, I would like to be able to add a new element to the list:
private void Button_Click(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
var table = lst_markets.ItemsSource as System.Data.Linq.Table<Market>;
table.InsertOnSubmit(new Market() { IdMarket = Guid.NewGuid(), Name = txt_newmarket.Text });
table.Context.SubmitChanges();
}
The value is indeed added to the database, but the ListBox is not refreshed.
What should I do to refresh the list?
Thanks,
Jeremie
Table<TEntity> does not implement INotifyCollectionChanged, so the binding manager does not get notified that the collection's contents have changed.
A few options for you:
keep an ObservableCollection that you fill from the table, and keep synchronized. As you add/remove items from it the list would stay synchronized via binding. See this article for something similar
Hack around it - set the lst_markets.ItemsSource to null and back to the table when changing the collection. This would cause a full rebind, and feels like a dirty hack, but should work.
Don't Do that! A Table<T> is not a collection - it represents a query. Bind to a collection instead. If I remember correctly, every time you iterate a Table it queries the database, which means that any time the listbox feels it needs to enumerate, or the binding manager, or your ui code does the same you are hitting the database.
This forum post has an ObservableEntitySetWrapper that may give you some ideas.
Also see this SO question: How to refresh a WPF DataGrid?
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.