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.
Related
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()?)
I am using Aspect Oriented Programming in my WPF project. I have used it to decorate my viewModels with INotifyPropertyChanged interface and an implementation behavioral class as seen below:
Container.RegisterType<SomeViewModel>(
new Interceptor<VirtualMethodInterceptor>(),
new InterceptionBehavior(new LoggingBehavior(TraceEventType.Verbose)),
new AdditionalInterface<INotifyPropertyChanged>(),
new InterceptionBehavior<NotifyPropertyChangedBehavior>());
This works fine.
But when I have only get only properties like :
public bool IsDummy
{
get { return _isDummy; }
}
How do I call the OnPropertyChanged method from the ViewModel, which does the job of updating the View(UI). So if I update variable _isDummy to true, I should be able to call something which does the same job as OnPropertyChanged("IsDummY");
To notify the view that a value has changed you must raise the OnPropertyChanged event with the property which has changed. This is usually done by the standard implementation:
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Then in the setter of the property
public bool IsDummy
{
set
{
if (_isDummy!= value)
{
_isDummy= value;
OnPropertyChanged();
}
}
}
This syntax only works if it is called from within the property setter, alternatively you can call it when the property is changed elsewhere in the class
OnPropertyChanged(nameof(IsDummy));
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
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.
I'm trying to bind my window title to a property in my view model, like so:
Title="{Binding WindowTitle}"
The property looks like this:
/// <summary>
/// The window title (based on profile name)
/// </summary>
public string WindowTitle
{
get { return CurrentProfileName + " - Backup"; }
}
The CurrentProfileName property is derived from another property (CurrentProfilePath) that is set whenever someone opens or saves profile. On initial startup, the window title is set properly, but when ever the CurrentProfilePath property changes, the change doesn't bubble up to the window title like I expected it would.
I don't think I can use a dependency property here because the property is a derived one. The base property from which it is derived is a dependency property, but that doesn't seem to have any effect.
How can I make the form title self-updating based on this property?
That's because WPF has no way of knowing that WindowTitle depends on CurrentProfileName. Your class needs to implement INotifyPropertyChanged, and when you change the value of CurrentProfileName, you need to raise the PropertyChanged event for CurrentProfileName and WindowTitle
private string _currentProfileName;
public string CurrentProfileName
{
get { return __currentProfileName; }
set
{
_currentProfileName = value;
OnPropertyChanged("CurrentProfileName");
OnPropertyChanged("WindowTitle");
}
}
UPDATE
Here's a typical implementation of INotifyPropertyChanged :
public class MyClass : INotifyPropertyChanged
{
// The event declared in the interface
public event PropertyChangedEventHandler PropertyChanged;
// Helper method to raise the event
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName);
}
...
}