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.
- 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.
- Message helper or appMessage.
- 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.
- <Window x:Class="MVVMAndWebService.Views.CompanyDetails" x:Name="This"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="CompanyDetails" Height="400" Width="500" Loaded="Window_Loaded">
- <Grid >
- <Grid.RowDefinitions>
- <RowDefinition Height="63"></RowDefinition>
- <RowDefinition Height="*"></RowDefinition>
- <RowDefinition Height="10"></RowDefinition>
- </Grid.RowDefinitions>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="10"></ColumnDefinition>
- <ColumnDefinition Width="*"></ColumnDefinition>
- <ColumnDefinition Width="10"></ColumnDefinition>
- </Grid.ColumnDefinitions>
- <Border BorderThickness="0"
- Grid.Row="0"
- Grid.ColumnSpan="3"
- BorderBrush="Transparent"
- Grid.Column="0">
- <Image x:Name="logoImage"
- Stretch="UniformToFill" Width="140" HorizontalAlignment="Left"
- Margin="0,5,20,0">
- </Image>
- </Border>
- <Grid Grid.Column="1" Margin="0,37,0,26" Grid.RowSpan="2">
- <Grid.RowDefinitions>
- <RowDefinition Height="40"></RowDefinition>
- <RowDefinition Height="40"></RowDefinition>
- <RowDefinition Height="40"></RowDefinition>
- <RowDefinition Height="70"></RowDefinition>
- </Grid.RowDefinitions>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="171*"></ColumnDefinition>
- <ColumnDefinition Width="80*"></ColumnDefinition>
- <ColumnDefinition Width="176*"/>
- </Grid.ColumnDefinitions>
- <Label x:Name="lblCompanyName"
- Content="Name : "
- FontSize="14"
- HorizontalAlignment="Right"
- VerticalAlignment="Center"
- Grid.Row="0"
- Grid.Column="0" Margin="0,6">
- </Label>
- <TextBox x:Name="TextBoxCompanyName"
-
- Width="200"
- MaxLength="50"
- Grid.Column="1"
- Grid.Row="0"
- TabIndex="0"
- Text="{Binding CompanyName}" Grid.ColumnSpan="2" Margin="0,6,0,7">
- </TextBox>
-
- <Label x:Name="lblUserName"
- Content="User Name : "
- FontSize="14"
- HorizontalAlignment="Right"
- VerticalAlignment="Center"
- Grid.Row="1"
- Grid.Column="0" Margin="0,6"
- >
- </Label>
- <TextBox x:Name="TextBoxUserName"
- Width="200"
- MaxLength="50"
- Grid.Column="1"
- Grid.Row="1"
- TabIndex="0"
- Text="{Binding UserName}" Grid.ColumnSpan="2" Margin="0,7,0,6">
- </TextBox>
- <Label x:Name="lblPassword"
- Content="Password : "
- FontSize="14"
- HorizontalAlignment="Right"
- VerticalAlignment="Center"
- Grid.Row="2"
- Grid.Column="0" Margin="0,6">
- </Label>
- <PasswordBox x:Name="TextBoxPassword"
-
- Width="200"
- MaxLength="50"
- Grid.Column="1"
- Grid.Row="2"
- TabIndex="0" Grid.ColumnSpan="2" Margin="13,2,40,11"
- >
- </PasswordBox>
- <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Stretch"
- Grid.Column="0" Grid.ColumnSpan="3" Margin="0,76,0,-76">
- <Button x:Name="ButtonSave"
- FontSize="20"
-
- HorizontalAlignment="Right"
- Width="100"
- Height="50"
- Margin="50,10,0,11"
- Content="Save"
- Command="{Binding SaveCompanyCommand}"
- CommandParameter="{Binding ElementName=This}"
- TabIndex="7" IsDefault="True"/>
- <Button x:Name="ButtonCancel"
- FontSize="20"
- Margin="20,10,0,11"
- Command="{Binding CancelCompanyCommand}"
- CommandParameter="{Binding ElementName=This}"
-
- HorizontalAlignment="Left"
- Width="90"
- Height="50"
- Content="Cancel"
- TabIndex="8" IsCancel="True" />
- </StackPanel>
- </Grid>
- </Grid>
- </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.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace CommonClasses
- {
- public interface IHavePassword
- {
- System.Security.SecureString Password { get; }
- }
- }
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.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace Helper
- {
- public class Message
- {
- public string Type;
- public object Data;
- }
- }
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.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace Helper
- {
- public interface IMessageTarget
- {
- Guid Token { get; }
- void ReceiveMessage(Message msg);
- }
- }
Token will differentiate between the requests from different Views. Let's create static class to send response to the Views.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace Helper
- {
- public static class AppMessage
- {
- static Dictionary<Guid, IMessageTarget> _registerWindow = new Dictionary<Guid, IMessageTarget>();
-
- public static void Register(IMessageTarget View, Guid token)
- {
- if (_registerWindow.ContainsKey(token))
- {
- _registerWindow.Remove(token);
- }
- _registerWindow.Add(token, View);
- }
-
- public static void Send(Guid token, Message notification)
- {
- if (_registerWindow.ContainsKey(token))
- {
- _registerWindow[token].ReceiveMessage(notification);
- }
- }
-
-
- public static void UnRegister(Guid token)
- {
- if (_registerWindow.ContainsKey(token))
- {
- _registerWindow.Remove(token);
- }
- }
- }
- }
- Register a View by token.
- Send response.
- 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.
- using CommonClasses;
- using Helper;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Data;
- using System.Windows.Documents;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
- using System.Windows.Shapes;
- using ViewModels;
-
- namespace MVVMAndWebService.Views
- {
-
-
-
- public partial class CompanyDetails : Window,IHavePassword, IMessageTarget
- {
- private Guid _token;
-
- public Guid Token
- {
- get
- {
- return _token;
- }
- }
-
- public CompanyDetails()
- {
- InitializeComponent();
- }
- private CompanyDetailsViewModel _CompanyDetailsViewModel;
- private void Window_Loaded(object sender, RoutedEventArgs e)
- {
- _token = Guid.NewGuid();
-
-
- AppMessage.Register(this, _token);
-
- this.DataContext = _CompanyDetailsViewModel = new CompanyDetailsViewModel(_token);
- }
- public System.Security.SecureString Password
- {
- get
- {
- return TextBoxPassword.SecurePassword;
- }
- }
-
- public void ReceiveMessage(Message msg)
- {
- if (msg.Type.Equals("OpenView"))
- {
- MessageBox.Show("The response is {0}", msg.Type);
- }
- else if (msg.Type.Equals("CloseView"))
- {
- MessageBox.Show("The response is {0}", msg.Type);
- }
- else if (msg.Type.Equals("OpenInformationBox"))
- {
- MessageBox.Show("The response is {0}", msg.Type);
- }
- }
- }
- }
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.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.ServiceModel;
-
- namespace WebService
- {
- public class Service
- {
- CarWashService.CarWashServiceClient carWashService;
- public void GetAddress()
- {
- String baseAddress = "AddressOfYourWebservice";
- BasicHttpBinding binding = new BasicHttpBinding();
- EndpointAddress endPointAddress = new EndpointAddress(baseAddress);
- carWashService = new CarWashService.CarWashServiceClient(binding, endPointAddress);
-
- }
- public CarWashService.CompanySetupDetail GetCompanyForSetup(string CompanyName, string UserName, String password)
- {
- GetAddress();
- return carWashService.GetcompanyForSetup(CompanyName, UserName, password);
-
- }
- }
- }
View Model should look like.
- using CommonClasses;
- using Helper;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.ServiceModel;
- using System.Text;
- using System.Threading.Tasks;
- using WashifyKIOSK.Service.CarWashService;
-
- namespace ViewModels
- {
- public class CompanyDetailsViewModel:BaseViewModel
- {
-
-
- #region Constructor
- private Guid ViewToken;
- public CompanyDetailsViewModel(Guid _token)
- {
- ViewToken = _token;
- }
- #endregion
-
- #region Properties
-
- private string companyName;
- private string userName;
- private string _PasswordInVM;
- private string _message = string.Empty;
- private string _newLine = Environment.NewLine;
-
- public string CompanyName
- {
- get { return companyName; }
- set
- {
- if (value != companyName)
- {
- companyName = value;
-
- OnPropertyChanged("CompanyName");
- }
- }
- }
-
- public string UserName
- {
- get { return userName; }
- set
- {
- if (value != userName)
- {
- userName = value;
- OnPropertyChanged("UserName");
- }
- }
- }
-
- public string PassWord
- {
- get { return _PasswordInVM; }
- set
- {
- if (value != _PasswordInVM)
- {
- _PasswordInVM = value;
- OnPropertyChanged("PassWord");
- }
- }
- }
- #endregion
- #region Commands
- private CommandClass<object> m_SaveCompanyCommand;
- public CommandClass<object> SaveCompanyCommand
- {
- get
- {
- return m_SaveCompanyCommand ?? (m_SaveCompanyCommand = new CommandClass<object>(
- ve => SaveCompanyInfo(ve), (t) => true));
- }
- }
-
- private CommandClass<object> m_CancelCompanyCommand;
- public CommandClass<object> CancelCompanyCommand
- {
- get
- {
- return m_CancelCompanyCommand ?? (m_CancelCompanyCommand = new CommandClass<object>(
- ve => CancelCompanyInfo(), (t) => true));
- }
- }
- #endregion
- #region Methods
-
-
-
- private void CancelCompanyInfo()
- {
- AppMessage.Send(ViewToken, new Helper.Message() { Type = "CloseView" });
- }
-
-
-
-
-
- private void SaveCompanyInfo(object parameter)
- {
- if (!ValidateInfo(parameter))
- {
- AppMessage.Send(ViewToken, new Helper.Message() { Type = "OpenInformationBox", Data = _message });
- }
- else
- {
- try
- {
- var passwordContainer = parameter as IHavePassword;
- if (passwordContainer != null)
- {
- var secureString = passwordContainer.Password;
- PassWord = ConvertToUnsecureString(secureString);
- }
- String baseAddress = "AddressofWebservice";
- BasicHttpBinding binding = new BasicHttpBinding();
- EndpointAddress endPointAddress = new EndpointAddress(baseAddress);
- WebService.Service.CarWashService.CarWashServiceClient carWashService = new WashifyKIOSK.Service.CarWashService.CarWashServiceClient(binding, endPointAddress);
- {
- WebService.Service.CarWashService.CompanySetupDetail company = carWashService.MethodName(CompanyName, UserName, PassWord);
- if (company != null)
- {
- AppMessage.Send(ViewToken, new Helper.Message() { Type = "OpenView", Data = company });
- }
- }
- }
- catch (Exception ex)
- {
- throw ex;
- }
- }
- }
-
-
-
-
-
-
-
-
-
-
-
- public bool ValidateInfo(object param)
- {
- bool err = false;
- _message = "Please provide required information." + _newLine;
- if (CompanyName == null)
- {
- _message = _message + _newLine;
- _message = _message + "* Company Name.";
- err = true;
- }
- if (UserName == null)
- {
- _message = _message + _newLine;
- _message = _message + "* User Name.";
- if (!err)
- err = true;
- }
- var passwordContainer = param as IHavePassword;
- if (passwordContainer != null)
- {
- var secureString = passwordContainer.Password;
- PassWord = ConvertToUnsecureString(secureString);
- }
- if (PassWord == "")
- {
- _message = _message + _newLine;
- _message = _message + "* Password.";
- if (!err)
- err = true;
- }
- if (err)
- {
- return false;
- }
- return true;
- }
-
-
-
-
-
-
- private string ConvertToUnsecureString(System.Security.SecureString securePassword)
- {
- if (securePassword == null)
- {
- return string.Empty;
- }
-
- IntPtr unmanagedString = IntPtr.Zero;
- try
- {
- unmanagedString = System.Runtime.InteropServices.Marshal.SecureStringToGlobalAllocUnicode(securePassword);
- return System.Runtime.InteropServices.Marshal.PtrToStringUni(unmanagedString);
- }
- finally
- {
- System.Runtime.InteropServices.Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
- }
- }
- #endregion
- } }
The coding of our View, ViewModel, Service, and other parts is complete. Now, we can run the application to see how commands will run.