My Master's Thesis Problems and solutions encountered…

8Jul/102

How to populate a combobox in a MVVM design pattern

This post just explains how to populate a dropdown box in your Silverlight application. It's almost the same as in the previous post, but I will just repeat it here, as I will be using this multiple times. One difference between this and the former post, is that we work with an ObservableCollection instead of normal strings, and another difference lies in having to populate a dropbox, which requires a partial class that makes sure that the data from the database is printed, and not the name of the object.

As in the previous post, this post will not be so thorough, and should anybody want to retrieve data from a MS SQL database trough a MVVM pattern with WCF, they should start by reading and following the steps on these two posts: MVVM structure and Deploying WCF Services.

Step 1: Model project -> Model folder -> New class.

I have called the new class ParentQuestionnaires.cs, and it will contain the data that I eventually want to retrieve from the database and insert into the application.


*
using System.Runtime.Serialization; 

namespace Model.Model
{
    [DataContract(Name = "ParentQuestionnaire")]
    public class ParentQuestionnaires
    {

        string _parentQuestionnaireName;
        [DataMember]
        public string ParentQuestionnaireName
        {
            get { return _parentQuestionnaireName; }
            set { _parentQuestionnaireName = value; }
        }

    }
}

Step 2: Model project -> Data_access folder -> New class.

I have called my class ParentQuestionnairesDA.cs, and it will contain the connection to the database and the query.

*using Model.Model;
using System.Data.SqlClient; 

namespace Model.Data_access
{
    public class ParentQuestionnairesDA: DBConnection
    {

        public List<ParentQuestionnaires> GetParentQuestionnaire(int UID)
        {

            List<ParentQuestionnaires> PQlist = new List<ParentQuestionnaires>();

            SqlConnection dbConn = connectToDB();
            string _selectQuery = string.Format("select blablbalbla from blabla where blabla = " + UID + ";");

            try
            {
                dbConn.Open();
                SqlCommand cmd = new SqlCommand(_selectQuery, dbConn);
                SqlDataReader rdr = cmd.ExecuteReader();
                while (rdr.Read())
                {
                    ParentQuestionnaires PQ = new ParentQuestionnaires();
                    PQ.ParentQuestionnaireName = (string)rdr[0];
                    PQlist.Add(PQ); 

                }

            }
            catch(Exception e) {

                Console.Write(e);
            }

            return PQlist; 

        }

    }
}

Step 3: Model project ->> IService1.cs

Connects client and server-side.


**
using Model.Model;

namespace Model
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        List<ParentQuestionnaires> GetParentQuestionnaire(int UID);
    }
}

Step 4: Model project -> Service1.svc.cs

Creates new instances of the two classes created above, calls the GetParentQuestionnaire(UID); method

**
using System.Data.SqlClient;
using Model.Model;
using Model.Data_access;

namespace Model
{

    public class Service1 : IService1
    {
        public List<ParentQuestionnaires> GetParentQuestionnaire(int UID)
        {

            List<ParentQuestionnaires> allQuestionnaires;
            ParentQuestionnairesDA AllDA = new ParentQuestionnairesDA();
            allQuestionnaires = AllDA.GetParentQuestionnaire(UID);
            return allQuestionnaires;
        }
    }
}

Step 5: This is the step that differentiates this guide from the rest: We need to make a partial class, that will make sure that we data from the database is printed in dropdown box - not the name of the object. First, create a new folder in your ViewModel, where you will put this class. I have named mine Model.

ViewModel project -> Model folder -> create class. I have called mine ParentQuestionnaire.cs


*using System.Windows.Shapes;

namespace ViewModel.QMServiceReference // name of your Service Reference!
{
    public partial class ParentQuestionnaire //will be called in the next step.
    {
        public override string ToString()
        {
            return this.ParentQuestionnaireName; //created in step 1.
        }
    }
}

After this step, you need to publish the Model and update your ServiceReference.

Step 6: ViewModel -> Create new class. I always name mine after the .xaml page where I want to insert the data, followeb by "_ViewModel". Hence, I have called mine .

*
using ViewModel.QMServiceReference;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace ViewModel
{
    public class Client_Questionnaire_ViewModel : INotifyPropertyChanged //inportant!
    {
        private ObservableCollection<ParentQuestionnaire> parentQuest; //ParentQuestionnaire, created in the previous step.
        public ObservableCollection<ParentQuestionnaire> ParentQuest
        {
            get { return parentQuest;  }
            set { parentQuest = value;
                    RaisePropertyChanged("ParentQuest"); 

            }
        }

        public Client_Questionnaire_ViewModel()
        {
            int UID = 2; //for now, hardcoding.
            QMServiceReference.Service1Client WebService = new Service1Client();
            WebService.GetParentQuestionnaireCompleted += new EventHandler<GetParentQuestionnaireCompletedEventArgs>(WebService_GetParentQuestionnaireCompleted);
            WebService.GetParentQuestionnaireAsync(UID);
        }
        void WebService_GetParentQuestionnaireCompleted(object sender, GetParentQuestionnaireCompletedEventArgs e)
        {
            ParentQuest = e.Result;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
            }
        }
    }
}

And lastly: Insert into the combo box into your .xaml page:

  <ComboBox  x:Name="ParentQuestionnairej" ItemsSource="{Binding ParentQuest}" HorizontalAlignment="Left" Width="150" Height="20"  Style="{StaticResource ComboBox2}" Margin="0,2,0,0" Grid.Column="1" Grid.Row="0" />
14Jun/102

MS SQL Connection – Deploying WCF Services

So, I found a host for my WCF Service, which uses a IIS 7 server that can run my .svc WebService file. I could connect to my MySQL database hosted another place, but given that my new host uses MS SQL, I will be migrating to that.

This post explains how you can deploy your WCF Service and connect to the underlying database, and how to retrieve data all the way through the MVVM Design Pattern. The prerequisites for this post is that you have 1) created a project with a design pattern that somewhat resembles the MVVM covered in my previous post (see MVVM Structure), and 2) that you have created a table in a MS SQL database called 'Suppliers', and inserted three attributes called ID, name and country, respectively.

First, make sure that you have 3 projects in your solution, fx. called Model (a WCF Service Application), View (a Silverlight application), and ViewModel (a Silverlight class). You have connected the projects View and ViewModel together like it was described in step 4 of the previous post.

Step 1: Just to make sure that you have all the right information in order to deploy to your server, right-click on your Model project >> Publish, and enter all necessary data. Notice that you have to enter ftp://yourdomain.net and not just ftp.yourdomain.net.

If you have entered the correct data, the following output will be shown, and the WCF Service will have been added to the root of your server:

Step 2: Go to your Model project and chose Web.Config. Find the row that starts with < system.serviceModel>. It will look something like this:

 <system.serviceModel>
<!--Add code here -->
    <services>
      <service name="Model.Service1" behaviorConfiguration="Model.Service1Behavior">
        <!-- Service Endpoints -->
        <endpoint address="" binding="wsHttpBinding" contract="Model.IService1">
 (...)

Step 3: Right below the < system.serviceModel> and before < services> starts, you'll need to add the following code snippet:


  <serviceHostingEnvironment>
          <baseAddressPrefixFilters>
              <add prefix="http://yourDomain.net"/>
          </baseAddressPrefixFilters>
      </serviceHostingEnvironment>

Step 4: Then, a small change needs to be implemented to the endpoint binding. Shown in the code below step 2, you can see that the standard binding is wsHttpBinding. This needs to be changed to BasicHttpBinding.

After having completed step 3 and 4, you will thus end up with the code below:


<system.serviceModel>

      <serviceHostingEnvironment>
          <baseAddressPrefixFilters>
              <add prefix="http://yourDomain.net"/>
          </baseAddressPrefixFilters>
      </serviceHostingEnvironment>

    <services>
      <service name="Model.Service1" behaviorConfiguration="Model.Service1Behavior">
        <!-- Service Endpoints -->
        <endpoint address="" binding="BasicHttpBinding" contract="Model.IService1">
          <!-- 

Step 5: Repeat step 1., ie. right-click on your WebSevice >> Publish. You will then notice that two files and folder have been added to the root of your server: A Service1.svc and a Web.Config file, and a Bin folder.

Step 6: Still in your Model project, open your IService1.cs. Here you will create a class that will contain the attributes and properties for the Suppliers object that we create. Also notice the code inside the brackets [], fx. [OperationContract] and [DataMember], which make the methods and properties available to the other projects.


namespace Model
{

    [DataContract]//Makes the class available
    public class Suppliers //creates the objectet Suppliers, and defines its attributtes (properties).
    {
        int _ID;
        string _name;
        string _country;

        [DataMember]//makes the property available
        public int ID
        {
            get { return _ID; }
            set { _ID = value; }
        }
        [DataMember]
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        [DataMember]
        public string Country
        {
            get { return _country; }
            set { _country = value; }
        }

    }

Step 7: Open your Service1.svc.cs file in the Model project. In this file we will connect to the database and run our queries. When you open the file you'll notice that a lot of code has already been created for you. Below is the standard content of the Service1.svc file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace Model
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
    public class Service1 : IService1
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }

        public CompositeType GetDataUsingDataContract(CompositeType composite)
        {
            if (composite == null)
            {
                throw new ArgumentNullException("composite");
            }
            if (composite.BoolValue)
            {
                composite.StringValue += "Suffix";
            }
            return composite;
        }
    }
}

You just need to delete everything inside the class, and instead insert what is explained in the following steps.

Step 8: Add a reference to using System.Data.SqlClient; at the top.

Step 9: Create a SqlConnection ConnectToDB() method, that sets the connections string and returns the Sql Connection. If you have Winhost, you will find what you need to insert into your Connection String by logging in >> Site >> Site Tools >> MS SQL 2008 >> Manage. The ConnectToDB method looks something like this:


  public SqlConnection connectToDB()
        {
            string connstring = "Data Source=************;Initial Catalog=*****;User ID=******;Password=*******;Integrated Security=False;";
            SqlConnection dbConn = new SqlConnection(connstring);

            return dbConn;
        }

Step 8: Now we will add the method that will query the database and return a List of data that ultimately will be inserted into the Silverlight application. Notice that we create a method called getSupplierList() of type List< Suppliers>, where Suppliers is the name of the class created in step 6.

    public List<Suppliers> getSupplierList()
        {

            List<Suppliers> SupplierList = new List<Suppliers>(); //list that will containt all data retrieved from the database. 

            SqlConnection dbConn = connectToDB();//Method above
            string _selectQuery = string.Format("SELECT * FROM suppliers");//Query
            try
            {
                dbConn.Open();
                //Creates the comand object and sets the query of the command.
                SqlCommand cmd = new SqlCommand(_selectQuery, dbConn); //Executes
                SqlDataReader rdr = cmd.ExecuteReader(); //loop, read data

                while (rdr.Read())//for hver række der bliver hentet ind, gør vi følgende
                {

                    Suppliers Sup = new Suppliers();

                    Sup.ID = (int)rdr[0];
                    Sup.Name = (string)rdr[1];
                    Sup.Country = (string)rdr[2];

                    SupplierList.Add(Sup); //row is added

                }

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            return SupplierList;
        }
    }

Step 9: Open IService1.cs again. Before the public class Suppliers that you created earlier, add the OperationContract shown below, that exposes your getSupplierList() method and makes it accessable to the ViewModel.

[ServiceContract]
    public interface IService1
    {

        [OperationContract]
        List<Suppliers> getSupplierList(); //Name of method in Service1

    }

Step 10: Repeat step 1 (publish).

Now, we need to connect the ViewModel to the Model.

Step 11: On your ViewModel, right-click on References, and chose Add new Webservice. Here you will want to find the webService you have uploaded to the server. In the address field, enter http://yourDomain.com/Service1.svc, like shown below. Name your WebService (I have named mine QMWebService) and click 'OK'.

Now, you have connected the ViewModel to the Model.

If your service is not hosted locally and your Silverlight is, then you will get a warning about CrossDomain policy. To enable your service to receive calls from your locally hosted application, follow step 12:

Step 12: Create a new .xml file that contains the following code (source).


<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="SOAPAction">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

Copy the file to the root of your server.

Step 13: This step is necessary in order for the View to reflect the changes made to it when the property in the ViewModel is set (updated). You will have to add a class to your ViewModel, and just copy-paste the code from below. It is taken from the MVVM demo application by Josh Smith, and can be also be found here. Name the class ViewModelBase.cs.

Name the class ViewModelBase, as all classes in the ViewModel are inherited from it.


using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Diagnostics;

namespace SilverLightViewModel
{
    /// <summary>
    /// Base class for all ViewModel classes in the application.
    /// It provides support for property change notifications
    /// and has a DisplayName property.  This class is abstract.
    /// </summary>
    public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
    {
        #region Constructor

        protected ViewModelBase()
        {
        }

        #endregion // Constructor

        #region DisplayName

        /// <summary>
        /// Returns the user-friendly name of this object.
        /// Child classes can set this property to a new value,
        /// or override it to determine the value on-demand.
        /// </summary>
        public virtual string DisplayName { get; protected set; }

        #endregion // DisplayName

        #region Debugging Aides

        /// <summary>
        /// Warns the developer if this object does not have
        /// a public property with the specified name. This
        /// method does not exist in a Release build.
        /// </summary>
        //[Conditional("DEBUG")]
        //[DebuggerStepThrough]
        //public void VerifyPropertyName(string propertyName)
        //{
        //    // Verify that the property name matches a real,
        //    // public, instance property on this object.
        //    if (TypeDescriptor.GetProperties(this)[propertyName] == null)
        //    {
        //        string msg = "Invalid property name: " + propertyName;

        //        if (this.ThrowOnInvalidPropertyName)
        //            throw new Exception(msg);
        //        else
        //            Debug.Fail(msg);
        //    }
        //}

        /// <summary>
        /// Returns whether an exception is thrown, or if a Debug.Fail() is used
        /// when an invalid property name is passed to the VerifyPropertyName method.
        /// The default value is false, but subclasses used by unit tests might
        /// override this property's getter to return true.
        /// </summary>
        protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

        #endregion // Debugging Aides

        #region INotifyPropertyChanged Members

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

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

            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #endregion // INotifyPropertyChanged Members

        #region IDisposable Members

        /// <summary>
        /// Invoked when this object is being removed from the application
        /// and will be subject to garbage collection.
        /// </summary>
        public void Dispose()
        {
            this.OnDispose();
        }

        /// <summary>
        /// Child classes can override this method to perform
        /// clean-up logic, such as removing event handlers.
        /// </summary>
        protected virtual void OnDispose()
        {
        }

#if DEBUG
        /// <summary>
        /// Useful for ensuring that ViewModel objects are properly garbage collected.
        /// </summary>
        ~ViewModelBase()
        {
            string msg = string.Format("{0} ({1}) ({2}) Finalized", this.GetType().Name, this.DisplayName, this.GetHashCode());
            System.Diagnostics.Debug.WriteLine(msg);
        }
#endif

        #endregion // IDisposable Members
    }
}

Step 14: The next step is to go to your MainPage_ViewModel, that will contain the necessary properties and eventhandlers. The aim with this class is to register changes, call the WebService, and retrieve the data that will be inserted into the .xaml Grid, that will be created in the next step.

MainPage_ViewModel:


using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using SilverLightViewModel;//new
using System.Collections.ObjectModel;//new
using ViewModel.QMServiceReference; //new

namespace ViewModel
{
    public class MainPage_ViewModel : ViewModelBase
    {
        private ObservableCollection<Suppliers> _suppliers; //Collection registres changes. Updates run.
        //list of all supplies

        public MainPage_ViewModel()
        {

            QMServiceReference.Service1Client WebService = new Service1Client();
            WebService.getSupplierListCompleted += new EventHandler<getSupplierListCompletedEventArgs>(WebService_getSupplierListCompleted);//Eventhandler is trigged when the webservice call has been run, and data han been retrieved
            WebService.getSupplierListAsync(); //calls webservice. 

        }

        void WebService_getSupplierListCompleted(object sender, getSupplierListCompletedEventArgs e)
        {

            //Is called when the webservice returnes a list 

            Suppliers = e.Result;
        }

        # region Properties

        public ObservableCollection<Suppliers> Suppliers
        {
            get { return _suppliers; } //instance of Suppliers
            set
            {
                _suppliers = value;
                OnPropertyChanged("Suppliers");
            }//property name
        }

        # endregion

    }
}

Step 15 (last step!): Create a Grid in your MainPage.xaml, like the one shown below. Notice that the name of the binding attribute is not named after the name you have created in your database, but rather in what you declared them in your Model, in the Suppliers class (see step 6).

MainPage.xaml:

 <data:DataGrid x:Name="SupplerGrid" MinHeight="100" IsReadOnly="True" AutoGenerateColumns="False" ItemsSource="{Binding Suppliers}">
                <data:DataGrid.Columns>
                    <data:DataGridTextColumn x:Name="ID" Header="ID" Binding="{Binding ID}" />
                    <data:DataGridTextColumn x:Name="ID2" Header="Name" Binding="{Binding Name}" />
                    <data:DataGridTextColumn x:Name="testData" Header="Value" Binding="{Binding Country}" />
                </data:DataGrid.Columns>
            </data:DataGrid>

And no... this time I won't end the post by stating "And that's it!", because this task is horrible, frustrating and endlessly time-consuming. But it works. I hope this post will help somebody get through the process costing only a limited amount of blood, sweat and tears.

Enjoy!