I just started learning WPF coming from Java Swing and WinForms. I decided to try something new to learn other concepts and technologies for developing programs. Last time, I was introduced on the concept of MVC Pattern. For what I have learned, it is a way of separating the UI logic, business logic, and data. I found out that one of the key concepts of WPF is Binding and the MVVM Pattern.
Here is a part of my code where i tried implementing MVVM.
MainWindowModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Controls;
namespace DocNanzDCMS.Model
{
public class MainWindowModel : INotifyPropertyChanged
{
private PropertyChangedEventArgs pce;
public MainWindowModel()
{
pce = new PropertyChangedEventArgs("");
}
private UserControl userControl;
#region ControlProperty
public UserControl ContentProperty {
get
{
return userControl;
}
set
{
userControl = value;
PropertyChanged(this, pce);
}
}
#endregion
private DateTime dateTime;
#region DateProperty
public String DateProperty
{
get
{
return dateTime.ToLongDateString();
}
set
{
dateTime = DateTime.Parse(value);
PropertyChanged(this, pce);
}
}
#endregion
public String TimeProperty
#region TimeProperty
{
get
{
return dateTime.ToLongTimeString();
}
set
{
dateTime = DateTime.Parse(value);
PropertyChanged(this, pce);
}
}
#endregion
private String title;
public String TitleProperty
#region TitleProperty
{
get
{
return title;
}
set
{
title = value;
PropertyChanged(this, pce);
}
}
#endregion
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
}
}
MainWindowViewModel.cs
using DocNanzDCMS.Model;
using DocNanzDCMS.View;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace DocNanzDCMS.ViewModel
{
public class MainWindowViewModel
{
private MainWindow mainWindow;
private MainWindowModel mainWindowModel;
private Thread mainWindowThread;
private LoginModel loginModel;
private LoginViewModel loginViewModel;
private LoginView loginView;
private String title;
public MainWindowViewModel(MainWindowModel mainWindowModel, MainWindow mainWindow)
{
this.mainWindowModel = mainWindowModel;
this.mainWindow = mainWindow;
initialize();
}
private void initialize()
{
loginModel = new LoginModel();
loginView = new LoginView();
loginViewModel = new LoginViewModel(loginModel, loginView);
mainWindow.DataContext = mainWindowModel;
mainWindowThread = new Thread(BackgroundProcess);
mainWindowThread.IsBackground = true;
mainWindowThread.Start();
gotoLogin();
}
private void BackgroundProcess()
{
while(true)
{
updateTitle();
updateTime();
try
{
Thread.Sleep(100);
}
catch(ThreadInterruptedException e)
{
}
}
}
public void gotoLogin()
{
mainWindowModel.ContentProperty = loginView;
title = "Login";
}
private void updateTime()
{
mainWindowModel.DateProperty = DateTime.Now.ToString();
mainWindowModel.TimeProperty = DateTime.Now.ToString();
}
public void updateTitle()
{
mainWindowModel.TitleProperty = "Doc Nanz Dental | "+title;
}
}
}
MainWindow.cs
using DocNanzDCMS.Model;
using DocNanzDCMS.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DocNanzDCMS
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MainWindowModel mainWindowModel;
private MainWindowViewModel mainWindowViewModel;
public MainWindow()
{
InitializeComponent();
initializeApp();
}
private void initializeApp()
{
mainWindowModel = new MainWindowModel();
mainWindowViewModel = new MainWindowViewModel(mainWindowModel, this);
}
}
}
MainWindow.xaml
<Window x:Class="DocNanzDCMS.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:DocNanzDCMS"
mc:Ignorable="d"
Title="{Binding TitleProperty}" Height="600" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="75"/>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<!--Banner-->
<Grid Grid.Row="0" Background="AliceBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="225"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<!--Date and Time Panel-->
<Grid Grid.Column="2" Background="Aquamarine">
<Grid.RowDefinitions>
<RowDefinition Height="1.5*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--Date Background-->
<StackPanel Grid.Row="0" Background="BurlyWood"/>
<!--Date-->
<Label Grid.Row="0" Content="{Binding DateProperty}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<!--Time Background-->
<StackPanel Grid.Row="1" Background="BlanchedAlmond"/>
<!--Time-->
<Label Grid.Row="1" Content="{Binding TimeProperty}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
<!--Content-->
<ScrollViewer Grid.Row="1" Content="{Binding ContentProperty}"/>
<!--Status Bar-->
<Grid Grid.Row="2">
</Grid>
</Grid>
</Window>
I created a Model and View and manipulated those in ViewModel. I am not sure if this is a proper way of implementing MVVM or is it even an MVVM, because I am seeing it as MVC pattern.
On Wikipedia, it says:
The components are, Model, View, ViewModel, and Binder.
This part of my code displays a window with a banner, and on rightmost part of the banner are labels that displays date and time. It works, but my concern is if the way I made it is actually following MVVM pattern.
For the sake of MVVM the View Model should not contain a reference to the View (is considered bad practice)
Is the View that know the ViewModel and not the opposite. The View knows the ViewModel which in turn know the Model (or Models)
The INotifyPropertyChanged interface should be implemented in the ViewModel to permits the view to update itself through binding (in some circustances is perfectly legit to implement the interface on the Model also).
Keep in mind that the ViewModel can be seen as a Model adapted to the need of the View, so with this in mind i prefer to leave the Model classes as simple POCO objects and write the INotifyPropertyChanged implementation on the ViewModel
The ViewModel become the DataContext of the View (you can assign the DataContext in the View's constructor in code behind or in the xaml).
For navigating through views you could use (at minimum) 2 approaches
You should decide if you want a View-first approach or a ViewModel-first approach.
In the View-First approach when you want to navigate to a new page you create a View and some mechanism (the binder) will create the respective ViewModel (which in turn create or obtain the Model)
In the ViewModel first approach you create a new ViewModel (which in turn create or obtain the Model) and the binder will create the respective View.
Based on what i told you, here is an example:
View (MainWindowView.cs), we assign the DataContext:
ViewModel (MainWindowViewModel.cs):
Model (MainWindowModel.cs) :
Also, i think you should look at some framework like prism or caliburn micro (i prefer the first one) to assist you in the correct implementation on the MVVM pattern and not reinventing the wheel (as a plus you will get also a navigation system, to navigate between views).
Your question is very broad but here are some thoughts.
A view model shouldn't know anything about the view. Instead of injecting the
MainWindowViewModel
with a reference to theMainWindow
, you should simply set theDataContext
of theMainWindow
to an instance of the view model:The
MainWindowViewModel
can then initialize and/or communicate with the model while the view binds to the view model.Also, a view model shouldn't expose any
UIElements
such as for example aUserControl
.UIElements
are defined in the view.After several nights of studying, I finally "absorbed" the concept of MVVM and its difference with MVC.
Model - Most of the time they are just classes with properties. Like
User
,Product
etc...View - The user interface.
RegisterUserView
,LoginView
,AddProductView
.ViewModel - This is where the action happens.
ViewModel
manipulates the model based on the requirements/rules and exposes it forView
. ButViewModel
does not know the existence ofView
.Binding - This is the glue between the
View
and theViewModel
.In comparison with MVC (Just my opinion),
View
inMVC
is passive whileView
inMVVM
is active. TheController
inMVC
decides what contents should be displayed inView
, while inMVVM
,View
performs the binding making it responsible on what it should display.WPF is so much pain, but it is really powerful.