I'm pretty new in WPF programming environment. I'm trying to write a program out using MVVM design pattern.
I've did some studies and read up some articles related to it and many of a time I came across this thing called
ViewModelBase
I know what it is.. But may I know specifically where should I begin with to be able to write out my own ViewModelBase? Like... Really understanding what's happening without getting too complicated. Thank you :)
It's worth nothing to use MVVM frameworks if you don't know what's going on inside.
So let's go step by step and build your own ViewModelBase class.
ViewModelBase is class common for all your viewmodels. Let's move all common logic to this class.
Your ViewModels should implement INotifyPropertyChanged (do you understand why?)
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
the [CallerMemberName] attribute is not required, but it will allow you to write:
OnPropertyChanged(); instead of OnPropertyChanged("SomeProperty");, so you will avoid string constant in your code. Example:
public string FirstName
{
set
{
_firstName = value;
OnPropertyChanged(); //instead of OnPropertyChanged("FirstName") or OnPropertyChanged(nameof(FirstName))
}
get{ return _firstName;}
}
Please note, that OnPropertyChanged(() => SomeProperty) is no more recommended, since we have nameof operator in C# 6.
It's common practice to implement properties that calls PropertyChanged like this:
public string FirstName
{
get { return _firstName; }
set { SetProperty(ref _firstName, value); }
}
Let's define SetProperty in your viewmodelbase:
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(storage, value))
return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
It simply fires PropertyChanged event when value of the property changes and returns true. It does not fire the event when the value has not changed and returns false. The basic idea is, that SetProperty method is virtual and you can extend it in more concrete class, e.g to trigger validation, or by calling PropertyChanging event.
This is pretty it. This is all your ViewModelBase should contain at this stage. The rest depends on your project. For example your app uses page base navigation and you have written your own NavigationService for handling navigation from ViewModel. So you can add NavigationService property to your ViewModelBase class, so you will have access to it from all your viewmodels, if you want.
In order to gain more reusability and keep SRP, I have class called BindableBase which is pretty much the implementation of INotifyPropertyChanged as we have done here. I reuse this class in every WPF/UWP/Silverligt/WindowsPhone solution because it's universal.
Then in each project I create custom ViewModelBase class derived from BindableBase:
public abstract ViewModelBase : BindableBase
{
//project specific logic for all viewmodels.
//E.g in this project I want to use EventAggregator heavily:
public virtual IEventAggregator () => ServiceLocator.GetInstance<IEventAggregator>()
}
if I have app, that uses page based navigation I also specify base class for page viewmodels.
public abstract PageViewModelBase : ViewModelBase
{
//for example all my pages have title:
public string Title {get; private set;}
}
I could have another class for dialogs:
public abstract DialogViewModelBase : ViewModelBase
{
private bool? _dialogResult;
public event EventHandler Closing;
public string Title {get; private set;}
public ObservableCollection<DialogButton> DialogButtons { get; }
public bool? DialogResult
{
get { return _dialogResult; }
set { SetProperty(ref _dialogResult, value); }
}
public void Close()
{
Closing?.Invoke(this, EventArgs.Empty);
}
}
The below class can be used as a ViewModelBase in WPF projects:
public abstract class ViewModelBase : INotifyPropertyChanged
{
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches the desired value. Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">Name of the property used to notify listeners.This
/// value is optional and can be provided automatically when invoked from compilers that
/// support CallerMemberName.</param>
/// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns>
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
// Log.DebugFormat("{0}.{1} = {2}", this.GetType().Name, propertyName, storage);
this.OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
And an example of ViewModel class is:
public class MyViewModel : ViewModelBase
{
private int myProperty;
public int MyProperty
{
get { return myProperty; }
set { SetProperty(ref myProperty, value); }
}
}
For ease of writing, below snippet can be used:
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets
xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>OnPropertyChanged</Title>
</Header>
<Snippet>
<SnippetTypes>
<SnippetType>SurroundsWith</SnippetType>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
<Declarations>
<Literal>
<ID>TYPE</ID>
<ToolTip>Property type</ToolTip>
<Default>int</Default>
</Literal>
<Literal>
<ID>NAME1</ID>
<ToolTip>Property name</ToolTip>
<Default>MyProperty</Default>
</Literal>
</Declarations>
<Code Language="CSharp">
<![CDATA[private $TYPE$ _$NAME1$;
public $TYPE$ $NAME1$
{
get => _$NAME1$;
set => SetProperty(ref _$NAME1$, value);
}]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
The full code could be downloaded from here.
You have some nuget package to implement MVVM
MVVM light
MVVM Cross
Prism
For me the easier for a beginner is MVVM light because it provide some code sample.
So the better is to install this nuget package, have a look about the generated code and back to us for more explanations if you need.
In most MVVM frameworks, the base ViewModel classes actually contain very little code - usually just an implementation of INotifyPropertyChanged and some helper functions.
Take a look at the source code for MVVM Light's ViewModelBase and ObservableObject classes. ObservableObject is mostly the INotifyPropertyChanged implementation - using a lambda expression rather than "magic strings" for the property name. ViewModelBase extends ObservableObject and is mostly a utility method to determine if you're running inside the Visual Studio designer
I like this BaseVewModel it gives a nice clean style to your view models. Check out the various 'before' and 'after' comparisons. Of course, nothing is mandatory - if you don't like a feature that the BaseViewModel provides then don't use it. Or modify it because you have the source code. In particular note that there are three different ways to implement properties with change notification - choose the level of sophistication that you understand/feel comfortable with.
To revisit this answer today, I'd like to offer additional productivity improvements when writing MVVM code for Visual Studio.
The Intellisense in Visual Studio can automatically create the SetProperty boilerplate method. To do so, I set the ViewModel in the XAML of my Window (see below). Then, whenever I reference a {Binding Path=NewProperty}, Right Click and Select Quick Actions and Refactoring... (or via Ctrl .). If the SetProperty method isn't made, it will automatically be created for you within your ViewModel class. Further, it'll generate the property and field required for the Binding.
<Window x:Class="My.Project.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:My.Project"
mc:Ignorable="d"
xmlns:viewmodel="clr-namespace:My.Project.ViewModels" d:DataContext="{d:DesignInstance Type=viewmodel:MainWindowViewModel}"
Title="MainWindow" Height="360" Width="1000">
...
</Window>
However, this method has drawbacks
The INotifyPropertyChanged is not implemented and the OnPropertyChanged method is not implemented (if you need it)
This would need to be done in every ViewModel
This is specific for Visual Studio
Benefits:
Once the SetProperty method is defined in the project, using the Quick Actions and Refactoring... option will only generate the necessary property and field for you. This also works if you inherit from a ViewModelBase.
Here is the SetProperty method as generated by Visual Studio.
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
// Log.DebugFormat("{0}.{1} = {2}", this.GetType().Name, propertyName, storage);
this.OnPropertyChanged(propertyName);
return true;
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using System.Runtime.CompilerServices;
using System.ComponentModel;
namespace CollectionViewMVVM.ViewModel
{
public class BaseViewModel : INotifyPropertyChanged
{
/*Referencia: https://www.youtube.com/watch?v=PXEt1esjnZ0&ab_channel=Codigo369*/
public INavigation Navigation;
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnpropertyChanged([CallerMemberName] string nombre = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nombre));
}
private ImageSource foto;
public ImageSource Foto
{
get { return foto; }
set
{
foto = value;
OnpropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public async Task DisplayAlert(string title, string message, string cancel)
{
await Application.Current.MainPage.DisplayAlert(title, message, cancel);
}
public async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
{
return await Application.Current.MainPage.DisplayAlert(title, message, accept, cancel);
}
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if(EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
field = value;
OnPropertyChanged(propertyName);
return true;
}
/*Ejemplo para declarar entrys*/
private string _title;
public string Title
{
get { return _title; }
set
{
SetProperty(ref _title, value);
}
}
/*Para tipos bool*/
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
SetProperty(ref _isBusy, value);
}
}
protected void SetValue<T>(ref T backingFielded, T value, [CallerMemberName] string propertyName = null)
{
if(EqualityComparer<T>.Default.Equals(backingFielded, value))
{
return;
}
backingFielded = value;
OnPropertyChanged(propertyName);
}
}
}
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));
Here I added a model to my viewmodel:
public dal.UserAccount User {
get
{
return _user;
}
set
{
_user = value;
RaisePropertyChanged(String.Empty);
}
}
I handle property change event...
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
This is the binding I use:
<TextBox Text="{Binding User.firstname, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
Why the propertychange event is not triggered on updating view?
PropertyChanged is used to notify the UI that something has been changed in the Model.
Since you're changing an inner property of the User object - the User property itself is not changed and therefore the PropertyChanged event isn't raised.
Second - your Model should implement the INotifyPropertyChanged interface. - In other words make sure UserAccount implements INotifyPropertyChanged, otherwise changing the firstname will not affect the view either.
Another thing:
The parameter RaisePropertyChanged should receive is the Name of the property that has changed. So in your case:
Change:
RaisePropertyChanged(String.Empty);
To
RaisePropertyChanged("User");
From MSDN:
The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs.
(No need to refresh all the Properties in this case)
You can read more on the concept of PropertyChanged here
You can invoke a property changed event from another class. Not particularly useful if you have all the sources. For closed source it might be. Though I consider it experimental and not production ready.
See this console copy paste example:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
public class Program
{
static void Main(string[] args)
{
var a = new A();
a.PropertyChanged += A_PropertyChanged;
var excpl = new Excpl();
excpl.Victim = a;
excpl.Ghost.Do();
}
private static void A_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("Event triggered");
}
}
[StructLayout(LayoutKind.Explicit)]
public struct Excpl
{
[FieldOffset(0)]
public A Victim;
[FieldOffset(0)]
public C Ghost;
}
public class A : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
public class C : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void Do()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(""));
}
}
}
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 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.