Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

MVVM (Model-View-ViewModel) Pattern For Windows Form Applications, using C#

, 9 May 2013
Rate this:
Please Sign up or sign in to vote.
It's a pattern like MVVM but for WinForms.

Introduction

The MVVM pattern is in favour of WPF/Silverlight developers because of its advantages. Microsoft says that MVVM:

  • Separates the business and presentation logic of the application from its UI, and makes it much easier to test, maintain, and evolve.
  • Allows developers and UI designers to more easily collaborate when developing their respective parts of the application.(Look at the link)

MVVM is for WPF, but is there any way to have the advantages for WinForms too? The answer is yes (good news or maybe not so new). Windows Form Applications can also have the advantages, and of course it doesn't mean the exact real Model-View-ViewModel (MVVM) pattern. I searched and found many approaches (Google Search) some of them were great and some not(!) But I've tried to present a simple useful solution. I hope you'll be interested.

Background

As you know, Program Logic and Content are separated In ASP.Net. And also in WPF, Program Logic is separated from UI. In Windows Forms, I preferred to use partial classes to separate Program Logic from UI until MVVM pattern has been introduced to me. I interested in using the pattern for WPF, so I decided to find a same way for WinForms too, but I was always too busy to work on it. One day, I tried to impose MVVM on WinForms, and here is the result of 3 spare hours working on the subject.

A very fast and short MVVM review

MVVM means Model-View-ViewModel:

MVVM

1. View: It refers to the elements that displayed by the UI. As some people say, View is UI and some others say that View is UI "but" Logic (I like the second one). And also, it passes commands to ViewModel.

2. ViewModel: It's an abstraction of the View, or mediation between View and Model. It passes Data between View and Model.

3. Model: It represents data from DAL (Data Access Layer) or a Domain Model. So, Model is not DAL but refers to it.

Using the code

View: I need two forms (MainForm and ViewForm). MainForm contains a TextBox (NameForAdding) and a Button (Add). ListForm contains only one DataGridView.

OK, there is not any code behind of MainForm:

public partial class MainForm : Form
{
}

As you know, I used a partial class in a separated assembly (MainForm.CodeBehind.cs) for it:

public partial class MainForm
{
	//fields
        private ViewModel viewModel;

        //Constructor
        public MainForm()
        {
            InitializeComponent();
            this.InitialControlHandlers();
        }                

        //Defining all Control handler methods       
        private void InitialControlHandlers()
        {
            this.viewModel = new ViewModel();        
            ListForm listForm = new ListForm()
            {
                ViewModel = this.viewModel
            };
            
            //Defines the listForm as an owned form
            this.AddOwnedForm(listForm);            

            //Defines: if ListForm closes, the MainForm will close too
            listForm.FormClosing+=new FormClosingEventHandler(
                (object sender, FormClosingEventArgs e) =>
                {
                    listForm.Dispose();
                    this.Close();
                });           

            //If the end user press Enter key, it will be like (s)he clicked on Add button. 
            this.NameForAdding.KeyPress += new KeyPressEventHandler(
                (object sender, KeyPressEventArgs e) =>
                {
                    if (e.KeyChar == (char)13)
                        this.AddToList();
                });

            this.Add.Click += new EventHandler(
                (object sender, EventArgs e) =>
                {
                    AddToList();
                }
                );

            //Defining a command for this button. It will be un-boxed to execute.
            this.Add.Tag = viewModel.AddToListCommand;

            //Defining a command for TextChanged. It will be un-boxed to execute.
            this.NameForAdding.Tag = viewModel.TextChangedCommand;

            //Binding the Text property of ViewModel to TextBox
            BindingSource binding = new BindingSource();            
            binding.DataSource = viewModel.Text;
            this.NameForAdding.DataBindings.Add(new Binding("Text", viewModel, "Text"));

            //Showing ListForm at finishing  the form load.
            listForm.Show();
        }
        private void AddToList()
        {
	    viewModel.Execute(this.Add.Tag, null);

            //After passing the value of the TextBox, it will be empty.
            this.NameForAdding.Text = string.Empty;

            this.NameForAdding.Focus();
        }
}

All events, methods and etc of the Form, are defined in the partial class.

ViewModel: 

public class ViewModel : INotifyPropertyChanged
{
        //Properties
        public string Text { get; set; }

        private ICommand _addToListCommand;
        public ICommand AddToListCommand
        {
            get
            {
                if (_addToListCommand == null)
                    _addToListCommand = new AddToList(this);
                return _addToListCommand;
            }
        }

        private ICommand _textChangedCommand;
        public ICommand TextChangedCommand
        {
            get
            {
                if (_textChangedCommand == null)
                    _textChangedCommand = new TextChanged(this);
                return _textChangedCommand;
            }
        }

        private ObservableCollection<StringValue> _nameList;
        public ObservableCollection<StringValue> NameList
        {
            get
            {
                return _nameList;
            }

            set
            {
                _nameList = value;
                _nameList.CollectionChanged += 
                        new System.Collections.Specialized.NotifyCollectionChangedEventHandler
                                                   (MyProperty_CollectionChanged);
            }
        } 

        //PropeetyChanged Handlers
        void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.NameList = 
                  (ObservableCollection<StringValue>)sender; 
                               //For Geting any new Changes in Entity 
        }

        void MyProperty_CollectionChanged
               (object sender, 
                      System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            {
                NotifyPropertyChanged("NameList");
            }
        }

        public void NotifyPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }        

        //INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;        

        //Methods
        public void Execute(object sender, object parameter)
        {
            ((ICommand)sender).Execute(parameter);
        }

        //...
        //For more information about the complete code, download the Source Code 
 

And its constructor: 

public ViewModel()
{
      Model model = new Model();
      model.PropertyChanged += new PropertyChangedEventHandler(model_PropertyChanged);
      //...
}

And 2 command classes that are used in UI and ViewModel:

public class AddToList : ICommand
{
    //Constructor
    public AddToList(ViewModel viewModel)
    {
        this.ViewModel = viewModel;
    }

    //Properties
    public ViewModel ViewModel { get; set; }

    //ICommand Members
    public void Execute(object sender)
    {
        if (string.IsNullOrEmpty(this.ViewModel.Text))
            return;
        if (string.IsNullOrEmpty(this.ViewModel.Text.Trim()))
            return;
        this.ViewModel.NameList.Add(new StringValue(this.ViewModel.Text));
    }    
}

public class TextChanged : ICommand
{
    //Constructor
    public TextChanged(ViewModel viewModel)
    {
        this.DataAccess = viewModel;
    }

    //Properties
    public ViewModel DataAccess { get; set; }

    //ICommand Members
    public void Execute(object sender)
    {
        this.DataAccess.Text = sender.ToString();
    }    
}

Model:

public class Model:INotifyPropertyChanged
{
   public ObservableCollection<StringValue> List = new ObservableCollection<StringValue>();

   private DataAccess dal = new DataAccess();

   public Model()
   {
      this.List = dal.Select();

      dal.DatabaseUpdated += new DataAccess.UpdateHandler(UpdataFromDal);

      this.List.CollectionChanged += 
                      new System.Collections.Specialized.NotifyCollectionChangedEventHandler
                                                                       (List_CollectionChanged);
   }

   void List_CollectionChanged(object sender, 
                            System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
   {
      dal.Update(this.List);
   }

   private void UpdataFromDal(ObservableCollection<StringValue> list)
   {
      this.List = list;

      List.CollectionChanged += 
                      new System.Collections.Specialized.NotifyCollectionChangedEventHandler
                                                                       (List_CollectionChanged);

      if (PropertyChanged != null)
         PropertyChanged(List, null);
   }

   #region INotifyPropertyChanged Members

      public event PropertyChangedEventHandler PropertyChanged;

   #endregion
}

OK, and what about ListForm? ListForm has a DataGridView and a Property as ViewModel
I bound DataGridView to ViewModel.NameList:

private void BindDataGridView()
{
   BindingSource binding = new BindingSource();
   binding.DataSource = ViewModel.NameList;
   this.DataGridView.DataSource = binding;
}

void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
   BindDataGridView(); 
}

And in form Load event:

ViewModel.PropertyChanged += new PropertyChangedEventHandler(ViewModel_PropertyChanged);

Illustrating generalities

If I want to use the "Inverted Pyramid" illlustrating:

  • In the example, The end user is able to enter a name in the main form (MainForm) for many times, and seeing the names that are adding to the GirdView of the other form (ListForm)
  • The end user is also confronting with some new names in ListForm that (s)he has never added them, like when another users are updating the list (just a simulating)
  • Adding proccess in the "end user"'s observation is 1.Filling the TextBox. 2. Click on Add (Button) 3. Watching the result in ListForm.
  • In behind, When the TextBox (NameForAdding) changes, Text property of ViewModel will change (because of binding) and when the Button (Add) is clicked, a command will fire to add Text to List of Model. After it, Model will synchronize it's List with database (using DataAccess). If it will be any changes in List, then NameList of ViewModel will update and any updates in NameList, re-bind the GridView in ListForm.
  • 2-Ways Binding is not available in WinForm, so I've implemented INotifyPropertyChanged and used NotifyCollectionChangedEventHandler.

Sequence Diagram

Answering Obscurities

What is StringValue? A string object has only one public Property (Length) and if you bind a string collection to the DataGridView, it will only show the lengths. StringValue is a defined class and I created a Collection of StringValue to be shown in the DataGirdView.

public class StringValue
{
   public string Value { get; set; }
   public StringValue(string value)
   {
      this.Value = value;
   }
}

What is ICommand? It's an interface that I created, imitating WPF build-in ICommand Interface:

public interface ICommand
{
   void Execute(object sender);
}

I set the Tag of the controls to commands (In partial class)

this.Add.Tag = viewModel.AddToListCommand;
this.NameForAdding.Tag = viewModel.TextChangedCommand;

The tags will be un-boxed when it's going to execute its command:

public void Execute(object sender, object parameter)
{
   ((ICommand)sender).Execute(parameter);
}

Why didn't I expound enough about ViewModel class? Because, it's almost like View-Model in WPF and there are enough sites, books, articles and etc to refer. (A too long article because of untimelies).

Why does Model seem a little strange? Model uses DataAcess class simulating it's working with a database (but it isn't). All about Model is, it's a gate between Presentation and DAL, in the meanwhile, it has implemented INotifyPropertyChanged to notify ViewModel about entity updates.

What does DataAccess do? DataAccess class is not a part of the pattern, I created it to fake an accessing to database. It's also generating some random names to simulate concurrency updates. (Please download demo and source code.)

Anything else?
Please help! If you face any ambiguity in this writing, please notify me, and don't hesitate to ask question. It helps me to improve the article.

Thank you.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Shahin Khorshidnia
Software Developer (Senior)
Iran (Islamic Republic Of) Iran (Islamic Republic Of)
Microsoft Certified Technology Specialist (MCTS)
 

"شاهین خورشیدنیا"
Follow on   LinkedIn

Comments and Discussions

 
GeneralGood Stuff! PinmemberMember 109339929-Jul-14 20:20 
SuggestionMVVM Pattern & suggested improvements PinmemberFredrik Claesson28-Aug-13 21:53 
GeneralAlready tried.... With better bindings and events... There are limitations in winforms that prevent a serious implementation PinmemberJorge Varas13-May-13 9:24 
GeneralRe: Already tried.... With better bindings and events... There are limitations in winforms that prevent a serious implementation PinmemberShahin Khorshidnia13-May-13 18:14 
GeneralMy vote of 2 PinmemberAndyVGa10-May-13 17:27 
SuggestionBad project name and bad rely of dlls. Pinmemberleiyang-ge8-May-13 22:54 
GeneralThank you very much [modified] PinmemberShahin Khorshidnia9-May-13 1:54 
GeneralMy vote of 5 Pinmemberehsan.vc2-Jan-13 0:46 
GeneralRe: My vote of 5 PinmemberShahin Khorshidnia2-Jan-13 7:12 
GeneralMy vote of 5 PinmemberB.Farivar16-Sep-12 1:37 
GeneralRe: My vote of 5 PinmemberShahin Khorshidnia16-Sep-12 1:59 
QuestionMVVM FX [modified] PinmemberTiago Freitas Leal13-Jun-12 12:21 
AnswerRe: MVVM FX PinmemberShahin Khorshidnia13-Jun-12 18:36 
GeneralRe: MVVM FX PinmemberTiago Freitas Leal13-Jun-12 20:38 
By the way, nice to read your post.
GeneralRe: MVVM FX PinprofessionalBrisingr Aerowing10-Mar-14 14:56 
Questionprimary goal Pinmembertomas oplt15-May-12 4:38 
AnswerRe: primary goal PinmemberShahin Khorshidnia15-May-12 6:21 
GeneralMy vote of 5 PinmemberMonjurul Habib10-May-12 19:29 
GeneralRe: My vote of 5 PinmemberShahin Khorshidnia7-Aug-12 8:26 
Generalmy vote of 5 PinmemberUday P.Singh5-May-12 0:54 
GeneralRe: my vote of 5 PinmemberShahin Khorshidnia5-May-12 1:21 
GeneralMy vote of 5 PinmemberAUDIBMW27-Apr-12 21:55 
GeneralRe: My vote of 5 PinmemberShahin Khorshidnia29-Apr-12 3:46 
GeneralMy Vote of 5 PinmemberBITA Moin26-Apr-12 6:01 
GeneralRe: My Vote of 5 PinmemberShahin Khorshidnia26-Apr-12 6:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 9 May 2013
Article Copyright 2012 by Shahin Khorshidnia
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid