I have seen that people
usually struggle with MVVM pattern in WPF and most of the people code WPF as
similar to Windows forms application. It is a wrong implementation. We need to use all the features of WPF so that we can benefit from WPF. We also can admire the beauty of the programming excellence in WPF.
In this article I will
demonstrate a small example of how to implement MVVM in WPF.
Introduction
MVVM is an GUI architectural design pattern.It is an extension of
MVC pattern. It is derived from Martin Fowler's Presentation Model.The Idea behind MVVM or MVC or MVP is to remove the dependency on code behind files.MVVM is helps in
developing loosely coupled applications in WPF. The main components of MVVM are:-
1. Model :- This is the
entity class which will have the data.
2. View :- This is
nothing but the xaml.
3. ViewModel :- The view
model is where we have the business logic and we bind the view and the model.
In the following example
we will bind data to a ListView using MVVM model.Along with that we will also
bind the title of the WPF Window from the view model.
The xaml code is as
follows:-
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="{Binding Title}" Height="400" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="726*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="350"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.ColumnSpan="1" Margin="5 5 5 5">
<ListView Name="dtgrid" Grid.Row="0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.CanContentScroll="False" Style="{x:Null}"
HorizontalAlignment="Stretch"
VerticalAlignment="Top" ItemsSource="{Binding ItemsData}" Background="Transparent"
BorderBrush="Transparent">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Height" Value="53"></Setter>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#ECF8F9"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Item" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="TxtBlockItem" Text="{Binding Path=Item}"
VerticalAlignment="Top" Margin="10,0,0,0" TextTrimming="WordEllipsis"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Description" Width="320">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" MaxHeight="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ListViewItem}, Path=ActualHeight}" VerticalScrollBarVisibility="Auto">
<TextBlock Name="txtDescription" Margin="10 0 0 0" Text="{Binding Path=Description,Mode=OneWay}"
TextWrapping="Wrap"/>
</ScrollViewer>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<Border BorderBrush="Black"
Grid.RowSpan="2" BorderThickness="1"
/>
</Grid>
</Grid>
</Window>
The code behind is as
follows:-
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ItemsViewModel();
}
}
In the constructor of
the window we are bind the DataContext property
with the ViewModel.
We can do this in xaml
also but I don't usually prefer to do it from xaml if we hard code in xaml then
again there will be tight coupling between with the View and ViewModel. By
setting the datacontext in the codebehind gives us the flexibility to change the
data context whenever we want let's say I want a different data context to be
bound to this window I can do that.
In the xaml we are doing
the binding using the syntax
Title="{Binding Title}"
Similarly we are binding
the data from the ObservableCollection to the List View using the ItemsSource
property as ItemsSource="{Binding ItemsData}".
Then we need to bind the individual properties to the textblock or what ever
control is used in the ListView. In our example we have used a textblock and the
Text property is binded as Text="{Binding Path=Item}".The point to be observed here is we need to
specify the property prefixed by Path. So this says that in the Observable
collection we have a property named Item. We have specified this inside into a
DataTemplate of a <GridViewColumn.CellTemplate>.
<GridViewColumn Header="Item" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="TxtBlockItem" Text="{Binding Path=Item}"
VerticalAlignment="Top" Margin="10,0,0,0" TextTrimming="WordEllipsis"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Now, moving on to the
View Model. The code is as follows
public class ItemsViewModel : INotifyPropertyChanged
{
#region
View Model Logic
/// <summary>
/// Observable Collection of
Items Model
/// </summary>
private ObservableCollection<ItemsModel> itemsData;
public ObservableCollection<ItemsModel> ItemsData
{
get { return itemsData; }
set
{
itemsData = value;
Refresh("ItemsData");
}
}
private string title = "WPF MVVM
Demo";
public string Title
{
get { return title; }
set
{
title = value;
Refresh("Title");
}
}
public ItemsViewModel()
{
ItemsDAL dataAccessLayer = new ItemsDAL();
List<ItemsModel> lstItemsModel = dataAccessLayer.LoadItems();
PopulateItems(lstItemsModel);
}
ObservableCollection<ItemsModel> obColl = new ObservableCollection<ItemsModel>();
private void PopulateItems(List<ItemsModel> lstItemsModel)
{
foreach (ItemsModel item in lstItemsModel)
{
obColl.Add(item);
}
ItemsData = obColl;
}
#endregion
#region
INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Fires the PropertyChanged event.
/// </summary>
/// <param
name="propertyName">The name
of the changed property</param>
private void Refresh(string propertyName)
{
if (this.PropertyChanged != null)
{
var e = new PropertyChangedEventArgs(propertyName);
this.PropertyChanged(this, e);
}
}
#endregion INotifyPropertyChanged Members
}
We have inherited from
the INotifyPropertyChanged interface. So that we can refresh a control when
ever a property is changed. In the code the we have used Refresh("Title"); in the setter property so that whenever a value
is changed we can do a refresh the value in the view. Similarly for the ItemsData Observable collection.
We have the Refresh(string propertyName)method to check if property is changed if it is changed then we
are doing a refresh. The collection should always be an
ObservableCollection.
The code for ItemsModel
is as follows:-
public class ItemsModel
{
private string item;
public string Item
{
get { return item; }
set { item = value; }
}
private string description;
public string Description
{
get { return description; }
set { description = value; }
}
}
It is just a model.
The refresh can be done
in the ViewModel as well as Model but I prefer to do it in the ViewModel.
ItemsDAL is used here to
mimic that the data is coming from data access layer.The code is as follows
public class ItemsDAL
{
public List<ItemsModel> LoadItems()
{
List<ItemsModel> lstItemsModel = new List<ItemsModel>();
for (int i = 1; i <= 50; i++)
{
ItemsModel item = new ItemsModel();
item.Item = "Item " + i;
item.Description = "Description for Item " + i;
lstItemsModel.Add(item);
}
return lstItemsModel;
}
}
So, here you go you have
implemented a simple WPF application using MVVM.
The output looks as follows:-
No comments:
Post a Comment