How the MVVM pattern became convenient:
WPF has a very powerful databinding feature that can directly bind two WPF elements together, but the common use of databinding is to bind some kind of data to the view. This is done by using the
One big limitation of using the
Separation of logic and presentation:
The MVVM pattern is so far only a convenient way to bind data to the view. But what about user actions, how are they handled? The classic approach, known from Win Forms is to register an event handler that is implemented in the code-behind file of the view. Doing this has some disadvantages:
Data Binding :
INotifyCollectionChanged
Interface:
public bool CanExecute(object parameter)
WPF has a very powerful databinding feature that can directly bind two WPF elements together, but the common use of databinding is to bind some kind of data to the view. This is done by using the
DataContext
property.One big limitation of using the
DataContext
property as
data source is, that we can use only one data object. But in a real life
project you usually have more than one data object per view. So the most
obvious approach is to aggregate all data objects into one single object that
exposes the aggregated data as properties and that can be bound to the DataContext
. This object
is called the view model in MVVM.Separation of logic and presentation:
The MVVM pattern is so far only a convenient way to bind data to the view. But what about user actions, how are they handled? The classic approach, known from Win Forms is to register an event handler that is implemented in the code-behind file of the view. Doing this has some disadvantages:
- Having event handlers in the code-behind is bad for testing,
since you cannot mock away the view.
- Changing the design of the view often also requires changes
in the code, since every element has its different event handlers.
- The logic is tightly bound to the view. It's not possible to
reuse the logic in another view.
So the idea is to move the whole presentation logic to the view
model by using another feature of WPF, namely
Commands
.
Commands can be bound like data and are supported by many elements as buttons, toggle
buttons, menu items, checkboxes and input bindings. The goal here is not to
have any line of logic in the code-behind of a view. This brings you the
following advantages;- The view-model can easily be tested by using standard
unit-tests (instead of UI-testing).
- The view can be redesigned without changing the view model.
- The view-model can even be reused; in some special cases
(this is usually not recommended).
The Model-View-ViewModel (MVVM) pattern
helps you to cleanly separate the business and presentation logic of your
application from its user interface (UI). It can make your application much
easier to test, maintain, and evolve. It can also greatly improve code re-use
opportunities and allows developers and UI designers to more easily collaborate
when developing their respective parts of the application.
Using the MVVM pattern, the UI of the
application and the underlying presentation and business logic is separated
into three separate parts:
·
Model: It
encapsulates the business logic and data.
·
View: It encapsulates
the UI and UI logic.
·
View Model: It encapsulates
presentation logic and state.
Prism includes
samples and reference implementations that show how to implement the MVVM
pattern in a Silverlight or Windows Presentation Foundation (WPF) application.
How MVVM Works?
In MVVM, the View Model knows nothing about the view. The
view interacts with the view model through data binding, commands, and change
notification events. The view model queries, observes, and coordinates updates
to the model, converting, validating, and aggregating data as necessary for
display in the view.
The View Class:
The view's responsibility is to define the structure and
appearance of what the user sees on the screen. Ideally, the code-behind of a
view contains only a constructor that calls the InitializeComponent method.
In Silverlight and WPF, data binding expressions in the
view are evaluated against its data context. In MVVM, the view's data
context is set to the view model.
The View Model Class:
The view model in the MVVM pattern encapsulates the
presentation logic and data for the view. The view model implements properties
and commands to which the view can data bind and notifies the view of any state
changes through change notification events via the INotifyPropertyChanged and INotifyCollectionChanged
interfaces. The view model is responsible for coordinating the view's
interaction with any model classes.
The view model exposes the model classes directly to the
view so that controls in the view can bind directly to them. The view model may
also implement data validation via the IDataErrorInfo
or INotifyDataErrorInfo interfaces.
The Model Class:
The model (Entity) represents the actual data and/or
information we are dealing with.The model in the MVVM pattern encapsulates business
logic and data.
Note :
The view and view model are loosely coupled via the
view's data context property; this allows visual elements and behaviors in the view to be data
bound to properties, commands, and methods on the view model.
Creating the View Model Using
XAML:
<UserControl.DataContext>
<my:QuestionnaireViewModel/>
</UserControl.DataContext>
Creating the View Model Programmatically:
public QuestionnaireView()
{
InitializeComponent();
this.DataContext = new QuestionnaireViewModel();
}
Data Binding :
Data binding plays a very important role in the MVVM
pattern. To ensure that the UI is kept up to date when the data changes in the
view model, it should implement the appropriate change notification interface.
If it defines properties that can be data bound with view, it should implement
the INotifyPropertyChanged interface. If the view model represents a collection, it should implement
the INotifyCollectionChanged interface or derive from the ObservableCollection<T> class that
provides an implementation of this interface. Both of these interfaces define
an event that is raised whenever the underlying data is changed.
In Prism we can
use NotificationObject class which
implements INotifyPropertyChanged
interface.
INotifyPropertyChanged
Interface:
The INotifyPropertyChanged interface is used to notify UI,
typically binding UI that a property value has changed.
For example, consider a Person object with a property
called FirstName. To provide generic property-change notification, the Person
type implements the INotifyPropertyChanged interface and raises a PropertyChanged event when FirstName is
changed.
While implementing INotifyPropertyChanged interface, it
define property changed event and the event is raised when the property value
got changed.
Example:
using
System.ComponentModel;
public class BaseModel :
INotifyPropertyChanged
{
private string _Name;
public string Name
{
get{return _Name; }
set{_Name = value;
OnPropertyChanged("Name");
}
}
public event
PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string
Name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs(Name));
}
}}
View:-
<TextBox Text="{Binding
Name, UpdateSourceTrigger=PropertyChanged}"></TextBox>
To set up dynamic bindings so that insertions or
deletions in the collection update the UI automatically, the collection must
implement the INotifyCollectionChanged interface. This interface exposes the CollectionChanged event that must
be raised whenever the underlying collection changes.
Example:
using System.Collections.Specialized;
public class BaseCollectionModel :
INotifyCollectionChanged
{
public event
NotifyCollectionChangedEventHandler CollectionChanged;
public void
OnCollectionChanged(NotifyCollectionChangedAction Action)
{
if (CollectionChanged != null)
{
CollectionChanged(this, new
NotifyCollectionChangedEventArgs(Action));
}
}
private List<Employe> emp;
public List<Employe> Emp
{
get{return emp; }
set{emp = value;
OnCollectionChanged(NotifyCollectionChangedAction.Add);
}}
}}
WPF provides the ObservableCollection<T> class, which is a built-in implementation
INotifyCollectionChanged interface.
Represents a dynamic data collection that provides
notification when the item gets added, removed and whole list get refreshed. ObservableCollection<T> implements INotifyCollectionChanged which provides notification when
the collection is changed.
Example:
When you click on add
button, a new record has been inserted into the data context. No matter
StudentInformationCollection is derived from List or ObservableCollection. But
it would be updated in the ListBox UI only if you derive it from ObservableCollection.
If you use List, you have reset the datacontext of WPF ListBox each and every
time you make changes to the collection.
using
System.Collections.ObjectModel;
private
ObservableCollection<Employe> emp1;
public ObservableCollection<Employe>
Emp1
{
get{return emp1;}
set{emp1 = value;}
}
Commands:
In WPF and Silverlight,
actions or operations that the user can perform through the UI are typically
defined as commands. Commands provide a convenient way to represent actions or
operations that can be easily bound to controls in the UI.
The view model can
implement commands as either a Command Method or as a
Command Object .A command
object is an object that implements the ICommand interface.
This interface defines an Execute method, which
encapsulates the operation itself, and a CanExecute method,
which indicates whether the command can be invoked at a particular time.
The Prism DelegateCommand
class encapsulates two delegates that each reference a method implemented
within your view model class. It inherits from the DelegateCommandBase
class, which implements the ICommand interface's Execute and CanExecute
methods by invoking these delegates. You specify the delegates to your view
model methods in the DelegateCommand class constructor.
Example
:
using
System.Windows.Input;
public class DelegateCommand:ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public DelegateCommand(Action<object> execute) : this(execute, null)
{
}
public DelegateCommand(Action<object> execute,
Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add {
CommandManager.RequerySuggested += value; }
remove {
CommandManager.RequerySuggested -= value; }
}
{
return _canExecute == null ? true :
_canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
No comments:
Post a Comment