public ObservableCollection<Model.ChildCommand> ChildCommands { get; private set; }
ChildCommands is bound to a Datagrid.
Both Segments show me the items in the Datagrid.
But in Segment one the Datagrid items doesn't refresh automatically when the collection changes.
With Segment two, the refresh works.
Segment 1:
var childs = from child in m_context.ChildCommand.Local
select child;
this.ChildCommands = new ObservableCollection<Model.ChildCommand>(childs);
Segment 2:
this.ChildCommands = m_context.ChildCommand.Local;
How do I get the automatic refresh by using Segment one?
The reason for Segment 1 not updating automatically is that you end up binding to a different ObservableCollection instance.
When m_context.ChildCommand.Local changes, your Segment 2 datagrid gets notified because it is bound to that observable collection instance. However your Segment 1 datagrid is bound to a different observable collection instance (that you yourself create when you say new ObservableCollection(childs).
If you truly want both of them to be bound to the m_context.ChildCommand.Local observable collection then you should implement it as such instead of creating a different observable collection instance for Segment 1.
You need to implement INotifyPropertyChanged for the ChildCommands property, as such:
public class YourClass: INotifyPropertyChanged
{
private ObservableCollection<Model.ChildCommand> _childCommands;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Model.ChildCommand> ChildCommands
{
get { return _childCommands; }
set
{
_childCommands= value;
OnPropertyChanged("ChildCommands");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
See this for more info:
http://msdn.microsoft.com/en-us/library/ms743695%28v=vs.110%29.aspx
my solution to this is to inherit my class from the NotificationObject class.
After that, just use
this.ChildCommands = m_context.ChildCommand.Local;
RaisePropertyChanged(() => this.ChildCommands); //this line notifies the UI that the underlying property has changed.
Bindings to a collection will not be updated if the collection gets re-instantiated. You have two options:
Implement INotifyPropertyChanged on the ChildCommands property
Instead of doing:
this.ChildCommands = new ObservableCollection(childs);
Do this instead:
this.ChildCommands.Clear();
foreach(var child in childs)
this.ChildCommands.Add(child);
Related
I have a BookViewModel class with some properties, one of then an ObservableCollection. But I'm having problems updating its value. This is my case:
public class BookViewModel : INotifyPropertyChanged
{
private IEnumerable<Book> booksList;
private ObservableCollection<Chapter> selectedChapters = new ObservableCollection<Chapter>();
public BookViewModel()
{
}
public BookViewModel(List<Book> booksList)
{
this.BooksList = booksList;
}
// ...
public ObservableCollection<Book> SelectedChapters
{
get
{
return this.selectedChapters;
}
set
{
this.selectedChapters = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In one UserControl of my application I do:
private TrainViewModel booksViewModel;
// ...
booksViewModel = new BookViewModel(booksList); // booksList comes from other site
this.DataContext = this.booksViewModel;
And in another UserControl, which is created dynamically as a child of the previous UserControl, I do:
private TrainViewModel booksViewModel;
// ...
booksViewModel = new BookViewModel();
this.DataContext = this.booksViewModel; // Different constructor
In this latter page, I have some checkboxes which modify my selectedChapters property by adding or removing elements from it:
// When some checkbox is checked
this.booksViewModel.SelectedChapters.Add(selectedChapter);
// When some checkbox is unchecked
this.booksViewModel.SelectedChapters.Remove(selectedChapter);
If each time a checkbox is checked or unchecked I do:
Debug.Print(this.booksViewModel.SelectedChapters.Count()); // Always print out 1!!
I'm wondering if using the same ViewModel, but with different instances in each view (the new thing), is causing the problem.
Ok, I could solve it. Not sure if I'm expressing myself well, but it was like I was modifying different sources of data (i.e. data contexts). So, what I did was try to cast the data context of the child UserControl to a BookViewModel (which is the data context of its parent) and work from that:
// Inside the event handler for check and uncheck
BookViewModel bookViewModel = this.DataContext as BookViewModel;
// When some checkbox is checked
if (bookViewModel != null){
this.booksViewModel.SelectedChapters.Add(selectedChapter);
}
// When some checkbox is unchecked
if (bookViewModel != null){
this.booksViewModel.SelectedChapters.Remove(selectedChapter);
}
And that's all. Update perfectly. I don't do anything with related to data dontext or view model in any part of the code (even in the constructor). Now, it's like I'm modifying the data in the same data context of the parent (sorry if my explanation isn't precise, I'm still getting used to WPF concepts).
I have a combo box that is bound to an object from a model that is instantiated inside of my view model. OnPropertyChange is handled inside of the Notifier class that inherits from INotifyPropertyChange. The view model polls executes a method from a data access layer and returns an observablelist to the view model. This is then passed into a constructor that builds the object i want to bind to the combo box. The object has two properties. 1) An observable list of possible selections and 2) a string that represents the current selected item.
Here is the problem. The combo box is successfully bound and populated by the list. It does not however appear to call the setter method when an item is changed. I need this functionality so I can continue with application logic once the item is elected. Debugging confirms that no setter is called, only the get. The setter is in fact called on the model however which make sense. Im certain I am missing something here and am open to suggestions on a better way to do this.
Model
public class WellListGroup : Notifier
{
private ObservableCollection<string> _headers;
public ObservableCollection<string> headers
{
get { return _headers; }
set { _headers = value; OnPropertyChanged("headers"); }
}
private string _selected;
public string selected
{
get { return this._selected;}
set { this._selected = value; OnPropertyChanged("selected");}
}
}
Notifier
public class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
View Model
public class MainViewModel : Notifier
{
//data access layer
public static getWells gw = new getWells();
//set combo box
public static ObservableCollection<string> headers = gw.getHeaders();
private WellListGroup _wlg = new WellListGroup {headers = headers, selected = headers[0]};
public WellListGroup wlg
{
get {
return _wlg;
}
set {
_wlg = value;
OnPropertyChanged("wlg");
OnChange()// do stuff!!!
}
}
View
<ComboBox x:Name="groupComboBox"
DockPanel.Dock="Top"
ItemsSource = "{Binding Path = wlg.headers}"
SelectedItem="{Binding Path = wlg.selected, Mode=TwoWay}">
</ComboBox>
EDIT - Reworked ViewModel to Subscribe to event on the object
public class MainViewModel : Notifier
{
//data access layer
public static getWells gw = new getWells();
//set combo box
public static List<string> headers = gw.getHeaders();
private WellListGroup _wlg = new WellListGroup {headers = headers, selected = headers[0]};
public WellListGroup wlg
{
get {
return _wlg;
}
set {
_wlg = value;
OnPropertyChanged("wlg");
OnChange(_wlg.selected);// do stuff!!!
}
}
public MainViewModel()
{
// Move this into the constructor to avoid any race conditions
_wlg = new WellListGroup {headers = headers, selected = headers[0]};
// Subscribe to the property change even for WLG
_wlg.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == "selected") {
}
OnChange(_wlg.selected);// do stuff!!!
};
}
The situation is that the reflected property setter is within the _wlg class and not the setter of the _wlg class itself on the VM. The bounded item is not going to the top level but the lower property as mentioned.
Either put in a commanding system to kick off the OnChange()// do stuff!!! code or subscribe to the _wlg class instance INotifyProptertyChanged event and call the method you mentioned.
Is there anyway to handle either of those from within the view model?
Yes, subscribe to the instance of the class WellListGroup property changed event and look for selected or others to report a change.
public MainViewModel()
{
// Move this into the constructor to avoid any race conditions
_wlg = new WellListGroup {headers = headers, selected = headers[0]};
// Subscribe to the property change even for WLG
_wlg.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == 'selected')
OnChange()// do stuff!!!
};
}
Of note, it is unclear if you really need to hold the strings in an ObservableCollection. That collection has its own implementation of notify events for adding and deleting of items within the collection.
If the VM needs that specific change info, then you will need to subscribe to the ObservableCollection's event(s) for such operations instead of/as well as the aforementioned above example.
If one does not need those notifications, holding the strings in an ObservableCollection is not needed and you can change it to a List<string> instead.
I'm trying to bind a WPF DataGrid to a List<ClassName>.
Below is my DataGrid:
<DataGrid ItemsSource="{Binding Source=FileProcessing}" AutoGenerateColumns="True"></DataGrid>
Below I am binding the list with database records:
FileProcessing = GetFileProcessingInfo(dtDateStart, dtDateEnd);
The FileProcessing is defined as a property below:
public List<FileProcessing_T> FileProcessing { get; set; }
The GetFileProcessingInfo Method also returns a List<FileProcessing_T> object.
The FileProcessing list does get some records from the database but the grid does not bind the data from the list.
I will appreciate your help.
You can keep your databinding.
But your have to implement the INotfifyPropertyChanged interface in the class where the FileProcessing property is located.
Because in the setter of FileProcessing you have to perform the change notification.
public ObservableCollection<FileProcessing_T> FileProcessing
{
get
{
return _fileProcessing;
}
set
{
if (_fileProcessing != value)
{
_fileProcessing = value;
RaisePropertyChanged("FileProcessing");
}
}
}
ObservableCollection<FileProcessing_T> _fileProcessing;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Otherwise the UI control will not know (not be notified) that the bound data has changed.
This will be enough to fix your problem.
It would even work if you continued to use List<FileProcessing_T> instead of ObservableCollection<FileProcessing_T>, however the ObservableCollection also supports change notifications if single elements are added and removed from the collection while List does not.
I have a button, When it's clicked it populates my Datagrid. The code is written within the .xaml.cs file, which I believe breaks the MVVM rule but it's just a temporary situation. I know it's not ideal for MVVM.
Calculate.xaml.cs
public void PopulateGrid(object sender, RoutedEventArgs e)
{
BindableCollection<Payments> PaymentCollection = new BindableCollection<Payments>
....
Datagrid.ItemsSource = PaymentCollection
....
}
My question is if there's a way to read the Datagrids ItemsSource From the ViewModel.
What I've Tried
LoansViewModel
public BindableCollection<Payments> paymentCollection {get; set;}
Calculate.xaml
<telerik:RadGridView ItemsSource="{Binding paymentCollection, Mode=TwoWay}" ... />
The collection paymentCollection Doesn't Update after calculate is clicked.
Just do this the correct MVVM way. Get rid of your PopulateGrid method in the .xaml.cs file and eliminate setting the Click property in your xaml. Instead bind the command property of the button to an ICommand property in your ViewModel the same way you are binding the ItemsSource of the RadGridView. You will need an implementation of ICommand to use and MVVM Lights RelayCommand is one option for that.
Here is the code for the ICommand:
private ICommand _populateGridCommand;
public ICommand PopulateGridCommand
{
get
{
if (_populateGridCommand == null)
{
_populateGridCommand = new RelayCommand(() => PopulateGrid());
}
return _populateGridCommand;
}
}
public void PopulateGrid()
{
PaymentCollection.Clear();
//load data and then add to the collection
}
UPDATE
To do this in code behind, you'll need to access the ViewModel and work on the collection from it. I don't like this but it should work.
public void PopulateGrid(object sender, RoutedEventArgs e)
{
var loansVM = DataGrid.DataContext as LoansViewModel;
loansVM.paymentsCollection.Clear();
var newData = //load data
foreach (var data in newData)
loansVM.paymentsCollection.Add(data);
}
Your xaml code looks like it should work provided the DataContext of your grid is set to your ViewModel instance where your paymentCollection property is declared.
Once your binding is set, it calls the get on the paymentCollection property. If your collection property object is not reassigned any further, and you add and remove elements from it, and it notifies on those changes via INotifyCollectionChanged, it will work. This is how ObservableCollection works and used most commonly for such scenarios.
However, if when you calculate, you re-assign your paymentCollection property with a new instance, your grid will not update, because you now have an entirely different collection. In that case you will need to notify the view that the paymentCollection property itself has changed. In which case you should implement it as a notification property:
private BindableCollection<Payments>_paymentCollection;
public BindableCollection<Payments> paymentCollection {
get { return _paymentCollection; }
set {
_paymentCollection = value;
OnPropertyChanged("paymentCollection");
}
}
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null) {
handler(this, new PropertyChangedEventArgs(name));
}
}
I'm a total newbie, just learning the basics of DataContext and the MVVM model. I've now got a grid bound to a view model object which implements INotifyPropertyChanged, however it appears that UpdateSourceTrigger (which all the WPF tutorials tell me to use) is not available for WinRT / Metro Style apps!
How do I implement INotifyPropertyChanged then?
I'm at the end of my tether here. I've spend nearly the whole day on the most basic of app examples, simply trying to get a grid to update after I click something. The only way I've managed to do this so far is to create an entirely new instance of the view model and reassign the DataContext which I know is wrong
UPDATE:
I have made some progress, but things have gotten very weird. I have a view model, with a generic list of items. The items list is wired up with a PropertyChangedEventHandler. If I replace the entire collection with a new one, the listview updates.
model.Items = new List<DataItem>{ new DataItem{ Title = "new item" }};
This results in a one item list with the above item. However, if I try adding an item, nothing happens
model.Items.Add(new DataItem{ Title = "added item" });
I also tried creating a method which added an item and specifically fired PropertyChanged, but that also doesn't work
Here's where it gets weird. Next I tried this code.
model.Items.Add(new DataItem { Title = "added item" });
model.Items = new List<DataItem> { new DataItem { Title = "new item" }};
This results in a two item list:
- new item
- added item
How can this be? The code says, "add one item" then "replace the whole list" but it executes in the reverse order?
UPDATE 2:
I've switched to ObservableCollection as suggested, which has actually solved the original problem. I can now add an item and it shows up on the list.
However, the new weird behaviour is still in effect. Items added before the collection is reset are appended to the end of the new collection. Why is my code executing in reverse order?
You need to implement the interface and send out the notification once the given property you care about changes.
public event PropertyChangedEventHandler PropertyChanged;
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("CustomerName"));
}
}
}
}
Keep in mind that for a collection, you should use an ObservableCollection as it will take care of the INotifyCollectionChanged being fired when an item is added or removed.
I would suggest to scale your sample back as far as possible. Don't start with a DataGrid but rather a simple TextBoxand Button, where the Button forces a change in your ViewModel which will then reflect on the UI.
Code taken from here.
It's best to implement a parent class which implements it like this:
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
And then in your subclass (i.e. ViewModel) in your property do something like this:
public class MyViewModel : NotifyPropertyChangedBase
{
private string _name;
public string Name {
get{ return _name; }
set{
_name = value;
RaisePropertyChanged("Name");
}
}
}