Thursday, February 24, 2011

How to: Cascading Country, City and State Combo Boxes in Silverlight using MVVM

Hi guys,

Recently one of my friend send me a mail. He need something like below…

“You need to perform ADD and VIEW operation inside the GRID where Country, State should be a drop down. The State drop down inside the grid must be filtered based on the country selected in the country grid. If you choose country INDIA, then State must be filtered for India only”

Please keep a note, you must follow MVVM strictly everywhere

The above is the requirement for his project….

So lets start solving the problem…

Lets create a project first…

1. Open VS Studio (Mine is 2008 with SP1)

2. Open new project Dialog and select Silvelight – > Silverlight Application. and give a name to the project. My project name is “testapp”

3. Upon click “OK” One new Dialog box will come. Check the “Host Silverlight application in a new web site” and click OK

As I am using a database I have a WCF service application to do the database operation.

Add a new WCF Class library project. – “WcfService

Following is the SQL script for the database with data.

   1: USE [CustomerDB]
   2: GO
   3: /****** Object:  Table [dbo].[Country]    Script Date: 02/24/2011 22:34:09 ******/
   4: SET ANSI_NULLS ON
   5: GO
   6: SET QUOTED_IDENTIFIER ON
   7: GO
   8: SET ANSI_PADDING ON
   9: GO
  10: CREATE TABLE [dbo].[Country](
  11:     [CountryID] [int] IDENTITY(1,1) NOT NULL,
  12:     [Name] [varchar](50) NULL,
  13:  CONSTRAINT [PK_Country] PRIMARY KEY CLUSTERED 
  14: (
  15:     [CountryID] ASC
  16: )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  17: ) ON [PRIMARY]
  18: GO
  19: SET ANSI_PADDING OFF
  20: GO
  21: SET IDENTITY_INSERT [dbo].[Country] ON
  22: INSERT [dbo].[Country] ([CountryID], [Name]) VALUES (1, N'India')
  23: INSERT [dbo].[Country] ([CountryID], [Name]) VALUES (2, N'United States')
  24: INSERT [dbo].[Country] ([CountryID], [Name]) VALUES (3, N'United Kingdom')
  25: INSERT [dbo].[Country] ([CountryID], [Name]) VALUES (4, N'Indonasia')
  26: INSERT [dbo].[Country] ([CountryID], [Name]) VALUES (5, N'Germany')
  27: SET IDENTITY_INSERT [dbo].[Country] OFF
  28: /****** Object:  Table [dbo].[State]    Script Date: 02/24/2011 22:34:09 ******/
  29: SET ANSI_NULLS ON
  30: GO
  31: SET QUOTED_IDENTIFIER ON
  32: GO
  33: SET ANSI_PADDING ON
  34: GO
  35: CREATE TABLE [dbo].[State](
  36:     [StateID] [int] IDENTITY(1,1) NOT NULL,
  37:     [Name] [varchar](50) NULL,
  38:     [CountryID] [int] NOT NULL,
  39:  CONSTRAINT [PK_State] PRIMARY KEY CLUSTERED 
  40: (
  41:     [StateID] ASC
  42: )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  43: ) ON [PRIMARY]
  44: GO
  45: SET ANSI_PADDING OFF
  46: GO
  47: SET IDENTITY_INSERT [dbo].[State] ON
  48: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (1, N'Andhra Pradesh', 1)
  49: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (3, N'Karnataka', 1)
  50: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (5, N'Kerala', 1)
  51: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (7, N'Alabama', 2)
  52: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (8, N'Washington', 2)
  53: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (9, N'Greater London', 3)
  54: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (10, N'Scotland', 3)
  55: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (11, N'North West England', 3)
  56: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (12, N'Sumatra', 4)
  57: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (13, N'Java', 4)
  58: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (14, N'Berlin', 5)
  59: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (15, N'Brandenburg', 5)
  60: INSERT [dbo].[State] ([StateID], [Name], [CountryID]) VALUES (16, N'Hamburg', 5)
  61: SET IDENTITY_INSERT [dbo].[State] OFF
  62: /****** Object:  Table [dbo].[City]    Script Date: 02/24/2011 22:34:09 ******/
  63: SET ANSI_NULLS ON
  64: GO
  65: SET QUOTED_IDENTIFIER ON
  66: GO
  67: SET ANSI_PADDING ON
  68: GO
  69: CREATE TABLE [dbo].[City](
  70:     [CityID] [int] IDENTITY(1,1) NOT NULL,
  71:     [Name] [varchar](50) NULL,
  72:     [StateID] [int] NOT NULL,
  73:  CONSTRAINT [PK_City] PRIMARY KEY CLUSTERED 
  74: (
  75:     [CityID] ASC
  76: )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  77: ) ON [PRIMARY]
  78: GO
  79: SET ANSI_PADDING OFF
  80: GO
  81: SET IDENTITY_INSERT [dbo].[City] ON
  82: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (1, N'Hyderabad', 1)
  83: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (3, N'Bangalore', 3)
  84: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (5, N'Cochin', 5)
  85: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (6, N'Olympia', 8)
  86: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (7, N'Montgomery', 7)
  87: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (8, N'London', 9)
  88: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (9, N'Glasgow', 10)
  89: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (10, N'Liverpool', 11)
  90: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (11, N'Medan', 12)
  91: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (12, N'Padang', 12)
  92: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (13, N'Bengkulu', 12)
  93: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (14, N'Jambi', 12)
  94: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (15, N'Jakarta', 13)
  95: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (16, N'Serang', 13)
  96: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (17, N'Yogyakarta ', 13)
  97: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (18, N'Berlin', 14)
  98: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (19, N'Potsdam', 15)
  99: INSERT [dbo].[City] ([CityID], [Name], [StateID]) VALUES (20, N'Hamburg', 16)
 100: SET IDENTITY_INSERT [dbo].[City] OFF
 101: /****** Object:  Table [dbo].[Customer]    Script Date: 02/24/2011 22:34:09 ******/
 102: SET ANSI_NULLS ON
 103: GO
 104: SET QUOTED_IDENTIFIER ON
 105: GO
 106: SET ANSI_PADDING ON
 107: GO
 108: CREATE TABLE [dbo].[Customer](
 109:     [ID] [int] IDENTITY(1,1) NOT NULL,
 110:     [FirstName] [varchar](50) NULL,
 111:     [LastName] [varchar](50) NULL,
 112:     [CompanyName] [varchar](50) NULL,
 113:     [Telephone] [varchar](50) NULL,
 114:     [Email] [varchar](50) NULL,
 115:     [CountryID] [int] NULL,
 116:     [StateID] [int] NULL,
 117:     [CityID] [int] NULL,
 118:  CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED 
 119: (
 120:     [ID] ASC
 121: )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
 122: ) ON [PRIMARY]
 123: GO
 124: SET ANSI_PADDING OFF
 125: GO
 126: /****** Object:  ForeignKey [FK_City_State]    Script Date: 02/24/2011 22:34:09 ******/
 127: ALTER TABLE [dbo].[City]  WITH CHECK ADD  CONSTRAINT [FK_City_State] FOREIGN KEY([StateID])
 128: REFERENCES [dbo].[State] ([StateID])
 129: GO
 130: ALTER TABLE [dbo].[City] CHECK CONSTRAINT [FK_City_State]
 131: GO
 132: /****** Object:  ForeignKey [FK_Customer_City]    Script Date: 02/24/2011 22:34:09 ******/
 133: ALTER TABLE [dbo].[Customer]  WITH CHECK ADD  CONSTRAINT [FK_Customer_City] FOREIGN KEY([CityID])
 134: REFERENCES [dbo].[City] ([CityID])
 135: GO
 136: ALTER TABLE [dbo].[Customer] CHECK CONSTRAINT [FK_Customer_City]
 137: GO
 138: /****** Object:  ForeignKey [FK_Customer_Country]    Script Date: 02/24/2011 22:34:09 ******/
 139: ALTER TABLE [dbo].[Customer]  WITH CHECK ADD  CONSTRAINT [FK_Customer_Country] FOREIGN KEY([CountryID])
 140: REFERENCES [dbo].[Country] ([CountryID])
 141: GO
 142: ALTER TABLE [dbo].[Customer] CHECK CONSTRAINT [FK_Customer_Country]
 143: GO
 144: /****** Object:  ForeignKey [FK_Customer_State]    Script Date: 02/24/2011 22:34:09 ******/
 145: ALTER TABLE [dbo].[Customer]  WITH CHECK ADD  CONSTRAINT [FK_Customer_State] FOREIGN KEY([StateID])
 146: REFERENCES [dbo].[State] ([StateID])
 147: GO
 148: ALTER TABLE [dbo].[Customer] CHECK CONSTRAINT [FK_Customer_State]
 149: GO
 150: /****** Object:  ForeignKey [FK_State_Country]    Script Date: 02/24/2011 22:34:09 ******/
 151: ALTER TABLE [dbo].[State]  WITH CHECK ADD  CONSTRAINT [FK_State_Country] FOREIGN KEY([CountryID])
 152: REFERENCES [dbo].[Country] ([CountryID])
 153: GO
 154: ALTER TABLE [dbo].[State] CHECK CONSTRAINT [FK_State_Country]
 155: GO

Add a LINQ to SQL class file (CustomerDataClasses.dbml) to your WCF project, connect to your Customer DB database and drag the tables into the designer view of LINQ to SQL file.


On IService1.cs




   1: [ServiceContract]
   2:     public interface IService1
   3:     {
   4:         [OperationContract]
   5:         string GetCustomers();
   6:  
   7:         [OperationContract]
   8:         string GetLocations();
   9:  
  10:         [OperationContract]
  11:         string AddCustomer(string stringCustomers);
  12:     }


On the Service.svc file add the following code.




   1: public class Service1 : IService1
   2:     {
   3:         public string GetCustomers()
   4:         {
   5:             List<testapp.WcfService.DTO.Customer> custs = new List<testapp.WcfService.DTO.Customer>();
   6:  
   7:             try
   8:             {
   9:                 using (CustomerDataClassesDataContext db = new CustomerDataClassesDataContext())
  10:                 {
  11:                     List<WcfService.Data.Customer> customers = db.Customers.ToList<WcfService.Data.Customer>();
  12:                     // this is not an issue because we are saving lot of time and 
  13:                     // minimizing the data transfer using JSON Serialization
  14:                     foreach (WcfService.Data.Customer item in customers)
  15:                     {
  16:                         custs.Add(new testapp.WcfService.DTO.Customer
  17:                         {
  18:                             ID = item.ID,
  19:                             CityId = item.CityID,
  20:                             CompanyName = item.CompanyName,
  21:                             CountryId = item.CountryID,
  22:                             Email = item.Email,
  23:                             FirstName = item.FirstName,
  24:                             LastName = item.LastName,
  25:                             StateId = item.StateID,
  26:                             Telephone = item.Telephone,
  27:                             Countries = Serializer.Deserialize<List<WcfService.DTO.Country>>(GetLocations())
  28:                         });
  29:                     }
  30:                     return Serializer.Serialize<List<testapp.WcfService.DTO.Customer>>(custs);
  31:                 }
  32:             }
  33:             catch (Exception ex)
  34:             {
  35:                 // falt contract is not defined 
  36:                 // for the time being
  37:                 return null;
  38:             }
  39:         }
  40:  
  41:         public string GetLocations()
  42:         {
  43:             List<WcfService.DTO.Country> CountryItems = new List<WcfService.DTO.Country>();
  44:             try
  45:             {
  46:                 using (CustomerDataClassesDataContext db = new CustomerDataClassesDataContext())
  47:                 {
  48:                     List<WcfService.Data.Country> countries = db.Countries.ToList<WcfService.Data.Country>();
  49:                     // this is not an issue because we are saving lot of time and 
  50:                     // minimizing the data transfer using JSON Serialization
  51:                     // Not using options.loadwith
  52:                     foreach (WcfService.Data.Country item in countries)
  53:                     {
  54:                         CountryItems.Add(new WcfService.DTO.Country
  55:                         {
  56:                             CountryId = item.CountryID,
  57:                             Name = item.Name,
  58:                             States = GetStates(item.CountryID)
  59:                         });
  60:                     }
  61:                     return Serializer.Serialize<List<WcfService.DTO.Country>>(CountryItems);
  62:                 }
  63:             }
  64:             catch (Exception ex)
  65:             {
  66:                 // falt contract is not defined 
  67:                 // for the time being
  68:                 return null;
  69:             }
  70:         }
  71:  
  72:         private List<WcfService.DTO.State> GetStates(int countryId)
  73:         {
  74:             List<WcfService.DTO.State> StateItems = new List<WcfService.DTO.State>();
  75:  
  76:             using (CustomerDataClassesDataContext db = new CustomerDataClassesDataContext())
  77:             {
  78:                 List<WcfService.Data.State> states = db.States.Where(r => r.CountryID == countryId).ToList<WcfService.Data.State>();
  79:                 // this is not an issue because we are saving lot of time and 
  80:                 // minimizing the data transfer using JSON Serialization
  81:                 // Not using options.loadwith
  82:                 foreach (WcfService.Data.State item in states)
  83:                 {
  84:                     StateItems.Add(new WcfService.DTO.State
  85:                     {
  86:                         StateId = item.StateID,
  87:                         CountryId = item.CountryID,
  88:                         Name = item.Name,
  89:                         Cities = GetCities(item.StateID)
  90:                     });
  91:                 }
  92:             }
  93:             return StateItems;
  94:         }
  95:  
  96:         private List<WcfService.DTO.City> GetCities(int stateId)
  97:         {
  98:             List<WcfService.DTO.City> CityItems = new List<WcfService.DTO.City>();
  99:  
 100:             using (CustomerDataClassesDataContext db = new CustomerDataClassesDataContext())
 101:             {
 102:                 List<WcfService.Data.City> cities = db.Cities.Where(r => r.StateID == stateId).ToList<WcfService.Data.City>();
 103:                 // this is not an issue because we are saving lot of time and 
 104:                 // minimizing the data transfer using JSON Serialization
 105:                 // Not using options.loadwith
 106:                 foreach (WcfService.Data.City item in cities)
 107:                 {
 108:                     CityItems.Add(new WcfService.DTO.City
 109:                     {
 110:                         CityId = item.CityID,
 111:                         StateId = item.StateID,
 112:                         Name = item.Name
 113:                     });
 114:                 }
 115:             }
 116:  
 117:             return CityItems;
 118:         }
 119:  
 120:         public string AddCustomer(string stringCustomers)
 121:         {
 122:             if (!string.IsNullOrEmpty(stringCustomers))
 123:             {
 124:                 List<testapp.WcfService.DTO.Customer> customers = Serializer.Deserialize<List<testapp.WcfService.DTO.Customer>>(stringCustomers);
 125:                 try
 126:                 {
 127:                     using (CustomerDataClassesDataContext db = new CustomerDataClassesDataContext())
 128:                     {
 129:                         foreach (testapp.WcfService.DTO.Customer customer in customers)
 130:                         {
 131:                             if (customer.ID != 0)
 132:                             {
 133:                                 WcfService.Data.Customer existingCustomer = db.Customers.Where(r => r.ID == customer.ID).SingleOrDefault();
 134:  
 135:                                 existingCustomer.CityID = customer.CityId;
 136:                                 existingCustomer.CompanyName = customer.CompanyName;
 137:                                 existingCustomer.CountryID = customer.CountryId;
 138:                                 existingCustomer.Email = customer.Email;
 139:                                 existingCustomer.FirstName = customer.FirstName;
 140:                                 existingCustomer.LastName = customer.LastName;
 141:                                 existingCustomer.StateID = customer.StateId;
 142:                                 existingCustomer.Telephone = customer.Telephone;
 143:                             }
 144:                             else
 145:                             {
 146:                                 WcfService.Data.Customer newCustomer = new WcfService.Data.Customer
 147:                                 {
 148:                                     CityID = customer.CityId,
 149:                                     CompanyName = customer.CompanyName,
 150:                                     CountryID = customer.CountryId,
 151:                                     Email = customer.Email,
 152:                                     FirstName = customer.FirstName,
 153:                                     LastName = customer.LastName,
 154:                                     StateID = customer.StateId,
 155:                                     Telephone = customer.Telephone
 156:                                 };
 157:  
 158:                                 db.Customers.InsertOnSubmit(newCustomer);
 159:                             }
 160:                         }
 161:  
 162:                         db.SubmitChanges();
 163:                         // this is not an issue because we are saving lot of time and 
 164:                         // minimizing the data transfer using JSON Serialization
 165:  
 166:                         return GetCustomers();
 167:                     }
 168:                 }
 169:                 catch (Exception ex)
 170:                 {
 171:                     // falt contract is not defined 
 172:                     // for the time being
 173:                     return null;
 174:                 }
 175:             }
 176:             else
 177:             {
 178:                 return null;
 179:             }
 180:         }
 181:     }




Set the binding to <endpoint address="" binding="basicHttpBinding" contract="WcfService.IService1">


I am using the JSON Serialization here. If you want to know more about this topic please read my previous post


In the client side….


Add the service reference to the WCF service…


My reference name is “ServiceReference1


Now create 3 folders inside the silverlight project.


1. Model


2. View


3. View Model


Under model add your model classes. County, City, State and Customer. See the code snippet below.


Country.cs





   1: public class Country : INotifyPropertyChanged
   2:     {
   3:         #region Properties
   4:         private string _Name;
   5:         public string Name
   6:         {
   7:             get
   8:             {
   9:                 return _Name;
  10:             }
  11:             set
  12:             {
  13:                 _Name = value;
  14:                 NotifyPropertyChanged("Name");
  15:             }
  16:         }
  17:  
  18:         private int _CountryId;
  19:         public int CountryId
  20:         {
  21:             get
  22:             {
  23:                 return _CountryId;
  24:             }
  25:             set
  26:             {
  27:                 _CountryId = value;
  28:                 NotifyPropertyChanged("CountryId");
  29:             }
  30:         }
  31:  
  32:         private List<State> _States;
  33:         public List<State> States
  34:         {
  35:             get
  36:             {
  37:                 return _States;
  38:             }
  39:             set
  40:             {
  41:                 _States = value;
  42:                 NotifyPropertyChanged("States");
  43:             }
  44:         }
  45:         #endregion
  46:  
  47:         #region NotifyPropertyChanged
  48:         public event PropertyChangedEventHandler PropertyChanged;
  49:  
  50:         private void NotifyPropertyChanged(String info)
  51:         {
  52:             if (PropertyChanged != null)
  53:             {
  54:                 PropertyChanged(this, new PropertyChangedEventArgs(info));
  55:             }
  56:         }
  57:         #endregion
  58:     }


State.CS



   1: public class State : INotifyPropertyChanged
   2:     {
   3:         #region Properties
   4:         private string _Name;
   5:         public string Name
   6:         {
   7:             get
   8:             {
   9:                 return _Name;
  10:             }
  11:             set
  12:             {
  13:                 _Name = value;
  14:                 NotifyPropertyChanged("Name");
  15:             }
  16:         }
  17:         private int _CountryId;
  18:         public int CountryId
  19:         {
  20:             get
  21:             {
  22:                 return _CountryId;
  23:             }
  24:             set
  25:             {
  26:                 _CountryId = value;
  27:                 NotifyPropertyChanged("CountryId");
  28:             }
  29:         }
  30:  
  31:         private int _StateId;
  32:         public int StateId
  33:         {
  34:             get
  35:             {
  36:                 return _StateId;
  37:             }
  38:             set
  39:             {
  40:                 _StateId = value;
  41:                 NotifyPropertyChanged("StateId");
  42:             }
  43:         }
  44:  
  45:         private List<City> _Cities;
  46:         public List<City> Cities
  47:         {
  48:             get
  49:             {
  50:                 return _Cities;
  51:             }
  52:             set
  53:             {
  54:                 _Cities= value;
  55:                 NotifyPropertyChanged("Cities");
  56:             }
  57:         }
  58:         #endregion
  59:  
  60:         #region NotifyPropertyChanged
  61:         public event PropertyChangedEventHandler PropertyChanged;
  62:  
  63:         private void NotifyPropertyChanged(String info)
  64:         {
  65:             if (PropertyChanged != null)
  66:             {
  67:                 PropertyChanged(this, new PropertyChangedEventArgs(info));
  68:             }
  69:         }
  70:         #endregion
  71:     }


City.cs



   1: public class City : INotifyPropertyChanged
   2:     {
   3:         #region Properties
   4:         private string _Name;
   5:         public string Name
   6:         {
   7:             get
   8:             {
   9:                 return _Name;
  10:             }
  11:             set
  12:             {
  13:                 _Name = value;
  14:                 NotifyPropertyChanged("Name");
  15:             }
  16:         }
  17:  
  18:         private int _StateId;
  19:         public int StateId
  20:         {
  21:             get
  22:             {
  23:                 return _StateId;
  24:             }
  25:             set
  26:             {
  27:                 _StateId = value;
  28:                 NotifyPropertyChanged("StateId");
  29:             }
  30:         }
  31:  
  32:         private int _CityId;
  33:         public int CityId
  34:         {
  35:             get
  36:             {
  37:                 return _CityId;
  38:             }
  39:             set
  40:             {
  41:                 _CityId = value;
  42:                 NotifyPropertyChanged("CityId");
  43:             }
  44:         }
  45:         #endregion
  46:  
  47:         #region NotifyPropertyChanged
  48:         public event PropertyChangedEventHandler PropertyChanged;
  49:  
  50:         private void NotifyPropertyChanged(String info)
  51:         {
  52:             if (PropertyChanged != null)
  53:             {
  54:                 PropertyChanged(this, new PropertyChangedEventArgs(info));
  55:             }
  56:         }
  57:         #endregion
  58:     }


Customer.cs



   1: public class Customer : INotifyPropertyChanged
   2:     {
   3:         #region Properties
   4:         private int _ID;
   5:         public int ID
   6:         {
   7:             get
   8:             {
   9:                 return _ID;
  10:             }
  11:             set
  12:             {
  13:                 _ID = value;
  14:                 NotifyPropertyChanged("ID");
  15:             }
  16:         }
  17:         private string _FirstName;
  18:         public string FirstName
  19:         {
  20:             get
  21:             {
  22:                 return _FirstName;
  23:             }
  24:             set
  25:             {
  26:                 _FirstName = value;
  27:                 NotifyPropertyChanged("FirstName");
  28:             }
  29:         }
  30:  
  31:         private string _LastName;
  32:         public string LastName
  33:         {
  34:             get
  35:             {
  36:                 return _LastName;
  37:             }
  38:             set
  39:             {
  40:                 _LastName = value;
  41:                 NotifyPropertyChanged("LastName");
  42:             }
  43:         }
  44:  
  45:  
  46:         private string _CompanyName;
  47:         public string CompanyName
  48:         {
  49:             get
  50:             {
  51:                 return _CompanyName;
  52:             }
  53:             set
  54:             {
  55:                 _CompanyName = value;
  56:                 NotifyPropertyChanged("CompanyName");
  57:             }
  58:         }
  59:  
  60:         private string _Country;
  61:         public string Country
  62:         {
  63:             get
  64:             {
  65:                 return _Country;
  66:             }
  67:             set
  68:             {
  69:                 _Country = value;
  70:                 NotifyPropertyChanged("Country");
  71:             }
  72:         }
  73:  
  74:         private Country _SelectedCountry;
  75:         public Country SelectedCountry
  76:         {
  77:             get
  78:             {
  79:                 return _SelectedCountry;
  80:             }
  81:             set
  82:             {
  83:                 _SelectedCountry = value;
  84:                 if (_SelectedCountry != null)
  85:                     this.States = _SelectedCountry.States;
  86:                 NotifyPropertyChanged("SelectedCountry");
  87:             }
  88:         }
  89:  
  90:         private State _SelectedState;
  91:         public State SelectedState
  92:         {
  93:             get
  94:             {
  95:                 return _SelectedState;
  96:             }
  97:             set
  98:             {
  99:                 _SelectedState = value;
 100:                 if (_SelectedState != null)
 101:                     this.Cities = _SelectedState.Cities;
 102:                 NotifyPropertyChanged("SelectedState");
 103:             }
 104:         }
 105:  
 106:         private City _SelectedCity;
 107:         public City SelectedCity
 108:         {
 109:             get
 110:             {
 111:                 return _SelectedCity;
 112:             }
 113:             set
 114:             {
 115:                 _SelectedCity = value;
 116:                 NotifyPropertyChanged("SelectedCity");
 117:             }
 118:         }
 119:  
 120:         private string _State;
 121:         public string State
 122:         {
 123:             get
 124:             {
 125:                 return _State;
 126:             }
 127:             set
 128:             {
 129:                 _State = value;
 130:                 NotifyPropertyChanged("State");
 131:             }
 132:         }
 133:  
 134:         private string _Telephone;
 135:         public string Telephone
 136:         {
 137:             get
 138:             {
 139:                 return _Telephone;
 140:             }
 141:             set
 142:             {
 143:                 _Telephone = value;
 144:                 NotifyPropertyChanged("Telephone");
 145:             }
 146:         }
 147:  
 148:         private string _Email;
 149:         public string Email
 150:         {
 151:             get
 152:             {
 153:                 return _Email;
 154:             }
 155:             set
 156:             {
 157:                 _Email = value;
 158:                 NotifyPropertyChanged("Email");
 159:             }
 160:         }
 161:  
 162:         private List<Country> _Countries;
 163:         public List<Country> Countries
 164:         {
 165:             get
 166:             {
 167:                 return _Countries;
 168:             }
 169:             set
 170:             {
 171:                 _Countries = value;
 172:  
 173:                 NotifyPropertyChanged("Countries");
 174:             }
 175:         }
 176:  
 177:         private List<City> _Cities;
 178:         public List<City> Cities
 179:         {
 180:             get
 181:             {
 182:                 return _Cities;
 183:             }
 184:             set
 185:             {
 186:                 _Cities = value;
 187:                 NotifyPropertyChanged("Cities");
 188:             }
 189:         }
 190:  
 191:         private List<State> _States;
 192:         public List<State> States
 193:         {
 194:             get
 195:             {
 196:                 return _States;
 197:             }
 198:             set
 199:             {
 200:                 _States = value;
 201:                 NotifyPropertyChanged("States");
 202:             }
 203:         }
 204:  
 205:         private int _StateId;
 206:         public int StateId
 207:         {
 208:             get
 209:             {
 210:                 return _StateId;
 211:             }
 212:             set
 213:             {
 214:                 _StateId = value;
 215:                 NotifyPropertyChanged("StateId");
 216:             }
 217:         }
 218:  
 219:         private int _CityId;
 220:         public int CityId
 221:         {
 222:             get
 223:             {
 224:                 return _CityId;
 225:             }
 226:             set
 227:             {
 228:                 _CityId = value;
 229:                 NotifyPropertyChanged("CityId");
 230:             }
 231:         }
 232:  
 233:         private int _CountryId;
 234:         public int CountryId
 235:         {
 236:             get
 237:             {
 238:                 return _CountryId;
 239:             }
 240:             set
 241:             {
 242:                 _CountryId = value;
 243:                 NotifyPropertyChanged("CountryId");
 244:             }
 245:         }
 246:         #endregion
 247:  
 248:         #region NotifyPropertyChanged
 249:         public event PropertyChangedEventHandler PropertyChanged;
 250:  
 251:         private void NotifyPropertyChanged(String info)
 252:         {
 253:             if (PropertyChanged != null)
 254:             {
 255:                 PropertyChanged(this, new PropertyChangedEventArgs(info));
 256:             }
 257:         }
 258:         #endregion
 259:     }


View Model…





   1: public class ViewModelCustomer : INotifyPropertyChanged
   2:     {
   3:         private Service1Client client;
   4:  
   5:         private ObservableCollection<Customer> _CustomerItems;
   6:         public ObservableCollection<Customer> CustomerItems
   7:         {
   8:             get
   9:             {
  10:                 return _CustomerItems;
  11:             }
  12:             set
  13:             {
  14:                 _CustomerItems = value;
  15:                 NotifyPropertyChanged("CustomerItems");
  16:             }
  17:         }
  18:  
  19:         private Customer _SelectedCustomer;
  20:         public Customer SelectedCustomer
  21:         {
  22:             get
  23:             {
  24:                 return _SelectedCustomer;
  25:             }
  26:             set
  27:             {
  28:                 _SelectedCustomer = value;
  29:                 NotifyPropertyChanged("SelectedCustomer");
  30:             }
  31:         }
  32:  
  33:         private List<Country> _Countries;
  34:         public List<Country> Countries
  35:         {
  36:             get
  37:             {
  38:                 return _Countries;
  39:             }
  40:             set
  41:             {
  42:                 _Countries = value;
  43:  
  44:                 NotifyPropertyChanged("Countries");
  45:             }
  46:         }
  47:  
  48:         public ICommand Click { get; set; }
  49:  
  50:         public ViewModelCustomer()
  51:         {
  52:             CustomerItems = new ObservableCollection<Customer>();
  53:             client = new Service1Client();
  54:             Click = new DelegateCommand<object>(OnClick);
  55:             client.GetCustomersCompleted += client_GetCustomersCompleted;
  56:             client.GetLocationsCompleted += client_GetLocationsCompleted;
  57:             client.GetLocationsAsync();
  58:         }
  59:  
  60:         void client_GetLocationsCompleted(object sender, GetLocationsCompletedEventArgs e)
  61:         {
  62:             if (e.Result != null)
  63:             {
  64:                 Countries = Serializer.Deserialize<List<Country>>(e.Result);
  65:                 client.GetCustomersAsync();
  66:             }
  67:         }
  68:  
  69:         private void OnClick(object arguments)
  70:         {
  71:             // save data;
  72:             if (arguments.ToString() == "Save")
  73:             {
  74:                 Service1Client client = new Service1Client();
  75:                 client.AddCustomerCompleted += client_AddCustomerCompleted;
  76:                 client.AddCustomerAsync(Serializer.Serialize<ObservableCollection<Customer>>(CustomerItems));
  77:             }
  78:             if (arguments.ToString() == "New")
  79:             {
  80:                 Customer newCustomer = new Customer
  81:                 {
  82:                     Countries = Countries,
  83:                     States = new List<State>(),
  84:                     Cities = new List<City>()
  85:                 };
  86:                 this.CustomerItems.Add(newCustomer);
  87:                 NotifyPropertyChanged("CustomerItems");
  88:             }
  89:         }
  90:  
  91:         void client_AddCustomerCompleted(object sender, AddCustomerCompletedEventArgs e)
  92:         {
  93:             if (e.Result != null)
  94:             {
  95:                 RefreshGrid(e.Result);
  96:             }
  97:         }
  98:  
  99:         void client_GetCustomersCompleted(object sender, GetCustomersCompletedEventArgs e)
 100:         {
 101:             if (e.Result != null)
 102:             {
 103:                 RefreshGrid(e.Result);
 104:             }
 105:         }
 106:  
 107:         private void RefreshGrid(string strJSON)
 108:         {
 109:             CustomerItems.Clear();
 110:             CustomerItems = Serializer.Deserialize<ObservableCollection<Customer>>(strJSON);
 111:             foreach (Customer item in this.CustomerItems)
 112:             {
 113:                 item.SelectedCountry = item.Countries.Where(r => r.CountryId == item.CountryId).SingleOrDefault();
 114:                 if (item.SelectedCountry != null)
 115:                     item.States = item.SelectedCountry.States;
 116:                 if (item.States != null)
 117:                     item.SelectedState = item.States.Where(r => r.StateId == item.StateId).SingleOrDefault();
 118:                 if (item.SelectedState != null)
 119:                     item.Cities = item.SelectedState.Cities;
 120:                 if (item.Cities != null)
 121:                     item.SelectedCity = item.Cities.Where(r => r.CityId == item.CityId).SingleOrDefault();
 122:             }
 123:         }
 124:  
 125:         #region NotifyPropertyChanged
 126:         public event PropertyChangedEventHandler PropertyChanged;
 127:  
 128:         private void NotifyPropertyChanged(String info)
 129:         {
 130:             if (PropertyChanged != null)
 131:             {
 132:                 PropertyChanged(this, new PropertyChangedEventArgs(info));
 133:             }
 134:         }
 135:         #endregion
 136:     }


View am not using now.. I kept the view outside for the time being…


Add a data grid into your mainpage.xaml file…… see the code snippet below.



   1: <Grid.RowDefinitions>
   2:             <RowDefinition></RowDefinition>
   3:             <RowDefinition></RowDefinition>
   4:         </Grid.RowDefinitions>
   5:         <data:DataGrid
   6:             AutoGenerateColumns="True"
   7:             Height="250"
   8:             HorizontalAlignment="Left"
   9:             VerticalAlignment="Top"
  10:             Width="700"
  11:             ItemsSource="{Binding CustomerItems, Mode=TwoWay}"
  12:             SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}">
  13:             <data:DataGrid.Columns>
  14:                 <data:DataGridTemplateColumn Header="First Name">
  15:                     <data:DataGridTemplateColumn.CellTemplate>
  16:                         <DataTemplate>
  17:                             <TextBox Text="{Binding FirstName, Mode=TwoWay}" />
  18:                         </DataTemplate>
  19:                     </data:DataGridTemplateColumn.CellTemplate>
  20:                 </data:DataGridTemplateColumn>
  21:                 <data:DataGridTemplateColumn Header="Last Name">
  22:                     <data:DataGridTemplateColumn.CellTemplate>
  23:                         <DataTemplate>
  24:                             <TextBox Text="{Binding LastName, Mode=TwoWay}" />
  25:                         </DataTemplate>
  26:                     </data:DataGridTemplateColumn.CellTemplate>
  27:                 </data:DataGridTemplateColumn>
  28:                 <data:DataGridTemplateColumn Header="Company Name">
  29:                     <data:DataGridTemplateColumn.CellTemplate>
  30:                         <DataTemplate>
  31:                             <TextBox Text="{Binding CompanyName, Mode=TwoWay}" />
  32:                         </DataTemplate>
  33:                     </data:DataGridTemplateColumn.CellTemplate>
  34:                 </data:DataGridTemplateColumn>
  35:                 <data:DataGridTemplateColumn Header="Country" Width="50">
  36:                     <data:DataGridTemplateColumn.CellTemplate>
  37:                         <DataTemplate>
  38:                             <ComboBox 
  39:                                 ItemsSource="{Binding Countries}"
  40:                                 SelectedItem="{Binding SelectedCountry, Mode=TwoWay}"
  41:                                 DisplayMemberPath="Name"/>
  42:                         </DataTemplate>
  43:                     </data:DataGridTemplateColumn.CellTemplate>
  44:                 </data:DataGridTemplateColumn>
  45:                 <data:DataGridTemplateColumn Header="State" Width="50">
  46:                     <data:DataGridTemplateColumn.CellTemplate>
  47:                         <DataTemplate>
  48:                             <ComboBox 
  49:                                 ItemsSource="{Binding States}"
  50:                                 SelectedItem="{Binding SelectedState, Mode=TwoWay}"
  51:                                 DisplayMemberPath="Name"/>
  52:                         </DataTemplate>
  53:                     </data:DataGridTemplateColumn.CellTemplate>
  54:                 </data:DataGridTemplateColumn>
  55:                 <data:DataGridTemplateColumn Header="City" Width="50">
  56:                     <data:DataGridTemplateColumn.CellTemplate>
  57:                         <DataTemplate>
  58:                             <ComboBox 
  59:                                 ItemsSource="{Binding Cities}"
  60:                                 SelectedItem="{Binding SelectedCity, Mode=TwoWay}"
  61:                                 DisplayMemberPath="Name"/>
  62:                         </DataTemplate>
  63:                     </data:DataGridTemplateColumn.CellTemplate>
  64:                 </data:DataGridTemplateColumn>
  65:                 <data:DataGridTemplateColumn Header="Telephone">
  66:                     <data:DataGridTemplateColumn.CellTemplate>
  67:                         <DataTemplate>
  68:                             <TextBox Text="{Binding Telephone, Mode=TwoWay}" />
  69:                         </DataTemplate>
  70:                     </data:DataGridTemplateColumn.CellTemplate>
  71:                 </data:DataGridTemplateColumn>
  72:                 <data:DataGridTemplateColumn Header="Email">
  73:                     <data:DataGridTemplateColumn.CellTemplate>
  74:                         <DataTemplate>
  75:                             <TextBox Text="{Binding Email, Mode=TwoWay}" />
  76:                         </DataTemplate>
  77:                     </data:DataGridTemplateColumn.CellTemplate>
  78:                 </data:DataGridTemplateColumn>
  79:             </data:DataGrid.Columns>
  80:         </data:DataGrid>
  81:         <StackPanel
  82:             Grid.Row="1">
  83:             <Button Content="New" Height="23" HorizontalAlignment="Left" cmd:Click.Command="{Binding Click}" cmd:Click.CommandParameter="New"  VerticalAlignment="Top" Width="96" />
  84:             <Button Content="Save" Height="23" HorizontalAlignment="Left" cmd:Click.Command="{Binding Click}" cmd:Click.CommandParameter="Save"  VerticalAlignment="Top" Width="96" />
  85:         </StackPanel>


And finally initialize your view model inside view….



   1: ViewModelCustomer VMCustomer;
   2:         public MainPage()
   3:         {
   4:             InitializeComponent();
   5:             VMCustomer = new ViewModelCustomer();
   6:             this.DataContext = VMCustomer;
   7:         }
You can download the source code here.


Anilal Sambasivan