MVVM Architecture When Using Web Services For Database Interaction - Part Two

This article is in continuation of my previous article MVVM Architecture When Using Webservices For Database Interaction. Let's see how to use the events and commands described in the previous article. 

  1. First of all, for differentiating between the Views from where the command has been received and what to send in return to that view result, we have used a GUID variable named as token.

So, let's define the Guid variable _token in CompanyDetail View and ViewModel.

Let's create a helper class to send a request and get a response for View navigation.

  1. Message helper or appMessage.
  2. Message helper: We will use it to get response from a command.

Let's start by taking an example.

We will be creating a View (Window) for company details and then, we need to match if those details occur in our database. Let's do it step by step.

Step 1

Create the architecture as discussed in previous article, MVVM Architecture When Using Webservices For Database Interaction.

Step 2

Add a window under View section in main project. I named it as CompanyDetails.

  1. <Window x:Class="MVVMAndWebService.Views.CompanyDetails"    x:Name="This"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="CompanyDetails" Height="400" Width="500" Loaded="Window_Loaded">  
  5.     <Grid >  
  6.         <Grid.RowDefinitions>  
  7.             <RowDefinition Height="63"></RowDefinition>  
  8.             <RowDefinition Height="*"></RowDefinition>  
  9.             <RowDefinition Height="10"></RowDefinition>  
  10.         </Grid.RowDefinitions>  
  11.         <Grid.ColumnDefinitions>  
  12.             <ColumnDefinition Width="10"></ColumnDefinition>  
  13.             <ColumnDefinition Width="*"></ColumnDefinition>  
  14.             <ColumnDefinition Width="10"></ColumnDefinition>  
  15.         </Grid.ColumnDefinitions>  
  16.         <Border BorderThickness="0"    
  17.             Grid.Row="0"  
  18.             Grid.ColumnSpan="3"   
  19.             BorderBrush="Transparent"  
  20.             Grid.Column="0">  
  21.             <Image x:Name="logoImage"   
  22.                            Stretch="UniformToFill"   Width="140"   HorizontalAlignment="Left"  
  23.                            Margin="0,5,20,0">  
  24.             </Image>  
  25.         </Border>  
  26.         <Grid  Grid.Column="1" Margin="0,37,0,26" Grid.RowSpan="2">  
  27.             <Grid.RowDefinitions>  
  28.                 <RowDefinition Height="40"></RowDefinition>  
  29.                 <RowDefinition Height="40"></RowDefinition>  
  30.                 <RowDefinition Height="40"></RowDefinition>  
  31.                 <RowDefinition Height="70"></RowDefinition>  
  32.             </Grid.RowDefinitions>  
  33.             <Grid.ColumnDefinitions>  
  34.                 <ColumnDefinition Width="171*"></ColumnDefinition>  
  35.                 <ColumnDefinition Width="80*"></ColumnDefinition>  
  36.                 <ColumnDefinition Width="176*"/>  
  37.             </Grid.ColumnDefinitions>  
  38.             <Label x:Name="lblCompanyName"   
  39.                        Content="Name : "  
  40.                        FontSize="14"    
  41.                        HorizontalAlignment="Right"  
  42.                        VerticalAlignment="Center"  
  43.                        Grid.Row="0"  
  44.                        Grid.Column="0" Margin="0,6">  
  45.             </Label>  
  46.             <TextBox x:Name="TextBoxCompanyName"  
  47.                            
  48.                          Width="200"   
  49.                          MaxLength="50"  
  50.                          Grid.Column="1"  
  51.                          Grid.Row="0"   
  52.                         TabIndex="0"  
  53.                 Text="{Binding CompanyName}" Grid.ColumnSpan="2" Margin="0,6,0,7">  
  54.             </TextBox>  
  55.   
  56.             <Label x:Name="lblUserName"   
  57.                        Content="User Name : "  
  58.                        FontSize="14"    
  59.                        HorizontalAlignment="Right"  
  60.                        VerticalAlignment="Center"  
  61.                        Grid.Row="1"  
  62.                        Grid.Column="0" Margin="0,6"  
  63.                    >  
  64.             </Label>  
  65.             <TextBox x:Name="TextBoxUserName"   
  66.                          Width="200"   
  67.                          MaxLength="50"  
  68.                          Grid.Column="1"  
  69.                          Grid.Row="1"   
  70.                         TabIndex="0"   
  71.                      Text="{Binding UserName}" Grid.ColumnSpan="2" Margin="0,7,0,6">  
  72.             </TextBox>  
  73.             <Label x:Name="lblPassword"   
  74.                        Content="Password : "  
  75.                        FontSize="14"   
  76.                        HorizontalAlignment="Right"  
  77.                        VerticalAlignment="Center"  
  78.                        Grid.Row="2"  
  79.                        Grid.Column="0" Margin="0,6">  
  80.             </Label>  
  81.             <PasswordBox x:Name="TextBoxPassword"                     
  82.                           
  83.                          Width="200"   
  84.                          MaxLength="50"  
  85.                          Grid.Column="1"  
  86.                          Grid.Row="2"   
  87.                         TabIndex="0" Grid.ColumnSpan="2" Margin="13,2,40,11"  
  88.                       >  
  89.             </PasswordBox>  
  90.             <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Stretch"   
  91.                         Grid.Column="0" Grid.ColumnSpan="3" Margin="0,76,0,-76">  
  92.                 <Button x:Name="ButtonSave"  
  93.                     FontSize="20"  
  94.                          
  95.                     HorizontalAlignment="Right"   
  96.                     Width="100"  
  97.                     Height="50"  
  98.                     Margin="50,10,0,11"  
  99.                     Content="Save"   
  100.                     Command="{Binding SaveCompanyCommand}"   
  101.                     CommandParameter="{Binding ElementName=This}"  
  102.                     TabIndex="7" IsDefault="True"/>  
  103.                 <Button x:Name="ButtonCancel"  
  104.                     FontSize="20"   
  105.                     Margin="20,10,0,11"  
  106.                     Command="{Binding CancelCompanyCommand}"  
  107.                     CommandParameter="{Binding ElementName=This}"  
  108.                         
  109.                     HorizontalAlignment="Left"  
  110.                     Width="90"  
  111.                     Height="50"  
  112.                     Content="Cancel"   
  113.                     TabIndex="8" IsCancel="True" />  
  114.             </StackPanel>  
  115.         </Grid>  
  116.     </Grid>  
  117. </Window>  

Step 3

On cs page of the above View, let's define a property GUID _token, to differentiate the requests received from Views.

Then, as we know the password will be a secured string, to get the value of the passwordBox, we cannot use the direct bindings as we have used in case of other textboxes. Please see the XAML provided above. Password is yet not bound to any property of ViewModel.

So, we need to implement an interface to the View which will be used to access the password string. So, this interface will help the View to get secure password string and that can be converted back to insecure string in View Model.

Binding for commands is done on Buttons.

Let's create an interface to the CommonClasses for above purpose, which will contain the below code.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading.Tasks;  
  6.   
  7. namespace CommonClasses  
  8. {  
  9.     public interface IHavePassword  
  10.     {  
  11.         System.Security.SecureString Password { get; }  
  12.     }  
  13. }  

Now, for getting the response as we have discussed earlier, we will be receiving the message at the end of each command. So, let's create a Message class as a helper. Keep it separate from the above architecture and add Helper project to the existing solution.

 

As you can see from the above screenshot, we need to create a class ‘Message’ containing the message properties.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading.Tasks;  
  6.   
  7. namespace Helper  
  8. {  
  9.     public class Message  
  10.     {  
  11.         public string Type;  
  12.         public object Data;  
  13.     }  
  14. }  
Next, we need to create an interface for Message because we need to implement it in the same View so as to support multiple inheritance.
 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading.Tasks;  
  6.   
  7. namespace Helper  
  8. {  
  9.     public interface IMessageTarget  
  10.     {  
  11.         Guid Token { get; }  
  12.         void ReceiveMessage(Message msg);  
  13.     }  
  14. }  

Token will differentiate between the requests from different Views. Let's create static class to send response to the Views.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading.Tasks;  
  6.   
  7. namespace Helper  
  8. {  
  9.    public static class AppMessage  
  10.     {  
  11.        static Dictionary<Guid, IMessageTarget> _registerWindow = new Dictionary<Guid, IMessageTarget>();  
  12.        //Register a view.  
  13.         public static void Register(IMessageTarget View, Guid token)  
  14.         {  
  15.             if (_registerWindow.ContainsKey(token))  
  16.             {  
  17.                 _registerWindow.Remove(token);  
  18.             }  
  19.             _registerWindow.Add(token, View);  
  20.         }  
  21.        //Send response  
  22.         public static void Send(Guid token, Message notification)  
  23.         {  
  24.             if (_registerWindow.ContainsKey(token))  
  25.             {  
  26.                 _registerWindow[token].ReceiveMessage(notification);  
  27.             }  
  28.         }  
  29.   
  30.        //unregister that view.  
  31.         public static void UnRegister(Guid token)  
  32.         {  
  33.             if (_registerWindow.ContainsKey(token))  
  34.             {  
  35.                 _registerWindow.Remove(token);  
  36.             }  
  37.         }  
  38.     }  
  39. }   
  1. Register a View by token.
  2. Send response.
  3. Unregister that View.

OK. As per the above understanding, our View should contain GUID property token, Message response Method from IMessage, Password string property from IhavePassword.

Our View.cs page will look like the below.
  1. using CommonClasses;  
  2. using Helper;  
  3. using System;  
  4. using System.Collections.Generic;  
  5. using System.Linq;  
  6. using System.Text;  
  7. using System.Threading.Tasks;  
  8. using System.Windows;  
  9. using System.Windows.Controls;  
  10. using System.Windows.Data;  
  11. using System.Windows.Documents;  
  12. using System.Windows.Input;  
  13. using System.Windows.Media;  
  14. using System.Windows.Media.Imaging;  
  15. using System.Windows.Shapes;  
  16. using ViewModels;  
  17.   
  18. namespace MVVMAndWebService.Views  
  19. {  
  20.     /// <summary>  
  21.     /// Interaction logic for CompanyDetails.xaml  
  22.     /// </summary>  
  23.     public partial class CompanyDetails : Window,IHavePassword,  IMessageTarget  
  24.     {  
  25.         private Guid _token;  
  26.   
  27.         public Guid Token  
  28.         {  
  29.             get  
  30.             {  
  31.                 return _token;  
  32.             }  
  33.         }  
  34.         
  35.         public CompanyDetails()  
  36.         {  
  37.             InitializeComponent();  
  38.         }  
  39.         private CompanyDetailsViewModel _CompanyDetailsViewModel;  
  40.         private void Window_Loaded(object sender, RoutedEventArgs e)  
  41.         {  
  42.             _token = Guid.NewGuid();  
  43.             //Now question is how to get response and take next step?  
  44.             //Register a view with appMessage:  
  45.             AppMessage.Register(this, _token);  
  46.               
  47.             this.DataContext = _CompanyDetailsViewModel = new CompanyDetailsViewModel(_token);  
  48.         }  
  49.         public System.Security.SecureString Password  
  50.         {  
  51.             get  
  52.             {  
  53.                 return TextBoxPassword.SecurePassword;  
  54.             }  
  55.         }  
  56.         //This method is to get the response.Message will contain the response and we can modify it as per the requirement.  
  57.         public void ReceiveMessage(Message msg)  
  58.         {  
  59.             if (msg.Type.Equals("OpenView"))  
  60.             {  
  61.                 MessageBox.Show("The response is {0}", msg.Type);  
  62.             }  
  63.             else if (msg.Type.Equals("CloseView"))  
  64.             {  
  65.                 MessageBox.Show("The response is {0}", msg.Type);  
  66.             }  
  67.             else if (msg.Type.Equals("OpenInformationBox"))  
  68.             {  
  69.                 MessageBox.Show("The response is {0}", msg.Type);  
  70.             }  
  71.         }  
  72.     }  
  73. }  

Let's move to ViewModel containing commands and call method from Web Service.

Note

From the previous article, we already know how to use a baseViewModel and Command class in a View Model. To access methods from Web Service, let's add a class in WebService and write the common code in that.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ServiceModel;  
  6.   
  7. namespace WebService  
  8. {  
  9.     public class Service  
  10.     {  
  11.         CarWashService.CarWashServiceClient carWashService;  
  12.         public void GetAddress()  
  13.         {  
  14.             String baseAddress = "AddressOfYourWebservice";  
  15.             BasicHttpBinding binding = new BasicHttpBinding();  
  16.             EndpointAddress endPointAddress = new EndpointAddress(baseAddress);  
  17.             carWashService = new CarWashService.CarWashServiceClient(binding, endPointAddress);  
  18.   
  19.         }  
  20.         public CarWashService.CompanySetupDetail GetCompanyForSetup(string CompanyName, string UserName, String password)  
  21.         {  
  22.             GetAddress();  
  23.             return carWashService.GetcompanyForSetup(CompanyName, UserName, password);  
  24.   
  25.         }  
  26.     }  
  27. }  
View Model should look like.
  1. using CommonClasses;  
  2. using Helper;  
  3. using System;  
  4. using System.Collections.Generic;  
  5. using System.Linq;  
  6. using System.ServiceModel;  
  7. using System.Text;  
  8. using System.Threading.Tasks;  
  9. using WashifyKIOSK.Service.CarWashService;  
  10.   
  11. namespace ViewModels  
  12. {  
  13.    public  class CompanyDetailsViewModel:BaseViewModel  
  14.     {  
  15.   
  16.         //Define Properties:  
  17.         #region Constructor  
  18.         private Guid ViewToken;  
  19.         public CompanyDetailsViewModel(Guid _token)  
  20.         {  
  21.             ViewToken = _token;  
  22.         }  
  23.         #endregion  
  24.  
  25.         #region Properties  
  26.   
  27.         private string companyName;  
  28.         private string userName;  
  29.         private string _PasswordInVM;  
  30.         private string _message = string.Empty;  
  31.         private string _newLine = Environment.NewLine;  
  32.   
  33.         public string CompanyName  
  34.         {  
  35.             get { return companyName; }  
  36.             set  
  37.             {  
  38.                 if (value != companyName)  
  39.                 {  
  40.                     companyName = value;  
  41.                     //Call OnPropertyChanged from BaseViewModel,and Pass the name of the property.  
  42.                     OnPropertyChanged("CompanyName");  
  43.                 }  
  44.             }  
  45.         }  
  46.   
  47.         public string UserName  
  48.         {  
  49.             get { return userName; }  
  50.             set  
  51.             {  
  52.                 if (value != userName)  
  53.                 {  
  54.                     userName = value;  
  55.                     OnPropertyChanged("UserName");  
  56.                 }  
  57.             }  
  58.         }  
  59.   
  60.         public string PassWord  
  61.         {  
  62.             get { return _PasswordInVM; }  
  63.             set  
  64.             {  
  65.                 if (value != _PasswordInVM)  
  66.                 {  
  67.                     _PasswordInVM = value;  
  68.                     OnPropertyChanged("PassWord");  
  69.                 }  
  70.             }  
  71.         }  
  72.         #endregion  
  73.         #region Commands  
  74.         private CommandClass<object> m_SaveCompanyCommand;  
  75.         public CommandClass<object> SaveCompanyCommand  
  76.         {  
  77.             get  
  78.             {  
  79.                 return m_SaveCompanyCommand ?? (m_SaveCompanyCommand = new CommandClass<object>(  
  80.                     ve => SaveCompanyInfo(ve), (t) => true));  
  81.             }  
  82.         }  
  83.   
  84.         private CommandClass<object> m_CancelCompanyCommand;  
  85.         public CommandClass<object> CancelCompanyCommand  
  86.         {  
  87.             get  
  88.             {  
  89.                 return m_CancelCompanyCommand ?? (m_CancelCompanyCommand = new CommandClass<object>(  
  90.                     ve => CancelCompanyInfo(), (t) => true));  
  91.             }  
  92.         }  
  93.         #endregion  
  94.         #region Methods  
  95.         /// <summary>  
  96.         /// Method to send notification to close the window  
  97.         /// </summary>  
  98.         private void CancelCompanyInfo()  
  99.         {  
  100.             AppMessage.Send(ViewToken, new Helper.Message() { Type = "CloseView" });  
  101.         }  
  102.   
  103.         /// <summary>  
  104.         /// Method to check the valid company details  
  105.         /// </summary>  
  106.         /// <param name="parameter"></param>  
  107.         private void SaveCompanyInfo(object parameter)  
  108.         {  
  109.             if (!ValidateInfo(parameter))  
  110.             {  
  111.                 AppMessage.Send(ViewToken, new Helper.Message() { Type = "OpenInformationBox", Data = _message });  
  112.             }  
  113.             else  
  114.             {  
  115.                 try  
  116.                 {  
  117.                     var passwordContainer = parameter as IHavePassword;  
  118.                     if (passwordContainer != null)  
  119.                     {  
  120.                         var secureString = passwordContainer.Password;  
  121.                         PassWord = ConvertToUnsecureString(secureString);  
  122.                     }  
  123.                     String baseAddress = "AddressofWebservice";  
  124.                     BasicHttpBinding binding = new BasicHttpBinding();  
  125.                     EndpointAddress endPointAddress = new EndpointAddress(baseAddress);  
  126.                     WebService.Service.CarWashService.CarWashServiceClient carWashService = new WashifyKIOSK.Service.CarWashService.CarWashServiceClient(binding, endPointAddress);  
  127.                     {  
  128.                         WebService.Service.CarWashService.CompanySetupDetail company = carWashService.MethodName(CompanyName, UserName, PassWord);  
  129.                         if (company != null)  
  130.                         {  
  131.                             AppMessage.Send(ViewToken, new Helper.Message() { Type = "OpenView", Data = company });  
  132.                         }  
  133.                     }  
  134.                 }  
  135.                 catch (Exception ex)  
  136.                 {  
  137.                     throw ex;  
  138.                 }  
  139.            }  
  140.         }  
  141.   
  142.         /// <summary>  
  143.         /// Validate the information entered  
  144.         /// </summary>  
  145.         /// <param name="param"></param>  
  146.         /// <returns></returns>  
  147.         /// <summary>  
  148.         /// Validate the information entered  
  149.         /// </summary>  
  150.         /// <param name="param"></param>  
  151.         /// <returns></returns>  
  152.         public bool ValidateInfo(object param)  
  153.         {  
  154.             bool err = false;  
  155.             _message = "Please provide required information." + _newLine;  
  156.             if (CompanyName == null)  
  157.             {  
  158.                 _message = _message + _newLine;  
  159.                 _message = _message + "* Company Name.";  
  160.                 err = true;  
  161.             }  
  162.             if (UserName == null)  
  163.             {  
  164.                 _message = _message + _newLine;  
  165.                 _message = _message + "* User Name.";  
  166.                 if (!err)  
  167.                     err = true;  
  168.             }  
  169.             var passwordContainer = param as IHavePassword;  
  170.             if (passwordContainer != null)  
  171.             {  
  172.                 var secureString = passwordContainer.Password;  
  173.                 PassWord = ConvertToUnsecureString(secureString);  
  174.             }  
  175.             if (PassWord == "")  
  176.             {  
  177.                 _message = _message + _newLine;  
  178.                 _message = _message + "* Password.";  
  179.                 if (!err)  
  180.                     err = true;  
  181.             }  
  182.             if (err)  
  183.             {  
  184.                 return false;  
  185.             }  
  186.             return true;  
  187.         }  
  188.   
  189.         /// <summary>  
  190.         /// Decript the password  
  191.         /// </summary>  
  192.         /// <param name="securePassword"></param>  
  193.         /// <returns></returns>  
  194.         private string ConvertToUnsecureString(System.Security.SecureString securePassword)  
  195.         {  
  196.             if (securePassword == null)  
  197.             {  
  198.                 return string.Empty;  
  199.             }  
  200.   
  201.             IntPtr unmanagedString = IntPtr.Zero;  
  202.             try  
  203.             {  
  204.                 unmanagedString = System.Runtime.InteropServices.Marshal.SecureStringToGlobalAllocUnicode(securePassword);  
  205.                 return System.Runtime.InteropServices.Marshal.PtrToStringUni(unmanagedString);  
  206.             }  
  207.             finally  
  208.             {  
  209.                 System.Runtime.InteropServices.Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);  
  210.             }  
  211.         }  
  212.         #endregion  
  213.     }  }
The coding of our View, ViewModel, Service, and other parts is complete. Now, we can run the application to see how commands will run.