wpf , binding and datacontext - wpf

This is my ViewModel -
public class ViewModel
{
public ObservableCollection<Person> Persons { get; set; }
}
and this is Class Person:
public class Person : INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Now, every time one of the persons's FirstName is changing I want to do some tasks,
lets say raise a messagebox.
How do I do that ?

You need to implement INotifyPropertyChanged
public class Person : INotifyPropertyChanged
{
private string firstName;
public string FirstName
{
get { return this.firstName;}
set
{
this.firstName = value;
this.RaisePropertyChanged("FirstName");
MessageBox.Show("Hello World");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null))
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

Typically your person class will use the interface INotifyPropertyChanged, firing the PropertyChanged event whenever FirstName changes. This allows you to bind items in a view to your Person class and the view will be updated when the data changes.
To pop up a message box when any FirstName however, you will need some code behind in your view. One way to do it is to, as before, use INotifyProperty changed and subscribe to that on all Person objects in your view, using MessageBox.Show whenever an event changing FirstName is invoked. You can use the CollectionChanged event in the ObservableCollection to track Person objects in and out of the list to make sure that they are all connected to your Person FirstName changed event handler.
The best way to do it, in my opinion, is to have an event in the ViewModel rather than the Person class which fires whenever a change is made to any Person class (with the specific Person object as an argument). This will only work if the ViewModel is the only thing which can change Person.FirstName, and your View will have to bind to the ViewModel in an appropriate way to effect this.

You need to implement INotifyPropertyChanged on your viewmodel, and raise the property changed event when setting your persons collection. This will allow you listen for the fact that it has changed.
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx

Related

Can't extend MVVMLight's ViewModelBase with INotifyPropertyChanged

I had been using my own ViewModelBase and RelayCommand, and recently I installed MVVMLight, as well as PropertyChanged.Fody. It appears that PropertyChanged.Fody doesn't notify if you're using a custom setter, so I do need to implement OnPropertyChanged somewhere myself.
public NotifiableViewModelBase CurrentViewModel
{
get => _currentViewModel;
set
{
// store the previous viewmodel when the viewmodel is changed.
PreviousViewModel = _currentViewModel;
_currentViewModel = value;
OnPropertyChanged();
}
}
This is a member of a class that still inherits from my own ViewModelBase
public abstract class NotifiableViewModelBase : INotifyPropertyChanged
{
protected NotifiableViewModelBase() { }
public virtual string DisplayName
{
get;
protected set;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
I'd like to have this class inherit from MVVMLight's ViewModelBase, but if I do that, it says PropertyChanged hides its inherited member, and when I delete that event's declaration (to use the inherited member), I get errors in the OnPropertyChanged method, saying PropertyChanged can only be used on the left hand side of a += or -=.
I've managed to get around this by declaring PropertyChanged as a new event, which allows me to inherit from MVVMLight's ViewModelBase, but this smells improper to me.
Am I "doing it right" using the new event keywords, or is there some answer to how to use PropertyChanged as expected in the OnPropertyChanged?
(or, is there a way to write my custom setter and have the notification work without explicitly calling OnPropertyChanged()?)

How INotifyPropertyChanged's PropertyChanged event get assigned?

I have following code and it is working fine.
public partial class MainWindow : Window
{
Person person;
public MainWindow()
{
InitializeComponent();
person = new Person { Name = "ABC" };
this.DataContext = person;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
person.Name = "XYZ";
}
}
class Person: INotifyPropertyChanged
{
string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string strPropertyName)
{
if(null != PropertyChanged)
{
PropertyChanged(this,
new PropertyChangedEventArgs(strPropertyName));
}
}
}
When I create the "person" object in the constructor of MainWindow, it will assign the value for "Name" property of person, that time PropertyChanged event is NULL.
If the same "person" class property "Name" assigned in Button_Click event, "PropertyChanged" event is NOT NULL and it is pointing to OnPropertyChanged.
My question is how "PropertyChanged" event is assigned to OnPropertyChanged method?
Thanks in advance.
The WPF data-binding infrastructure will add a PropertyChanged handler when you set the object as a DataContext, in order to detect changes to your properties.
You can watch this happen by setting a breakpoint.
The OnPropertyChanged method that it points to is an internal WPF method, as you can see by inspecting the Target property of the delegate.
The event will be null until something is subscribed to it. By the time the button click event has happened, it has a subscriber (via the databinding system).

How can I trigger the PropertyChanged event from static method?

I have the following class
public class LanguagingBindingSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Dummy
{
get { return String.Empty; }
set
{
PropertyChanged(this, new PropertyChangedEventArgs("Dummy"));
}
}
}
that is bound to elements in XAML like this
Text="{Binding Dummy,Source={StaticResource languageSource},Converter={StaticResource languageConverter},ConverterParameter=labelColor}"
The sole purpose of the LanguageBindingSource class and its Dummy method is to allow property notifications to update the bindings when one or more resources change. The actual bound values are provided by the converter, looking up resources by the names passed as parameters. See the comments on this answer for more background.
My problem is that the resources are changed by a process external to the XAML pages containing the bindings and I need a single static method that I can call to trigger property change notification for all instances of the binding. I'm struggling to figure out just how I might do that. All ideas will be most appreciated.
Modify your class as follows:-
public class LanguagingBindingSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate {};
public static void FirePropertyChanged(string key)
{
((LanguagingBindingSource)Application.Resources[key]).NotifyPropertyChanged("Dummy");
}
private void NotifyPropertyChanged(string name)
{
PropertyChanged(this, new PropertyChangedEventArgs(name);
}
public string Dummy
{
get { return String.Empty; }
set
{
NotifyPropertyChanged("Dummy"));
}
}
}
Now are any point where you need to fire off this change use:-
LanguagingBindingSource.FirePropertyChanged("languageBindingSource");
Where "languageBindingSource" is the resource key that you are also using in your binding Source property.

Tracking only the Items in the observableCollection that have changed.Is this possible?

I have a datagrid that is bound to a observableCollection of Employees
The user is allowed to do in line editing by double clicking the datagridRow.
When binding the property in question I also use UpdateSourceTrigger.
When I user presses the save button ,the saveCommand is triggered in my MVVM and I want to create a list of only the employees that I have had property modified.
All my ViewModels implements INotifyPropertyChanged.
Despite lots of links on google I cannot seem to find an example that takes you through or explain how to track the items that have changed in a observablecollection.
Can you help?
Create base class for your Employee, for example, EntityBase and enum describing its states:
public enum EntityState
{
NotChanged,
Changed,
Added,
Deleted
}
public abstract class EntityBase : INotifyPropertyChanging, INotifyPropertyChanged
{
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
private EntityState state = EntityState.NotChanged;
public EntityState State
{
get { return state; }
set { state = value; }
}
public EntityBase()
{
state = EntityState.NotChanged;
}
protected virtual void SendPropertyChanging(string propertyName)
{
if ((this.PropertyChanging != null))
{
this.PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
protected virtual void SendPropertyChanged(string propertyName)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
When one of your employes in collection changed - mark it with EntityState.Changed, and then you can request only changed entities from collection and process them as you want.
I'm not aware of any built in way to do what you want.
What I've done in the past is to implement a boolean IsDirty property on the objects in the collection. Then set the IsDirty property to true anytime you raise PropertyChanged.

PropertyChanged doesn't work properly

I have a Silverlight application in which I implemented MVVM pattern.
In my application there is a child window on which I have ComboBox. I bound ItemsSource and SelectedItem of my combobox to a property (typeof ObservableCollection) and property of MyType appropriately. MyType is a "MODEL" derived from INotifyPropertyChanged. When my window is loaded I set values to this properties. But my combobox doesn't display selected item.
I found that when I set property which is bound to selected item (in ViewModel), the PropertyChanged event is null.
Can anyone help me. Thanks.
From the way you've described it the only thing being bound to is the ViewModel yet the only thing that implements INotifyPropertyChanged is MyType. Nothing is actually binding to the instance of my type to listen to its PropertyChanged event which is why its null.
It sounds like you haven't implemented INotifyPropertyChanged on your ViewModel.
PropertyChanged works fine, so it must be in your implementation of it. Simply implementing INotifyProperty changed isn't good enough, you have to explicity call the event.
For example, this will not work:
public class Model : INotifyPropertyChanged
{
public string Title { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
In order for it to work, you must raise the property changed. Easiest way is to encapsulate the logic in a method, like this:
public class Model : INotifyPropertyChanged
{
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
RaisePropertyChanged("Title");
}
}
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Of course you can put the event and the method in a base class to inherit from so multiple models can take advantage of it.

Resources