MVVM â Unit Testing ”; Previous Next The idea behind unit testing is to take discrete chunks of code (units) and write test methods that use the code in an expected way, and then test to see if they get the expected results. Being code themselves, unit tests are compiled just like the rest of the project. They are also executed by the test-running software, which can speed through each test, effectively giving the thumbs up or thumbs down to indicate whether the test has passed or failed, respectively. Letâs take a look at an example created earlier. Following is the implementation of Student Model. using System.ComponentModel; namespace MVVMDemo.Model { public class StudentModel {} public class Student : INotifyPropertyChanged { private string firstName; private string lastName; public string FirstName { get { return firstName; } set { if (firstName != value) { firstName = value; RaisePropertyChanged(“FirstName”); RaisePropertyChanged(“FullName”); } } } public string LastName { get { return lastName; } set { if (lastName != value) { lastName = value; RaisePropertyChanged(“LastName”); RaisePropertyChanged(“FullName”); } } } public string FullName { get { return firstName + ” ” + lastName; } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } } } Following is the implementation of StudentView. <UserControl x:Class=”MVVMDemo.Views.StudentView” xmlns = “http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x = “http://schemas.microsoft.com/winfx/2006/xaml” xmlns:mc = “http://schemas.openxmlformats.org/markup-compatibility/2006” xmlns:d = “http://schemas.microsoft.com/expression/blend/2008” xmlns:local = “clr-namespace:MVVMDemo.Views” xmlns:viewModel = “clr-namespace:MVVMDemo.ViewModel” xmlns:data = “clr-namespace:MVVMDemo.Model” xmlns:vml = “clr-namespace:MVVMDemo.VML” vml:ViewModelLocator.AutoHookedUpViewModel = “True” mc:Ignorable = “d” d:DesignHeight = “300” d:DesignWidth = “300”> <UserControl.Resources> <DataTemplate DataType = “{x:Type data:Student}”> <StackPanel Orientation = “Horizontal”> <TextBox Text = “{Binding Path = FirstName, Mode = TwoWay}” Width = “100” Margin = “3 5 3 5″/> <TextBox Text = “{Binding Path = LastName, Mode = TwoWay}” Width = “100” Margin = “0 5 3 5″/> <TextBlock Text = “{Binding Path = FullName, Mode = OneWay}” Margin = “0 5 3 5″/> </StackPanel> </DataTemplate> </UserControl.Resources> <Grid> <StackPanel Orientation = “Horizontal”> <ListBox ItemsSource = “{Binding Students}” SelectedItem = “{Binding SelectedStudent}”/> <Button Content = “Delete” Command = “{Binding DeleteCommand}” HorizontalAlignment = “Left” VerticalAlignment = “Top” Width = “75” /> </StackPanel> </Grid> </UserControl> Following is the StudentViewModel implementation. using MVVMDemo.Model; using System.Collections.ObjectModel; using System.Windows.Input; using System; namespace MVVMDemo.ViewModel { public class StudentViewModel { public MyICommand DeleteCommand { get; set;} public StudentViewModel() { LoadStudents(); DeleteCommand = new MyICommand(OnDelete, CanDelete); } public ObservableCollection<Student> Students { get; set; } public void LoadStudents() { ObservableCollection<Student> students = new ObservableCollection<Student>(); students.Add(new Student { FirstName = “Mark”, LastName = “Allain” }); students.Add(new Student { FirstName = “Allen”, LastName = “Brown” }); students.Add(new Student { FirstName = “Linda”, LastName = “Hamerski” }); Students = students; } private Student _selectedStudent; public Student SelectedStudent { get { return _selectedStudent; } set { _selectedStudent = value; DeleteCommand.RaiseCanExecuteChanged(); } } private void OnDelete() { Students.Remove(SelectedStudent); } private bool CanDelete() { return SelectedStudent != null; } public int GetStudentCount() { return Students.Count; } } } Following is the MainWindow.xaml file. <Window x:Class = “MVVMDemo.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:MVVMDemo” xmlns:views = “clr-namespace:MVVMDemo.Views” mc:Ignorable = “d” Title = “MainWindow” Height = “350” Width = “525”> <Grid> <views:StudentView x:Name = “StudentViewControl”/> </Grid> </Window> Following is the MyICommand implementation, which implements the ICommand interface. using System; using System.Windows.Input; namespace MVVMDemo { public class MyICommand : ICommand { Action _TargetExecuteMethod; Func<bool> _TargetCanExecuteMethod; public MyICommand(Action executeMethod) { _TargetExecuteMethod = executeMethod; } public MyICommand(Action executeMethod, Func<bool> canExecuteMethod) { _TargetExecuteMethod = executeMethod; _TargetCanExecuteMethod = canExecuteMethod; } public void RaiseCanExecuteChanged() { CanExecuteChanged(this, EventArgs.Empty); } bool ICommand.CanExecute(object parameter) { if (_TargetCanExecuteMethod != null) { return _TargetCanExecuteMethod(); } if (_TargetExecuteMethod != null) { return true; } return false; } // Beware – should use weak references if command instance lifetime is longer than lifetime of UI objects that get hooked up to command // Prism commands solve this in their implementation public event EventHandler CanExecuteChanged = delegate { }; void ICommand.Execute(object parameter) { if (_TargetExecuteMethod != null) { _TargetExecuteMethod(); } } } } When the above code is compiled and executed, you will see the following output on your main window. To write a unit test for the above example, letâs add a new Test Project to the Solution. Add reference to the project by a right-click on References. Select the existing project and click Ok. Let us now add a simple Test which will check the Student Count as shown in the following code. using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using MVVMDemo.ViewModel; namespace MVVMTest { [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { StudentViewModel sViewModel = new StudentViewModel(); int count = sViewModel.GetStudentCount(); Assert.IsTrue(count == 3); } } } To execute this test, select Test → Run → All Tests menu option. You can see in the Test Explorer that the Test is Passed, because in the StudentViewModel, three students are added. Change the count condition from 3 to 4 as shown in the following code. using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using MVVMDemo.ViewModel; namespace MVVMTest { [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { StudentViewModel sViewModel = new StudentViewModel(); int count = sViewModel.GetStudentCount(); Assert.IsTrue(count == 4); } } } When the test plan is executed again, you will see that that the test failed because student count is not equal to 4. We recommend you to execute the above example in a step-by-step method for better understanding. Print Page Previous Next Advertisements ”;
Category: mvvm
MVVM â View / ViewModel Communication ”; Previous Next In this chapter, we will learn how to add interactivity to MVVM applications and how to cleanly call logic. You will also see that all of this is done by maintaining the loose coupling and good structuring which is the heart of the MVVM pattern. To understand all this, first let us learn about commands. View / ViewModel Communication via Commands The command pattern has been well documented and frequently uses design pattern for a couple of decades. In this pattern there are two main actors, the invoker and the receiver. Invoker The invoker is a piece of code which can execute some imperative logic. Typically, it is a UI element that the user interacts with, in the context of a UI framework. It could just be another chunk of logic code somewhere else in the application. Receiver The receiver is the logic that is intended for execution when the invoker fires. In the context of MVVM, the receiver is typically a method in your ViewModel that needs to be called. In between these two, you have an obstruction layer, which implies the invoker and receiver do not have to explicitly know about each other. This is typically represented as an interface abstraction exposed to the invoker and a concrete implementation of that interface is capable of calling the receiver. Letâs have a look into a simple example in which you will learn the commands and how to use them to communicate Between View and ViewModel. In this chapter, we will continue with the same example from the last chapter. In StudentView.xaml file, we have a ListBox which hooks up the student data from a ViewModel. Now letâs add a button for deleting a student from the ListBox. The important thing is that working with commands on button is very easy because they have a command property to hook up to an ICommand. So, we can expose a property on our ViewModel that has an ICommand and binds to it from the buttonâs command property as shown in the following code. <Button Content = “Delete” Command = “{Binding DeleteCommand}” HorizontalAlignment = “Left” VerticalAlignment = “Top” Width = “75” /> Letâs add a new class in your project, which will implement the ICommand interface. Following is the implementation of ICommand interface. using System; using System.Windows.Input; namespace MVVMDemo { public class MyICommand : ICommand { Action _TargetExecuteMethod; Func<bool> _TargetCanExecuteMethod; public MyICommand(Action executeMethod) { _TargetExecuteMethod = executeMethod; } public MyICommand(Action executeMethod, Func<bool> canExecuteMethod){ _TargetExecuteMethod = executeMethod; _TargetCanExecuteMethod = canExecuteMethod; } public void RaiseCanExecuteChanged() { CanExecuteChanged(this, EventArgs.Empty); } bool ICommand.CanExecute(object parameter) { if (_TargetCanExecuteMethod != null) { return _TargetCanExecuteMethod(); } if (_TargetExecuteMethod != null) { return true; } return false; } // Beware – should use weak references if command instance lifetime is longer than lifetime of UI objects that get hooked up to command // Prism commands solve this in their implementation public event EventHandler CanExecuteChanged = delegate { }; void ICommand.Execute(object parameter) { if (_TargetExecuteMethod != null) { _TargetExecuteMethod(); } } } } As you can see, this is a simple delegating implementation of ICommand where we have two delegates one for the executeMethod and one for the canExecuteMethod which can be passed in on construction. In the above implementation, there are two overloaded constructors, one for only executeMethod and one for both executeMethod and I can canExecuteMethod. Letâs add a property of MyICommand type in StudentView Model class. Now we need to construct an instance in the StudentViewModel. We will use the overloaded constructor of MyICommand that takes two parameters. public MyICommand DeleteCommand { get; set;} public StudentViewModel() { LoadStudents(); DeleteCommand = new MyICommand(OnDelete, CanDelete); } Now add the implementation of OnDelete and CanDelete methods. private void OnDelete() { Students.Remove(SelectedStudent); } private bool CanDelete() { return SelectedStudent != null; } We also need to add a new SelectedStudent so that the user can delete the Selected Item from ListBox. private Student _selectedStudent; public Student SelectedStudent { get { return _selectedStudent; } set { _selectedStudent = value; DeleteCommand.RaiseCanExecuteChanged(); } } Following is the complete implementation of ViewModel class. using MVVMDemo.Model; using System.Collections.ObjectModel; using System.Windows.Input; using System; namespace MVVMDemo.ViewModel { public class StudentViewModel { public MyICommand DeleteCommand { get; set;} public StudentViewModel() { LoadStudents(); DeleteCommand = new MyICommand(OnDelete, CanDelete); } public ObservableCollection<Student> Students { get; set; } public void LoadStudents() { ObservableCollection<Student> students = new ObservableCollection<Student>(); students.Add(new Student { FirstName = “Mark”, LastName = “Allain” }); students.Add(new Student { FirstName = “Allen”, LastName = “Brown” }); students.Add(new Student { FirstName = “Linda”, LastName = “Hamerski” }); Students = students; } private Student _selectedStudent; public Student SelectedStudent { get { return _selectedStudent; } set { _selectedStudent = value; DeleteCommand.RaiseCanExecuteChanged(); } } private void OnDelete() { Students.Remove(SelectedStudent); } private bool CanDelete() { return SelectedStudent != null; } } } In StudentView.xaml, we need to add SelectedItem property in a ListBox which will bind to the SelectStudent property. <ListBox ItemsSource = “{Binding Students}” SelectedItem = “{Binding SelectedStudent}”/> Following is the complete xaml file. <UserControl x:Class = “MVVMDemo.Views.StudentView” xmlns = “http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x = “http://schemas.microsoft.com/winfx/2006/xaml” xmlns:mc = “http://schemas.openxmlformats.org/markup-compatibility/2006” xmlns:d = “http://schemas.microsoft.com/expression/blend/2008” xmlns:local = “clr-namespace:MVVMDemo.Views” xmlns:viewModel = “clr-namespace:MVVMDemo.ViewModel” xmlns:data = “clr-namespace:MVVMDemo.Model” xmlns:vml = “clr-namespace:MVVMDemo.VML” vml:ViewModelLocator.AutoHookedUpViewModel = “True” mc:Ignorable = “d” d:DesignHeight = “300” d:DesignWidth = “300”> <UserControl.Resources> <DataTemplate DataType = “{x:Type data:Student}”> <StackPanel Orientation = “Horizontal”> <TextBox Text = “{Binding Path = FirstName, Mode = TwoWay}” Width = “100” Margin = “3 5 3 5″/> <TextBox Text = “{Binding Path = LastName, Mode = TwoWay}” Width = “100” Margin = “0 5 3 5″/> <TextBlock Text = “{Binding Path = FullName, Mode = OneWay}” Margin = “0 5 3 5″/> </StackPanel> </DataTemplate> </UserControl.Resources> <Grid> <StackPanel Orientation = “Horizontal”> <ListBox ItemsSource = “{Binding Students}” SelectedItem = “{Binding SelectedStudent}”/> <Button Content = “Delete” Command = “{Binding DeleteCommand}” HorizontalAlignment = “Left” VerticalAlignment = “Top” Width = “75” /> </StackPanel> </Grid> </UserControl> When the above code is compiled and executed, you will see the following window. You can see that
MVVM – Discussion
Discuss MVVM ”; Previous Next Every good developer wants and tries to create the most sophisticated applications to delight their users. Most of the times, developers achieve this on the first release of the application. However, with new feature addition, fixing the bug without putting a lot of consideration into the structure of the application code becomes difficult due to code complexity. For this, there is a need for good clean structure of code. In this tutorial, you will learn how to reduce code complexity and how to maintain a clean and reusable structure of your code by using MVVM pattern. Print Page Previous Next Advertisements ”;
MVVM – WPF Data Templates
MVVM â WPF Data Templates ”; Previous Next A template describes the overall look and visual appearance of the control. For each control there is a default template associated with it which gives the appearance to that control. In WPF application, you can easily create your own templates when you want to customize the visual behavior and visual appearance of a control. Connectivity between the logic and template can be achieved by data binding. In MVVM, there is another primary form which is known as ViewModel first construction. ViewModel first construction approach leverages the capabilities of implicit data templates in WPF. Implicit data templates can automatically select an appropriate template from the current resource dictionary for an element that uses data binding. They do this based on the type of data object which is rendered by data binding. First, you need to have some element that is binding to a data object. Letâs take a look at our simple example again in which you will understand how you can do view model first leveraging data templates, specifically implicit data templates. Here is the implementation of our StudentViewModel class. using MVVMDemo.Model; using System.Collections.ObjectModel; namespace MVVMDemo.ViewModel { public class StudentViewModel { public StudentViewModel() { LoadStudents(); } public ObservableCollection<Student> Students { get; set; } public void LoadStudents() { ObservableCollection<Student> students = new ObservableCollection<Student>(); students.Add(new Student { FirstName = “Mark”, LastName = “Allain” }); students.Add(new Student { FirstName = “Allen”, LastName = “Brown” }); students.Add(new Student { FirstName = “Linda”, LastName = “Hamerski” }); Students = students; } } } You can see that the above ViewModel is unchanged. We will continue with same example from the previous chapter. This ViewModel class just exposes the Students collection property and populates it on construction. Let”s go to the StudentView.xaml file, remove the existing implementation and define a data template in Resources section. <UserControl.Resources> <DataTemplate x:Key = “studentsTemplate”> <StackPanel Orientation = “Horizontal”> <TextBox Text = “{Binding Path = FirstName, Mode = TwoWay}” Width = “100” Margin = “3 5 3 5″/> <TextBox Text = “{Binding Path = LastName, Mode = TwoWay}” Width = “100” Margin = “0 5 3 5″/> <TextBlock Text = “{Binding Path = FullName, Mode = OneWay}” Margin = “0 5 3 5″/> </StackPanel> </DataTemplate> </UserControl.Resources> Now add a list box and data bind that list box to Students property as shown in the following code. <ListBox ItemsSource = “{Binding Students}” ItemTemplate = “{StaticResource studentsTemplate}”/> In the Resource section, the DataTemplate has a key of studentsTemplate and then to actually use that template, we need to use the ItemTemplate property of a ListBox. So now you can see that we instruct the listbox to go use that specific template for rendering out those Students. Following is the complete implementation of StudentView.xaml file. <UserControl x:Class = “MVVMDemo.Views.StudentView” xmlns = “http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x = “http://schemas.microsoft.com/winfx/2006/xaml” xmlns:mc = “http://schemas.openxmlformats.org/markup-compatibility/2006” xmlns:d = “http://schemas.microsoft.com/expression/blend/2008” xmlns:local = “clr-namespace:MVVMDemo.Views” xmlns:viewModel = “clr-namespace:MVVMDemo.ViewModel” xmlns:vml = “clr-namespace:MVVMDemo.VML” vml:ViewModelLocator.AutoHookedUpViewModel = “True” mc:Ignorable = “d” d:DesignHeight = “300” d:DesignWidth = “300”> <UserControl.Resources> <DataTemplate x:Key = “studentsTemplate”> <StackPanel Orientation = “Horizontal”> <TextBox Text = “{Binding Path = FirstName, Mode = TwoWay}” Width = “100” Margin = “3 5 3 5″/> <TextBox Text = “{Binding Path = LastName, Mode = TwoWay}” Width = “100” Margin = “0 5 3 5″/> <TextBlock Text = “{Binding Path = FullName, Mode = OneWay}” Margin = “0 5 3 5″/> </StackPanel> </DataTemplate> </UserControl.Resources> <Grid> <ListBox ItemsSource = “{Binding Students}” ItemTemplate = “{StaticResource studentsTemplate}”/> </Grid> </UserControl> When the above code is compiled and executed, you will see the following window, which contains one ListBox. Each ListBoxItem contains the Student class object data which are displayed on TextBlock and Text boxes. To make this an implicit template, we need to remove the ItemTemplate property from a list box and add a DataType property in our template definition as shown in the following code. <UserControl.Resources> <DataTemplate DataType = “{x:Type data:Student}”> <StackPanel Orientation = “Horizontal”> <TextBox Text = “{Binding Path = FirstName, Mode = TwoWay}” Width = “100” Margin = “3 5 3 5″/> <TextBox Text = “{Binding Path = LastName, Mode = TwoWay}” Width = “100” Margin = “0 5 3 5″/> <TextBlock Text = “{Binding Path = FullName, Mode = OneWay}” Margin = “0 5 3 5″/> </StackPanel> </DataTemplate> </UserControl.Resources> <Grid> <ListBox ItemsSource = “{Binding Students}”/> </Grid> In DataTemplate, the x:Type markup extension is very important which is like a type of operator in XAML. So, basically we need to point to the Student data type which is in MVVMDemo.Model namespace. Following is the updated complete XAML file. <UserControl x:Class=”MVVMDemo.Views.StudentView” xmlns = “http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x = “http://schemas.microsoft.com/winfx/2006/xaml” xmlns:mc = “http://schemas.openxmlformats.org/markup-compatibility/2006” xmlns:d = “http://schemas.microsoft.com/expression/blend/2008” xmlns:local = “clr-namespace:MVVMDemo.Views” xmlns:viewModel = “clr-namespace:MVVMDemo.ViewModel” xmlns:data = “clr-namespace:MVVMDemo.Model” xmlns:vml = “clr-namespace:MVVMDemo.VML” vml:ViewModelLocator.AutoHookedUpViewModel = “True” mc:Ignorable = “d” d:DesignHeight = “300” d:DesignWidth = “300”> <UserControl.Resources> <DataTemplate DataType = “{x:Type data:Student}”> <StackPanel Orientation = “Horizontal”> <TextBox Text = “{Binding Path = FirstName, Mode = TwoWay}” Width = “100” Margin = “3 5 3 5″/> <TextBox Text = “{Binding Path = LastName, Mode = TwoWay}” Width = “100” Margin = “0 5 3 5″/> <TextBlock Text = “{Binding Path = FullName, Mode = OneWay}” Margin = “0 5 3 5″/> </StackPanel> </DataTemplate> </UserControl.Resources> <Grid> <ListBox ItemsSource = “{Binding Students}”/> </Grid> </UserControl> When you run this application again, you will still get the same rendering of the Students with data template because it”s automatically mapping the type of the object being rendered by locating the appropriate DataTemplate. We recommend you to execute the above example in a step-by-step method for better understanding. Print Page Previous Next Advertisements ”;
MVVM – Advantages
MVVM â Advantages ”; Previous Next MVVM pattern is ultimately the modern structure of the MVC pattern, so the main goal is still the same to provide a clear separation between domain logic and presentation layer. Here are some of the advantages and disadvantages of MVVM pattern. The key benefit is allowing true separation between the View and Model beyond achieving separation and the efficiency that you gain from having that. What that means in real terms is that when your model needs to change, it can be changed easily without the view needing to and vice-versa. There are three important key things that flow out of applying MVVM which are as follows. Maintainability A clean separation of different kinds of code should make it easier to go into one or several of those more granular and focused parts and make changes without worrying. That means you can remain agile and keep moving out to new releases quickly. Testability With MVVM each piece of code is more granular and if it is implemented right your external and internal dependences are in separate pieces of code from the parts with the core logic that you would like to test. That makes it a lot easier to write unit tests against a core logic. Make sure it works right when written and keeps working even when things change in maintenance. Extensibility It sometimes overlaps with maintainability, because of the clean separation boundaries and more granular pieces of code. You have a better chance of making any of those parts more reusable. It has also the ability to replace or add new pieces of code that do similar things into the right places in the architecture. The obvious purpose of MVVM pattern is abstraction of the View which reduces the amount of business logic in code-behind. However, following are some other solid advantages − The ViewModel is easier to unit test than code-behind or event-driven code. You can test it without awkward UI automation and interaction. The presentation layer and the logic is loosely coupled. Disadvantages Some people think that for simple UIs, MVVM can be overkill. Similarly in bigger cases, it can be hard to design the ViewModel. Debugging would be bit difficult when we have complex data bindings. Print Page Previous Next Advertisements ”;
MVVM – WPF Data Bindings
MVVM â WPF Data Bindings ”; Previous Next In this chapter, we will be learn how data binding supports the MVVM pattern. Data binding is the key feature that differentiates MVVM from other UI separation patterns like MVC and MVP. For data binding you need to have a view or set of UI elements constructed, and then you need some other object that the bindings are going to point to. The UI elements in a view are bound to the properties which are exposed by the ViewModel. The order that the View and ViewModel are constructed on depends on the situation, as we have covered the View first. A View and ViewModel get constructed and the DataContext of the View gets set to the ViewModel. Bindings can either be OneWay or TwoWay data bindings to flow data back and forth between the View and ViewModel. Let”s take a look at data bindings in the same example. Below is the XAML code of StudentView. <UserControl x:Class = “MVVMDemo.Views.StudentView” xmlns = “http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x = “http://schemas.microsoft.com/winfx/2006/xaml” xmlns:mc = “http://schemas.openxmlformats.org/markup-compatibility/2006” xmlns:d = “http://schemas.microsoft.com/expression/blend/2008” xmlns:local = “clr-namespace:MVVMDemo.Views” xmlns:viewModel = “clr-namespace:MVVMDemo.ViewModel” xmlns:vml = “clr-namespace:MVVMDemo.VML” vml:ViewModelLocator.AutoHookedUpViewModel = “True” mc:Ignorable = “d” d:DesignHeight = “300” d:DesignWidth = “300”> <!–<UserControl.DataContext> <viewModel:StudentViewModel/> </UserControl.DataContext>–> <Grid> <StackPanel HorizontalAlignment = “Left”> <ItemsControl ItemsSource = “{Binding Path = Students}”> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation = “Horizontal”> <TextBox Text = “{Binding Path = FirstName, Mode = TwoWay}” Width = “100” Margin = “3 5 3 5″/> <TextBox Text = “{Binding Path = LastName, Mode = TwoWay}” Width = “100” Margin = “0 5 3 5″/> <TextBlock Text = “{Binding Path = FullName, Mode = OneWay}” Margin = “0 5 3 5″/> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </StackPanel> </Grid> </UserControl> If you look at the above XAML code you will see that ItemsControl is bound to the Students collection exposed by ViewModel. You can also see that the property of Student model has their own individual bindings as well, and these are bound to the Textboxes and TextBlock. The ItemSource of ItemsControl is able to bind to the Students property, because the overall DataContext for the View is set to ViewModel. The individual bindings of properties here are also DataContext bindings, but they”re not binding against the ViewModel itself, because of the way an ItemSource works. When an item source binds to its collection it renders out a container for each item at rendering, and it sets the DataContext of that container to the item. So the overall DataContext for each textbox and textblock within a row is going to be an individual Student in the collection. And you can also see that these bindings for TextBoxes are TwoWay data binding and for TextBlock it is OneWay data binding as you canât edit TextBlock. When you run this application again, you will see the following output. Let us now change the text in the second textbox of first row from Allain to Upston and press tab to lose focus. You will see that the TextBlock text is also updated. This is because the bindings of the TextBoxes are set to TwoWay and it updates the Model as well, and from the model again the TextBlock is updated. Print Page Previous Next Advertisements ”;
MVVM – Dependency Injection
MVVM â Dependency Injection ”; Previous Next In this chapter, we will briefly discuss about dependency injection. We have already covered data binding decouples Views and ViewModels from each other that allows them to communicate without knowing explicitly what is going on at the other end of the communication. Now we need something similar to decouple our ViewModel from the client services. In early days of object-oriented programming, developers have faced the issue of creating and retrieving instances of classes in applications. Various solutions have been proposed for this problem. For the past few years, dependency injection and inversion of control (IoC) have gained popularity among developers and have taken precedence over some older solutions such as the Singleton pattern. Dependency Injection / IoC Containers IoC and dependency injection are two design patterns that are closely related and the container is basically a chunk of infrastructure code that does both of those patterns for you. IoC pattern is about delegating responsibility for construction and the dependency injection pattern is about providing dependencies to an object that”s already been constructed. They can both be treated as a two-phase approach to constructing. When you use a container, the container takes several responsibilities which are as follows − It constructs an object when asked. The container will determine what that object depends on. Constructing those dependencies. Injecting them into the object being constructed. Recursively doing process. Let”s have a look at how we can use dependency injection to break decoupling between ViewModels and the client services. We will wire up the save handling AddEditCustomerViewModel form by using dependency injection related to that. First we need to create a new interface in our project in Services folder. If you donât have a services folder in your project then create it first and add the following interface in the Services folder. using MVVMHierarchiesDemo.Model; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MVVMHierarchiesDemo.Services { public interface ICustomersRepository { Task<List<Customer>> GetCustomersAsync(); Task<Customer> GetCustomerAsync(Guid id); Task<Customer> AddCustomerAsync(Customer customer); Task<Customer> UpdateCustomerAsync(Customer customer); Task DeleteCustomerAsync(Guid customerId); } } Following is the implementation of ICustomersRepository. using MVVMHierarchiesDemo.Model; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MVVMHierarchiesDemo.Services { public class CustomersRepository : ICustomersRepository { ZzaDbContext _context = new ZzaDbContext(); public Task<List<Customer>> GetCustomersAsync() { return _context.Customers.ToListAsync(); } public Task<Customer> GetCustomerAsync(Guid id) { return _context.Customers.FirstOrDefaultAsync(c => c.Id == id); } public async Task<Customer> AddCustomerAsync(Customer customer){ _context.Customers.Add(customer); await _context.SaveChangesAsync(); return customer; } public async Task<Customer> UpdateCustomerAsync(Customer customer) { if (!_context.Customers.Local.Any(c => c.Id == customer.Id)) { _context.Customers.Attach(customer); } _context.Entry(customer).State = EntityState.Modified; await _context.SaveChangesAsync(); return customer; } public async Task DeleteCustomerAsync(Guid customerId) { var customer = _context.Customers.FirstOrDefault(c => c.Id == customerId); if (customer != null) { _context.Customers.Remove(customer); } await _context.SaveChangesAsync(); } } } The simple way to do Save handling is to add a new instance of ICustomersRepository in AddEditCustomerViewModel and overload the AddEditCustomerViewModel and CustomerListViewModel constructor. private ICustomersRepository _repo; public AddEditCustomerViewModel(ICustomersRepository repo) { _repo = repo; CancelCommand = new MyIcommand(OnCancel); SaveCommand = new MyIcommand(OnSave, CanSave); } Update the OnSave method as shown in the following code. private async void OnSave() { UpdateCustomer(Customer, _editingCustomer); if (EditMode) await _repo.UpdateCustomerAsync(_editingCustomer); else await _repo.AddCustomerAsync(_editingCustomer); Done(); } private void UpdateCustomer(SimpleEditableCustomer source, Customer target) { target.FirstName = source.FirstName; target.LastName = source.LastName; target.Phone = source.Phone; target.Email = source.Email; } Following is the complete AddEditCustomerViewModel. using MVVMHierarchiesDemo.Model; using MVVMHierarchiesDemo.Services; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MVVMHierarchiesDemo.ViewModel { class AddEditCustomerViewModel : BindableBase { private ICustomersRepository _repo; public AddEditCustomerViewModel(ICustomersRepository repo) { _repo = repo; CancelCommand = new MyIcommand(OnCancel); SaveCommand = new MyIcommand(OnSave, CanSave); } private bool _EditMode; public bool EditMode { get { return _EditMode; } set { SetProperty(ref _EditMode, value); } } private SimpleEditableCustomer _Customer; public SimpleEditableCustomer Customer { get { return _Customer; } set { SetProperty(ref _Customer, value); } } private Customer _editingCustomer = null; public void SetCustomer(Customer cust) { _editingCustomer = cust; if (Customer != null) Customer.ErrorsChanged -= RaiseCanExecuteChanged; Customer = new SimpleEditableCustomer(); Customer.ErrorsChanged += RaiseCanExecuteChanged; CopyCustomer(cust, Customer); } private void RaiseCanExecuteChanged(object sender, EventArgs e) { SaveCommand.RaiseCanExecuteChanged(); } public MyIcommand CancelCommand { get; private set; } public MyIcommand SaveCommand { get; private set; } public event Action Done = delegate { }; private void OnCancel() { Done(); } private async void OnSave() { UpdateCustomer(Customer, _editingCustomer); if (EditMode) await _repo.UpdateCustomerAsync(_editingCustomer); else await _repo.AddCustomerAsync(_editingCustomer); Done(); } private void UpdateCustomer(SimpleEditableCustomer source, Customer target) { target.FirstName = source.FirstName; target.LastName = source.LastName; target.Phone = source.Phone; target.Email = source.Email; } private bool CanSave() { return !Customer.HasErrors; } private void CopyCustomer(Customer source, SimpleEditableCustomer target) { target.Id = source.Id; if (EditMode) { target.FirstName = source.FirstName; target.LastName = source.LastName; target.Phone = source.Phone; target.Email = source.Email; } } } } When the above code is compiled and executed, you will see the same output but now ViewModels are more loosely decoupled. When you press the Add Customer button, you will see the following view. When the user leaves any field empty, then it will become highlighted and the save button will become disabled. Print Page Previous Next Advertisements ”;
MVVM – Useful Resources
MVVM – Useful Resources ”; Previous Next The following resources contain additional information on MVVM. Please use them to get more in-depth knowledge on this topic. Useful Video Courses Master KnockoutJS – JavaScript MVVM 39 Lectures 2 hours Skillbakery More Detail Breeze Music Player – Learn Kotlin, MVVM and MediaPlayer API 23 Lectures 1 hours CLEMENT OCHIENG More Detail Complete Weather App in SwiftUI, MVVM, Lottie Animation 15 Lectures 2 hours DevTechie More Detail iOS 12 and Swift 4 Crash Course 403 Lectures 80 hours Stone River ELearning More Detail iOS 15 SwiftUI 3 Loan Tracker MVVM, CoreData, and Cloud Kit 17 Lectures 4 hours Packt Publishing More Detail Flutter Advanced Course – Clean Architecture With MVVM 172 Lectures 19.5 hours Mina Farid More Detail Print Page Previous Next Advertisements ”;
MVVM – Hooking Up Views
MVVM â Hooking Up Views ”; Previous Next In this chapter, we will cover different ways in which you can get your views hooked up to ViewModel. First, let”s have a look at View first construction where we can declare it in XAML. As we have seen the example in the last chapter where we have hooked up a view from the main window. Now we will see other ways to hook up views. We will be using the same example in this chapter as well. Following is the same Model class implementation. using System.ComponentModel; namespace MVVMDemo.Model { public class StudentModel {} public class Student : INotifyPropertyChanged { private string firstName; private string lastName; public string FirstName { get { return firstName; } set { if (firstName != value) { firstName = value; RaisePropertyChanged(“FirstName”); RaisePropertyChanged(“FullName”); } } } public string LastName { get { return lastName; } set { if (lastName != value) { lastName = value; RaisePropertyChanged(“LastName”); RaisePropertyChanged(“FullName”); } } } public string FullName { get { return firstName + ” ” + lastName; } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } } } Here is the ViewModel class implementation. This time LoadStudents method is called in the default constructor. using MVVMDemo.Model; using System.Collections.ObjectModel; namespace MVVMDemo.ViewModel{ public class StudentViewModel { public StudentViewModel() { LoadStudents(); } public ObservableCollection<Student> Students { get; set; } public void LoadStudents() { ObservableCollection<Student> students = new ObservableCollection<Student>(); students.Add(new Student { FirstName = “Mark”, LastName = “Allain” }); students.Add(new Student { FirstName = “Allen”, LastName = “Brown” }); students.Add(new Student { FirstName = “Linda”, LastName = “Hamerski” }); Students = students; } } } Whether the view is a Window, User Control or Page, parser generally works top to bottom and left to right. It calls the default constructor for each element as it encounters it. There are two ways to construct a view. You can use any on them. View First Construction in XAML View First Construction in Code-behind View First Construction in XAML One way is to simply add your ViewModel as a nested element in the setter for the DataContext property as shown in the following code. <UserControl.DataContext> <viewModel:StudentViewModel/> </UserControl.DataContext> Here is the complete View XAML file. <UserControl x:Class=”MVVMDemo.Views.StudentView” xmlns = “http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x = “http://schemas.microsoft.com/winfx/2006/xaml” xmlns:mc = “http://schemas.openxmlformats.org/markup-compatibility/2006” xmlns:d = “http://schemas.microsoft.com/expression/blend/2008” xmlns:local = “clr-namespace:MVVMDemo.Views” xmlns:viewModel = “clr-namespace:MVVMDemo.ViewModel” mc:Ignorable = “d” d:DesignHeight = “300” d:DesignWidth = “300”> <UserControl.DataContext> <viewModel:StudentViewModel/> </UserControl.DataContext> <Grid> <StackPanel HorizontalAlignment = “Left”> <ItemsControl ItemsSource = “{Binding Path = Students}”> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation = “Horizontal”> <TextBox Text = “{Binding Path = FirstName, Mode = TwoWay}” Width = “100” Margin = “3 5 3 5″/> <TextBox Text = “{Binding Path = LastName, Mode = TwoWay}” Width = “100” Margin = “0 5 3 5″/> <TextBlock Text = “{Binding Path = FullName, Mode = OneWay}” Margin = “0 5 3 5″/> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </StackPanel> </Grid> </UserControl> View First Construction in Code-behind Another way is that you can get View first construction is by simply constructing the view model yourself in the code behind of your View by setting the DataContext property there with the instance. Typically, the DataContext property is set in the constructor method of view, but you could also defer the construction until the Load event of the view fires. using System.Windows.Controls; namespace MVVMDemo.Views { /// <summary> /// Interaction logic for StudentView.xaml /// </summary> public partial class StudentView : UserControl { public StudentView() { InitializeComponent(); this.DataContext = new MVVMDemo.ViewModel.StudentViewModel(); } } } One reason for constructing the view model in Code-behind instead of XAML is that the View model constructor takes parameters, but XAML parsing can only construct elements if defined in default constructor. Now in this case the XAML file of View will look like as shown in the following code. <UserControl x:Class = “MVVMDemo.Views.StudentView” xmlns = “http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x = “http://schemas.microsoft.com/winfx/2006/xaml” xmlns:mc = “http://schemas.openxmlformats.org/markup-compatibility/2006” xmlns:d = “http://schemas.microsoft.com/expression/blend/2008” xmlns:local = “clr-namespace:MVVMDemo.Views” mc:Ignorable = “d” d:DesignHeight = “300” d:DesignWidth = “300”> <Grid> <StackPanel HorizontalAlignment = “Left”> <ItemsControl ItemsSource = “{Binding Path = Students}”> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation = “Horizontal”< <TextBox Text = “{Binding Path = FirstName, Mode = TwoWay}” Width = “100” Margin = “3 5 3 5″/> <TextBox Text = “{Binding Path = LastName, Mode = TwoWay}” Width = “100” Margin = “0 5 3 5″/> <TextBlock Text = “{Binding Path = FullName, Mode = OneWay}” Margin = “0 5 3 5″/> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </StackPanel> </Grid> </UserControl> You can declare this View in the MainWindow as shown in the MainWindow.XAML file. <Window x:Class = “MVVMDemo.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:MVVMDemo” xmlns:views = “clr-namespace:MVVMDemo.Views” mc:Ignorable = “d” Title = “MainWindow” Height = “350” Width = “525”> <Grid> <views:StudentView x:Name = “StudentViewControl”/> </Grid> </Window> When the above code is compiled and executed, you will see the following output on your main window. We recommend you to execute the above example in a step-by-step manner for better understanding. Print Page Previous Next Advertisements ”;
MVVM – Validations
MVVM â Validations ”; Previous Next In this chapter, we will learn about validations. We will also look at a clean way to do validation with what WPF bindings already support but tying it into MVVM components. Validation in MVVM When your application starts accepting data input from end users you need to consider validating that input. Make sure it conforms to your overall requirements. WPF has some great builds and features in the binding system for validating input and you can still leverage all those features when doing MVVM. Keep in mind that the logic that supports your validation and defines what rules exist for what properties should be part of the Model or the ViewModel, not the View itself. You can still use all the ways of expressing validation that are supported by WPF data binding including − Throwing exceptions on a property is set. Implementing the IDataErrorInfo interface. Implementing INotifyDataErrorInfo. Use WPF validation rules. In general, INotifyDataErrorInfo is recommended and was introduced to WPF .net 4.5 and it supports querying the object for errors associated with properties and it also fixes a couple of deficiencies with all the other options. Specifically, it allows asynchronous validation. It allows properties to have more than one error associated with them. Adding Validation Letâs take a look at an example in which we will add validation support to our input view, and in large application you will probably need this a number of places in your application. Sometimes on Views, sometimes on ViewModels and sometimes on these helper objects there are wrappers around model objects. Itâs a good practice for putting the validation support in a common base class that you can then inherit from different scenarios. The base class will support INotifyDataErrorInfo so that that validation gets triggered when properties change. Create add a new class called ValidatableBindableBase. Since we already have a base class for a property change handling, letâs derive the base class from it and also implement the INotifyDataErrorInfo interface. Following is the implementation of ValidatableBindableBase class. using System; using System.Collections.Generic; using System.ComponentModel; //using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows.Controls; namespace MVVMHierarchiesDemo { public class ValidatableBindableBase : BindableBase, INotifyDataErrorInfo { private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>(); public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged = delegate { }; public System.Collections.IEnumerable GetErrors(string propertyName) { if (_errors.ContainsKey(propertyName)) return _errors[propertyName]; else return null; } public bool HasErrors { get { return _errors.Count > 0; } } protected override void SetProperty<T>(ref T member, T val, [CallerMemberName] string propertyName = null) { base.SetProperty<T>(ref member, val, propertyName); ValidateProperty(propertyName, val); } private void ValidateProperty<T>(string propertyName, T value) { var results = new List<ValidationResult>(); //ValidationContext context = new ValidationContext(this); //context.MemberName = propertyName; //Validator.TryValidateProperty(value, context, results); if (results.Any()) { //_errors[propertyName] = results.Select(c => c.ErrorMessage).ToList(); } else { _errors.Remove(propertyName); } ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName)); } } } Now add AddEditCustomerView and AddEditCustomerViewModel in respective folders. Following is the code of AddEditCustomerView.xaml. <UserControl x:Class = “MVVMHierarchiesDemo.Views.AddEditCustomerView” xmlns = “http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x = “http://schemas.microsoft.com/winfx/2006/xaml” xmlns:mc = “http://schemas.openxmlformats.org/markup-compatibility/2006” xmlns:d = “http://schemas.microsoft.com/expression/blend/2008” xmlns:local = “clr-namespace:MVVMHierarchiesDemo.Views” mc:Ignorable = “d” d:DesignHeight = “300” d:DesignWidth = “300”> <Grid> <Grid.RowDefinitions> <RowDefinition Height = “Auto” /> <RowDefinition Height = “Auto” /> </Grid.RowDefinitions> <Grid x:Name = “grid1” HorizontalAlignment = “Left” DataContext = “{Binding Customer}” Margin = “10,10,0,0” VerticalAlignment = “Top”> <Grid.ColumnDefinitions> <ColumnDefinition Width = “Auto” /> <ColumnDefinition Width = “Auto” /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height = “Auto” /> <RowDefinition Height = “Auto” /> <RowDefinition Height = “Auto” /> <RowDefinition Height = “Auto” /> </Grid.RowDefinitions> <Label Content = “First Name:” Grid.Column = “0” HorizontalAlignment = “Left” Margin = “3” Grid.Row = “0” VerticalAlignment = “Center” /> <TextBox x:Name = “firstNameTextBox” Grid.Column = “1” HorizontalAlignment = “Left” Height = “23” Margin = “3” Grid.Row = “0” Text = “{Binding FirstName, ValidatesOnNotifyDataErrors = True}” VerticalAlignment = “Center” Width = “120” /> <Label Content = “Last Name:” Grid.Column = “0” HorizontalAlignment = “Left” Margin = “3” Grid.Row = “1” VerticalAlignment = “Center” /> <TextBox x:Name = “lastNameTextBox” Grid.Column = “1” HorizontalAlignment = “Left” Height = “23” Margin = “3” Grid.Row = “1” Text = “{Binding LastName, ValidatesOnNotifyDataErrors = True}” VerticalAlignment = “Center” Width = “120” /> <Label Content = “Email:” Grid.Column = “0” HorizontalAlignment = “Left” Margin = “3” Grid.Row = “2” VerticalAlignment = “Center” /> <TextBox x:Name = “emailTextBox” Grid.Column = “1” HorizontalAlignment = “Left” Height = “23” Margin = “3” Grid.Row = “2” Text = “{Binding Email, ValidatesOnNotifyDataErrors = True}” VerticalAlignment = “Center” Width = “120” /> <Label Content = “Phone:” Grid.Column = “0” HorizontalAlignment = “Left” Margin = “3” Grid.Row = “3” VerticalAlignment = “Center” /> <TextBox x:Name = “phoneTextBox” Grid.Column = “1” HorizontalAlignment = “Left” Height = “23” Margin = “3” Grid.Row = “3” Text = “{Binding Phone, ValidatesOnNotifyDataErrors = True}” VerticalAlignment = “Center” Width = “120” /> </Grid> <Grid Grid.Row = “1”> <Button Content = “Save” Command = “{Binding SaveCommand}” HorizontalAlignment = “Left” Margin = “25,5,0,0” VerticalAlignment = “Top” Width = “75” /> <Button Content = “Add” Command = “{Binding SaveCommand}” HorizontalAlignment = “Left” Margin = “25,5,0,0” VerticalAlignment = “Top” Width = “75” /> <Button Content = “Cancel” Command = “{Binding CancelCommand}” HorizontalAlignment = “Left” Margin = “150,5,0,0” VerticalAlignment = “Top” Width = “75” /> </Grid> </Grid> </UserControl> Following is the AddEditCustomerViewModel implementation. using MVVMHierarchiesDemo.Model; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MVVMHierarchiesDemo.ViewModel { class AddEditCustomerViewModel : BindableBase { public AddEditCustomerViewModel() { CancelCommand = new MyIcommand(OnCancel); SaveCommand = new MyIcommand(OnSave, CanSave); } private bool _EditMode; public bool EditMode { get { return _EditMode; } set { SetProperty(ref _EditMode, value);} } private SimpleEditableCustomer _Customer; public SimpleEditableCustomer Customer { get { return _Customer; } set { SetProperty(ref _Customer, value);} } private Customer _editingCustomer = null; public void SetCustomer(Customer cust) { _editingCustomer = cust; if (Customer != null) Customer.ErrorsChanged -= RaiseCanExecuteChanged; Customer = new SimpleEditableCustomer(); Customer.ErrorsChanged += RaiseCanExecuteChanged; CopyCustomer(cust, Customer); } private void RaiseCanExecuteChanged(object sender, EventArgs e) { SaveCommand.RaiseCanExecuteChanged(); } public MyIcommand CancelCommand { get; private set; } public MyIcommand SaveCommand { get; private set; } public event Action Done