After adding a new row why aren't my bound controls updating? - wpf

I'm using wpf drag and drop databinding to datasets. I generate a new datatable row using the AddNew method of the BindingListCollectionView. I set values on the new row and call CommitNew on the BindingListCollectionView. I expect to see my assigned values in the bound controls but they are all blank. If I save the changes to the database the controls are then updated, but I want the user to see my assigned values before calling UpdateAll on the TableAdapterManager.
Background:
I've created a strongly typed dataset in a project separate from my wpf application. My wpf app references the dataset application. I added an object datasource in the wpf app pointing to the typed dataset. I dragged fields/controls from the datasources window to the wpf designer window. The generated xaml includes a Window.Resources section with a CollectionViewSource accurately bound to my dataset and datatable. This CollectionViewSource is the DataContext for the controls that I dragged to the design surface. All of the controls use TwoWay databinding.
When the window loads I grab a reference to the xaml's CollectionViewSource (using FindResource). I then grab a reference to the view property of the CollectionViewSource and cast it to a BindingListCollectionView. Now I use the AddNew method of the BindingListCollectionView to generate a new row (which AddNew returns as an object). I cast the object to a DataRowView and access it's Row property. I then cast the row to a strongly typed datatable row (generated by the DataSet designer). Now I assign values to some of the datatable row columns. I call CommitNew on the BindingListCollectionView. Finally I call MoveCurrentToFirst on the CollectionViewSource.
Problem:
Using a watch expression I can see the data is in the SourceCollection of both the CollectionView and the BindingListCollectionView. Can anyone explain why the bound controls do not show the data unless I save the changes to the database?
Code (generated XAML not shown):
Private WithEvents _cvsScanData As System.Windows.Data.CollectionViewSource
Private WithEvents _blcvScanData As System.Windows.Data.BindingListCollectionView
_cvsScanData = CType(Me.FindResource("Dt_tblScanDataViewSource"), System.Windows.Data.CollectionViewSource)
_blcvScanData = CType(_cvsScanData.View, BindingListCollectionView)
Dim newRow As LabDataSet.dt_tblScanDataRow = CType(CType(_blcvScanData.AddNew, System.Data.DataRowView).Row, LabDataSet.dt_tblScanDataRow)
newRow.SampleID = "testSampleID"
newRow.MachineID = "testMachineID"
_blcvScanData.CommitNew()
_cvsScanData.View.MoveCurrentToFirst()

The simple fix is to call the Refresh method of the BindingListCollectionView after calling CommitNew.
_blcvScanData.Refresh()
I stumbled across this answer to my own question via intellisense. If anyone can explain why refresh is necessary I'd appreciate it. I expected the INotifyPropertyChange interface to update the bound controls obviating the need to call refresh.

Related

WPF Datagrid bound to ViewModel.DataTable. SelectedItem not binding to ViewModel.SelectedObject

TYFYT. I need some help understanding what's going on here and how binding to a datagrid works. I am coming from a knowledge base of working with WinForms so maybe my approach is all wrong?...
I have a WPF window, with a Datagrid.
In the ViewModel I have DataTable that I have populated with my Booking objects. The cols across the top correspond to a TimeSlot, and the row headers correspond to an instructor.
void DisplayBookings()
{
foreach (Booking booking in bookings)
{ //Add each Booking object to the DataTable
DtBookings.Rows[booking.GridRow][booking.SlotTimeId + 1] = booking;
}
}
In the window xaml I have the datagrid bound to the DtBookings.
<DataGrid x:Name="grdInstructors"
ItemsSource="{Binding DtBookings}" DisplayMemberPath="Id"
SelectedItem="{Binding SelectedCell}"
When I run this, it's working fine. I see the bookings in the correct cells in the grid.
Trying to do work against the grid is what is raising questions.
Q1. What is the grid cell holding? I have put the Booking objects in the ViewModel.DataTable and in the grid cell I see "myNamespace.Models.Booking". This is fine but when I try to access it I get the error "'Unable to cast object of type 'System.String' to type 'MyObject' (Booking)
So is the datagrid only holding a String that is the name of the object? I could accept that answer since I'm thinking the View should only be a visual representation of the objects.
If that is so, then how should SelectedItem=ViewModel.MySelectedItem work? (In this case it doesn't). Initially I thought it should have a copy of the object so that when I click on the cell it will tell the ViewModel what object I have chosen. This normally works when I am bound to an ObservableCollection, but in this case it is bound to a DataTable so I'm not getting the same result.
And on a final note I have noticed that DisplayMemberPath="Id" doesn't work in this case. (It's not really an issue since I usually override `ToString())
It would be great to have a definitive answer on this before I add logic to work around the problem and defeat the point of MVVM and Binding.
TYVM.

Add columns and rows to wpf listview dynamically

i want to add new columns and rows to a C# WPF ListView (GridView) at runtime. As far as i know you can only add rows to a gridview by using anonymous objects or classes with a static set of members to which the columns are bound. Is there a way to do this at runtime that the user is able to add a new column, bind this column to something and add new data?
thx
ooorndtski
Yes, you can do this in code an runtime. You need the GridView as a variable (give it a name in XAML to auto-generate that variable in Visual Studio). The GridView has a Columns property that you can handle like any other collection, you can add and remove for example.
This is the example from MSDN (The gridview name "myGridView"):
GridViewColumn gvc3 = new GridViewColumn();
gvc3.DisplayMemberBinding = new Binding("EmployeeNumber");
gvc3.Header = "Employee No.";
gvc3.Width = 100;
myGridView.Columns.Add(gvc3);
Generally speaking, anything you can do in XAML, you can do in code.
So, what i was looking for is described in this thread.
He creates a new Dictionary-class, which implements the INotifyPropertyChanged interface.
When adding data to the dictionary an event is triggered.
At the place in your code where you want to add a new row, you just put the data into an object of this Dictionary class and add the Dictionary to an ObservableCollection which is bound to the DataGrid.

Observable Collection is not updating the datagrid

I am using a Dim All_PriceLists As System.Collections.ObjectModel.ObservableCollection(Of BSPLib.PriceLists.PriceListPrime) where PriceListPrime implements Inotify for all properties in it.
I bound the All_PriceList to a datagrid as DataGrid1.ItemsSource = All_PriceLists but when I do All_PriceLists=Getall() where Getall reads and gets the data from the DB, the datagrid is not updating.
It updates only when I hack it this way:
DataGrid1.ItemsSource = Nothing
DataGrid1.ItemsSource = All_PriceLists
Could you please tell me where I have gone wrong or what I should implement. Thank you.
You have several solutions to your problem
Update the ItemsSource directly (instead of replacing the local member variable)
DataGrid1.ItemsSource = new ObservableCollection(Of PriceListPrime)(GetAll())
Update the ObservableCollection (as mentioned in another answer)
All_PriceList.Clear();
For Each item in Getall()
All_PriceList.Add(item)
Next
Set your DataContext to a view model and bind to a property of the view model
Dim vm as new MyViewModel()
DataContext = vm
vm.Items = new ObservableCollection(Of PriceListPrime)(GetAll())
The view model will implement INotifyPropertyChanged and raised the PropertyChanged event when the Items property is changed. In the Xaml your DataGrid's ItemsSource will bind to the Items property.
The problem is that you are not updating the collection, you are replacing it, which is different.
The datagrid remains bound to the old list and the updated data is stored in a new unbound collection. So, you are not hacking a solution, your are binding the datagrid to the new collection, which is correct.
If you want a more automatic solution, you should bind your datagrid to a dataset/datatable, which is totally different code.
You should update ObservableCollection instead of creating new one if you want the app to react on your changes.
So, clear All_PriceList collection and add new items into it. Example:
All_PriceList.Clear();
For Each item in Getall()
All_PriceList.Add(item)
Next
ObservableCollection doesn't support AddRange, so you have to add items one by one or implement INotifyCollectionChanged in your own collection.

Silverlight DataGrid and Binding columns

I have a silverlight datagrid control and columns autogenerate property is set to false.
I am using MVVM and wants to bind the columns collection.
The data which i get is from xml. Something similar to sample code
Link
Now by passing the datagrid control from xmal file to the modelview I can get the expected behavior but with that way , i am adding columns in the datagrid control.
Is there any way, so that I can bind the columns collection with the datagrid control so that no need to pass the control to view model.
-Rajesh
Sounds like a strange implementation of MVVM you have there. Your ViewModel should be completely independent of the View. If you want to create dynamic columns on the grid then why not expose a relevant property collection on the ViewModel, and iterate through it in a relevant method on the View code behind e.g.
//Used with an Infragistics XamWebGrid control
private void BuildGrid() {
foreach (var dataItem in **ViewModel**.MyDataCollection)
{
var myCol = new TemplateColumn
{
HeaderText = dataItem.ItemNm,
Key = dataItem.PrimaryKey
};
MyGrid.Columns.Add(myCol);
}
}
You really shouldn't be passing controls from the View over to the ViewModel. The ViewModel should have no knowledge of any controls on the View.

WPF Datagrid using MVVM.. is two way binding to DataTable possible?

I have a datagrid in my View with it's ItemSource bound to a DataTable in my ViewModel. When I update the DataTable programmatically (adding a column through a command) the changes are not populated to the View. Also, if I invalidate the View, by switching to another tab and then switching back, the changes made are shown.
My ViewModel inherits from INotifyPropertyChanged, and I am raising the PropertyChanged event correctly since I use the same process for other bound properties in the ViewModel and they work as expected.
Is it possible to get the datagrid to reflect changes I've made to the bound DataTable using the MVVM pattern?
Is there a datagrid event I can use to refresh the datagrid in the view's code behind?
Thanks for your help!
-Steven
While modifying rows and editing cell contents in the DataTable get reflected in the DataGrid (works for me) you're right that ColumnChanges don't seem to be. If you're using the AutoGenerateColumns option then I imagine it does so at initialization but doesn't watch for changes afterwards.
If you can find an event which fires (I haven't noticed one) when a column is added to the DataTable you could then add it manually in the code behind. Another hack which may or may not be practical would be to set your DataTable propety to null, and then re-set your property to the DataTable, with OnPropertyChanged being called each time. That should force the rebuilding of the DataGrid.
private DataTable _myDataTable;
public DataTable MyDataTable
{
get { return _myDataTable; }
set
{
_myDataTable = value;
OnPropertyChanged("MyDataTable");
}
}
void SomeMethod()
{
....results in column changes
DataTable holder;
holder = MyDataTable
MyDataTable = null;
MyDataTable = holder;
}
You must use ObservableCollection<> type to binding with DataGrid. Until do this, your DataGrid can update the change of DataTable in ViewModel.
Raising the PropertyChanged event seem useless when you bind with the type rather than ObservableCollection<>.

Resources