Sample application using MVVM in WPF

As we have already learned the basics of MVVM pattern in previous session, Lets  now understood each of them in practical by creating a sample application.

Lets create a sample WPF application, below are the steps:


1. Open visual studio and click on new project and select Windows under c# and select WPF Application from the list in center, click ok and you have created a WPF Project.



2. Now, we will focus on the View, ViewModel and Inotify , Relay commands and see how they work in practical.

3. Windows are Usercontrols that contains XAML part comes under VIEW category.In above created project you can see the Default MainWindow, this is View for our project or any other window or User control that you will be adding to project would come under View.



4. Lets add a “TextBlock” and a “Button” on Main window using below XAML:

<Window x:Class="WpfApplication1.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:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>

        <TextBlock Width="100" Height="100"  VerticalAlignment="Center"/>
        <Button 
                            IsDefault="True"
                            Content="Change Text" Width="100" Height="100" VerticalAlignment="Bottom"
                            >

        </Button>
    </Grid>
</Window>

So the design should look like below image:

5. Lets Create a ViewModel for Main Window:

Add a folder named as “ViewModel” in solution and add a class named as “MainWindowViewModel.cs” under this file.

6. Now, How would View “MainWindow” know that the ViewModel “MainWindowViewModel.cs” is associated to it or created to bind the properties for this View.For that we use DataContext.
How do we use that?

Go to cs page of MainWindow and Write the below line of code below the InitializeComponent() in the constructor of MainWindow:


     public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }

From above line of code in constructor our View would come to know that which View it should look for to get Data and Commands.

7. Lets move to ViewModel Part now, As we need to inheriet InotifyPropertyChanged to manage the commands and change in properties, so instead of doing it everytime in every View, we should create a baseView.

Add a class under View folder and name it as ViewModelBase , now solution should look like:

8. Define ViewModelBase as Abstract class and inheriet it from InotifyChanged as below:

NameSpace for InotifiyPropertyChanged:

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

   public abstract class ViewModelBase : INotifyPropertyChanged
    {

        public ViewModelBase()
        {
IntiliazedViewModel();
        }

  /// <summary>
        /// Performs intial loading of any view model specific objects
        /// </summary>
        protected virtual void IntiliazedViewModel()
        {

        }
  /// <summary>
        /// Used to initilize and/or load any collections from within the constructor
        /// </summary>
        public abstract void IntitializeCollections();
    }


9. Define the get, set for property values in base,Please read the comment above code block to know what code exactly used for:

public abstract class ViewModelBase : INotifyPropertyChanged
    {

        public ViewModelBase()
        {
IntiliazedViewModel();
        }

  /// <summary>
        /// Performs intial loading of any view model specific objects
        /// </summary>
        protected virtual void IntiliazedViewModel()
        {

        }
  /// <summary>
        /// Used to initilize and/or load any collections from within the constructor
        /// </summary>
        public abstract void IntitializeCollections();

/// <summary>
        /// Sets the value of a property.
        /// </summary>
        /// <typeparam name="T">The type of the property value.</typeparam>
        /// <param name="propertySelector">Expression tree contains the property definition.</param>
        /// <param name="value">The property value.</param>
        protected void SetValue<T>(Expression<Func<T>> propertySelector, T value)
        {
            string propertyName = GetPropertyName(propertySelector);
            SetValue(propertyName, value);
        }

        /// <summary>
        /// Sets the value of a property.
        /// </summary>
        /// <typeparam name="T">The type of the property value.</typeparam>
        /// <param name="propertyName">The name of the property.</param>
        /// <param name="value">The property value.</param>
        protected void SetValue<T>(string propertyName, T value)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentException("Invalid_PropertyName", propertyName);
            }

            if (_values == null)
            {
                _values = new Dictionary<string, object>();
            }
            if (!(_values.ContainsKey(propertyName) && _values[propertyName] != null && _values[propertyName].Equals(value)))
            {
                _values[propertyName] = value;
                NotifyPropertyChanged(propertyName);
            }
        }

      /// <summary>
        /// Gets the value of a property.
        /// </summary>
        /// <typeparam name="T">The type of the property value.</typeparam>
        /// <param name="propertySelector">Expression tree contains the property definition.</param>
        /// <returns>The value of the property or default value if not exist.</returns>
        protected T GetValue<T>(Expression<Func<T>> propertySelector)
        {
            string propertyName = GetPropertyName(propertySelector);
            return GetValue<T>(propertyName);
        }

        /// <summary>
        /// Gets the value of a property.
        /// </summary>
        /// <typeparam name="T">The type of the property value.</typeparam>
        /// <param name="propertyName">The name of the property.</param>
        /// <returns>The value of the property or default value if not exist.</returns>
        protected T GetValue<T>(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException("propertyName", "Invalid_PropertyName");
            }

            object value;

            if (_values == null)
            {
                _values = new Dictionary<string, object>();
            }

            if (!_values.TryGetValue(propertyName, out value))
            {
                value = default(T);
                _values.Add(propertyName, value);
            }

            return (T)value;
        }
    }


Now above code is ready to get and set property values of any Viewmodel, but we need to notify when property gets changed.Lets write line of code for PropertyChanged Events and Delegates and add the below lines of code to the ViewModelBase.

   /// <summary>
        /// Raised when a property on this object has a new value.
        /// </summary>
        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises this object's PropertyChanged event.
        /// </summary>
        /// <param name="propertyName">The property that has a new value.</param>
        protected void NotifyPropertyChanged(string propertyName)
        {
            VerifyPropertyName(propertyName);

            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler == null)
                return;

            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }

        protected void NotifyPropertyChanged<T>(Expression<Func<T>> propertySelector)
        {
            PropertyChangedEventHandler propertyChanged = PropertyChanged;

            if (propertyChanged == null)
                return;

            string propertyName = GetPropertyName(propertySelector);
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
  /// <summary>
        /// Warns the developer if this object does not have
        /// a public property with the specified name. This
        /// method does not exist in a Release build.
        /// </summary>
        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public void VerifyPropertyName(string propertyName)
        {
            if (TypeDescriptor.GetProperties(this)[propertyName] != null)
                return;

            string msg = "Invalid property name: " + propertyName;
            throw new ArgumentException(msg);
        }

Now , we are all set to use the ViewModelBase for other ViewModels.

Lets see how to bind properties and commands, go to MainWindowViewModel.cs:
 public class MainWindowViewModel : ViewModelBase
    {
   /// <summary>
        /// It will thefirst thing to run when ever a ViewModel is called.
        /// </summary>
        public override void IntitializeCollections()
        {
        }
    }
Lets define the properties to bind the View Controls:
public class MainWindowViewModel : ViewModelBase
    {
   /// <summary>
        /// It will thefirst thing to run when ever a ViewModel is called.
        /// </summary>
        public override void IntitializeCollections()
        {
        }

   public string GetText
        {
            get { return GetValue(() => this.GetText); }
            set { SetValue(() => this.GetText, value); }
        }
}

Now go to MainWindow View and bind the text block with GetText Property:

now textblock should look like as below:
  <TextBlock Text="{Binding GetText,UpdateSourceTrigger=PropertyChanged}" Width="100" Height="100"  VerticalAlignment="Center"/>

now Text if bound to GetText and will Update on PropertyChanged Event.

Lets Call the GetText in IntitializeCollections or you can do it in ViewModelconstructor also to get the text in textbox:

public class MainWindowViewModel : ViewModelBase
    {
   /// <summary>
        /// It will thefirst thing to run when ever a ViewModel is called.
        /// </summary>
        public override void IntitializeCollections()
        {
GetText=”Hiiiiiiii”;

        }

   public string GetText
        {
            get { return GetValue(() => this.GetText); }
            set { SetValue(() => this.GetText, value); }
        }
}

Now Run the application and text block should display the text as “Hiiiiiiii” on Main window. See the below image for result:



Try to click on change Text, but nothing will get changed.

So lets now move to Command and Inotify:

So , what do we need is when we click on the Change Text button, text should get to changed what we have written on the click of this button, but How do we define the click for the button in MVVM is using Relay commands:

Go to ViewModel again and write the commands to bind the button click:

   private RelayCommand m_ChangeText;
        public RelayCommand ChangeTextCommand
        {
            get
            {
                return m_ChangeText ?? (m_ChangeText = new RelayCommand(
                    ve => ChangeText(), (t) => true));
            }
        }

  public void ChangeText()
        {
            GetText = "How are you?";
        }

Define the above commands in MainWindowViewModel and MainWindowViewModel should look like:

namespace WpfApplication1.ViewModel
{
    public class MainWindowViewModel : ViewModelBase
    {
        /// <summary>
        /// It will thefirst thing to run when ever a ViewModel is called.
        /// </summary>
        public override void IntitializeCollections()
        {
            GetText = "Hiiiiiiii";
        }

        public string GetText
        {
            get { return GetValue(() => this.GetText); }
            set { SetValue(() => this.GetText, value); }
        }

        private RelayCommand m_ChangeText;
        public RelayCommand ChangeTextCommand
        {
            get
            {
                return m_ChangeText ?? (m_ChangeText = new RelayCommand(
                    ve => ChangeText(), (t) => true));
            }
        }

        public void ChangeText()
        {
            GetText = "How are you?";
        }
    }
}

Now Bind the button click to “ChangeTextCommand” to get the text changed.

Now again go to MainWindow.Xaml, and button now should look like:

<Button 
                            IsDefault="True"
                            Content="Change Text" Width="100" Height="100" VerticalAlignment="Bottom"
                            Command="{Binding Path=ChangeTextCommand}">

        </Button>

So button command is bind to “ChangeTextCommand”, when ever we click on this button ,the ChangeTextCommand command will get fired and change the value of GetText property and InotifyChangedEvent will get fired and UpdateSourceTrigger would let the control to update the value on PropertyChanged.

Now run the application and get the result:


In Next article we will understand how to fire other events such as Mousedoubleclick, dropdownselected events and many more for Xaml controls using relayCommand.

Thank you.






Comments

  1. protected void SetValue(Expression> propertySelector, T value)
    {
    string propertyName = GetPropertyName(propertySelector);
    SetValue(propertyName, value);
    }
    i got the following error:
    The name 'GetPropertyName' does not exist in the current context

    ReplyDelete

Post a Comment

Popular Posts

How to position controls using Canvas control in WPF using MVVM

Change font and size in visual studio

JWTs in ASP.NET Core 2.0