My Master's Thesis Problems and solutions encountered…

17Jul/100

Combobox two-way binding in MVVM

Almost all applications have a need of retrieving data into a combobox, and do something depending on what the user choses from the combobox. This is also the case in my application, and luckily Silverlight 4 has a pretty good way of dealing with the situation.

In this post I will show how you can populate a textbox with a QuestionnaireID depending on which QuestionnaireName the user has chosen from the combobox. Obviously, this is just a proof of method, as the the retrieved QuestionnaireID has another purpose (in my case it will be passed to a child window, and be used to update the database with the changes the user wants to make to the questionnaire). But for now, I will just show how you can solve the task taking into consideration the MVVM design structure.

I can recommend looking into the blog post called Binding to Silverlight ComboBox and Using SelectedValue, SelectedValuePath and DisplayMemberPath, if any questions come up, as you can download the complete source code from there.

So, as with all my other posts, the prerequisite is that you have a Model project where the objects are defined and populated with data fromn the database, and a View where you have all your .xaml files, and the ViewModel, that binds the two together and calls the Webservice.

Step 1: Go to your Model, and add a FirePropertyChanged to each of the attributes of the object.

    [DataContract(Name = "AllQuestionnaires")]
    public class DropBox_Questionnaire : INotifyPropertyChanged
    {

        string _questionnaireName;
        [DataMember]
        public string QuestionnaireName
        {
            get { return _questionnaireName; }
            set { _questionnaireName = value;
            FirePropertyChanged("QuestionnaireName");

            }
        }

        int _questionnaireID;
        [DataMember]
        public int QuestionnaireID
        {
            get { return _questionnaireID; }
            set { _questionnaireID = value;
            FirePropertyChanged("QuestionnaireID");
            }
        }

//INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void FirePropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

    }

So you have now prepared the attributes for the two-way binding.

Step 2: In your ViewModel, create a SelectedQuestionnaire property that will contain the QuestionnaireID of the questionnaire, that the user has chosen. I have added all code, including the property for the Combobox, and the WebService call.


//Combox
      private ObservableCollection<AllQuestionnaires> _questList;
        public ObservableCollection<AllQuestionnaires> QuestList
        {
            get { return _questList; }
            set
            {
                _questList = value;
                RaisePropertyChanged("QuestList");
            }
        }

//Selected value
  private int _selectedQuestionnaire;
        public int SelectedQuestionnaire
        {
            get { return _selectedQuestionnaire; }
            set
            {
                _selectedQuestionnaire = value;
                RaisePropertyChanged("SelectedQuestionnaire");

            }
        }

//Constructor, Calls webservice
   public Client_Questionnaire2_ViewModel()
        {
            int UID = 2;
            int pq_ID = 1; //temporary hadrcoding

            QMServiceReference.Service1Client WebService = new Service1Client();

            WebService.GetAllQuestionnairesCompleted += new EventHandler<GetAllQuestionnairesCompletedEventArgs>(WebService_GetAllQuestionnairesCompleted);
            WebService.GetAllQuestionnairesAsync(UID, pq_ID);
}
//sets QuestList Property, returns data from database
   void WebService_GetAllQuestionnairesCompleted(object sender, GetAllQuestionnairesCompletedEventArgs e)
        {
            QuestList = e.Result;
        }

Step 3: Define the combobox in your xaml page in the View, and notice the ItemSource (that binds to the object) and the SelectedValue, that binds to the string property SelectedQuestionnaire that will contain what the user chooses (hence the TwoWay binding). The DisplayMemberPath and SelectedValuePath define the object attributes that the combobox will show, and the value it will send to SelectedQuestionnaire, respectively.

<!-- The combobox -->
<ComboBox  x:Name="QuestonnaireTest"
         ItemsSource="{Binding Path=QuestList, Mode=OneWay}"  //QuestList, property from ViewModel
         SelectedValue="{Binding Path=SelectedQuestionnaire, Mode=TwoWay}" //SelectedQuestionnaire, property from ViewModel
         DisplayMemberPath="QuestionnaireName" //Attribute from Model
         SelectedValuePath="QuestionnaireID" //Attribute from Model

/>

<!-- The textblock, where the QuestionnaireID is inserted -->

<TextBlock Name="textBlock" Text="{Binding Path=SelectedQuestionnaire, Mode=OneWay}" />

Enjoy!

Filed under: C#, MVVM, Silverlight 4 No Comments
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!

13Jun/100

MVVM Structure

This post explains how to set up a project according to the MVVM Design Pattern (Model-View-ViewModel). What needs to be done is to create three projects called Model (WCF Service Application), View (Silverlight application), and ViewModel (Silverlight class):

Step 1: Add a new project named Model: Right-click on your Solution >> Add new project >> Visual C# >> WCF >> WCF Service Application. The project will contain the method that connects to the database and returns a list. OBS: Remember to change to .Net Framework 3.5 instead of .NET Framework 4.0.

Model

Step 2: Add a new project named View. Right-click on your Solution >> Add new project >> Visual C# >> Silverlight >> Silverlight Application. This project will contain all the .xaml files.

Uncheck the "Host the Silverlight application in a new or existing Web site in the solution".

Step 3: Add a new project named ViewModel. Right-click on your Solution >> Add new project >> Visual C# >> Silverlight >> Silverlight Class Library. A class will be auto-generated, and name it MainPage_ViewModel.cs as it will contain all code-behind for your MainPage.xaml.

So, the structure of the application has been created, and next we need to link the View and ViewModel together, by creating a reference:

Step 4: Select your View and right-click on References: Click Add reference >> Projects >> Chose ViewModel from the list.

Step 5: Go to MainPage.xaml.cs, and add using ViewModel; at the top. Also, add the code snippet seen in line 8, that will create a new instance of the MainPage_ViewModel() class.


using ViewModel; 

namespace View
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            this.DataContext = new MainPage_ViewModel();
        }
    }
}

And that's it! (Well, almost: to actually use this structure and gather data from a database thorugh a WCF and bind the Model and the ModelView together, you'll need to follow the steps of my next post).

13May/100

Passing Data and Uri Mapping

It's very simple to pass data from one Silverlight page to another:

Say you have an AutoCompleteBox and a Button on PageA.xaml, and when you press the button, you want to 1) reach PageB.xaml, and 2) print the content of the AutoCompleteBox in to a TextBlock in PageB.xaml.

Step 1: Insert the AutoCompleteBox and a Button. Notice the x:Name, and Click:


<input:AutoCompleteBox x:Name="Supplier"
                            IsTextCompletionEnabled ="True"
                            />

<Button Content="Search"
            Click="Button_Click"/>

Step 2: Go to PageA.xaml.cs and create the navigate method:

void Button_Click(object sender, RoutedEventArgs e)
        {

            if (Supplier.Text != "") //if user has inserted anything in the AutoCompleteBox
            {

                string supplier = Supplier.Text.ToString(); //Supplier = name of AutoCompleteBox
                NavigationService.Navigate(new Uri(string.Format("/PageB.xaml?Supplier={0}", supplier), UriKind.Relative));
            }
            else
            {
            MessageBox.Show("Enter something");
            }
        }

Step 3: Create PageB.xaml and make a new TextBlock that will contain the content retrived from the AutoCompleteBox on PageA:

<TextBlock x:Name="Supplier_name"/>
 

Step 4: Go to PageB.xaml.cs and create the method that will accept the value from PageA and insert it into the TextBlock.


protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            string supplier = NavigationContext.QueryString["Supplier"].ToString();
            Supplier_name.Text = supplier;
        }
 

This will work perfectly, but you will notice that your URL doesn't look very pretty when you reach PageB. So, we will use Uri Mapping to define what we want the URL to look like.

Uri Mapping

Step 1: Go to your App.xaml and insert xml namespace for the navigation framework at the top:

xmlns:nav="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation">
 

Step 2: Go to PageA.xaml and change your Button_Click method. Inside the string.Format() you add what you want the URL to be called, followed by the value you pass:


string supplier = Supplier.Text.ToString();
NavigationService.Navigate(new Uri(string.Format("PageB/{0}", supplier), UriKind.Relative));
 

Step 3: Add a new UriMapper to the Application Ressources. The Uri represents what the URL will look like, and the MappedUri where the button links to:

 <Application.Resources>
        <nav:UriMapper x:Key="uriMapper">
                <nav:UriMapping Uri="PageB/{c}" MappedUri="/PageB.xaml?Supplier={c}" />
        </nav:UriMapper>
 </Application.Resources>
 

Notice that the string.Format("PageB/{0}"... is identical to the Uri="PageB/{c}", except the value inside the brackets.

Step 4: Go to your Navigation Framework (probably located at your MainPage.xaml), and add the UriMapper property to the Navigation Frame (see line 5):

 <navigation:Frame x:Name="ContentFrame"
                              Grid.Row="1"
                              Grid.Column="1"
                              Source="/Customer_Home.xaml"
                              UriMapper="{StaticResource uriMapper}"
 />
 

After running this code you'll see, that the URL is called "...PageB/ followed by the value entered in the AutoCompleteBox.

11May/100

AutoCompleteBox

When inserting a search function within your application, it is useful to use the AutoCompleteBox if you already know what the user will search for. In the case of my application, the user can only search for a specific set of company names, and so the AutoCompleteBox is useful.

Like in the previous post, I will hardcode the set data into the code, but this will later be changed so it collects the names from a MySQL database.

First, you need to create a reference to System.Windows.Controls and to System.Windows.Controls.Input. You do this by right-clicking on your project >> Add reference >> Choose System.Windows.Controls, and System.Windows.Controls.Input. If you do not add the reference, you'll be presented with an error message saying "Verify that you are not missing an assembly reference".

Next, you need to add a an xmlns entry at the top of your .xaml page.


xmlns:input="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input"

Then you just insert your new AutoCompleteBox. Something like this (notice the IsTextCompletionEnabled):


  <input:AutoCompleteBox x:Name="Supplier"
                          Width="220"
                          Height="20"
                          Margin="10"
                          IsTextCompletionEnabled ="True"/>

The IsTextCompletionEnabled enables the automatic completion of the text as you type. Its default is set to False, so if you do not wish it to auto-complete, then do not add the property.

In the code-behind, you can bind a simple string array to the ItemsSource property:


 public Customer_Find_Supplier()
        {
            InitializeComponent();
            this.Supplier.ItemsSource = new string[]
            {
            "Company One", "Company Two", "Company three"
            };
        }

And that's it!

Filed under: C#, Silverlight, XAML No Comments
10May/100

Linking from User Control to User Control

As I showed in a recent post, it is possible to link from a Silverlight User Control to a Silverlight page using the Silverlight Navigation Framework. But at times you will need to link from a User Control to another User Control, and there you won't be able to use it.

In my case, I encoutered this situation because my application has three different user types, who each have their own Navigation Framework. They have this because they each have vastly different tasks to perform, and thus there's need of different menus to access different pages. But there is one task that they must all carry out: Before any user can access the application, they must log in. Of course, the login page is a User Control, so I needed to link the Login page to one of the Navigation Framework user pages, which are also User Controls.

Basically, you will need too add a small code snippet outside the Grid on the page you wish to link from (in my case, the login-page), and an event EventHandler to a button. In the EventHandler you specify which grids are visible, and which are not.

Below, I have posted a very simplified version of my MainPage.xaml, that I use for the login. Notice that there are two grids at the top, and that we have named the inner grid Login, and its Visibility is set to Visible:


<UserControl x:Class="QM_v1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d"
xmlns:local="clr-namespace:QM_v1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="437" d:DesignWidth="947">
<!--Background layout-->
<Grid x:Name="LayoutRoot">

   <Grid x:Name="Login" Background="White" Width="Auto" Height="auto" ShowGridLines="False" Visibility="Visible">

  <!-- Focus on the 'Enter' button -->

           <TextBox x:Name="Mail">
           <PasswordBox x:Name="Pass" >

            <StackPanel Grid.Row="2" Grid.Column="2" Orientation="Horizontal">
                <Button Content="Cancel" Click="Cancel_button"/>
                <Button Content="Enter" Click="Enter_button"/>

            </StackPanel>

   </Grid>

<!-- Important bit: -->
<local:Customer x:Name="Customer" Visibility="Collapsed"/>
<local:Supplier x:Name="Supplier" Visibility="Collapsed"/>
<local:ETS x:Name="ETS" Visibility="Collapsed"/>
</Grid>
</UserControl>

Notice that the local:Customer bit is outside the second grid. In here you specify the name of the .xaml pages that you wish to link to, depending on which user logs in. Here I have just specified that I have three .xaml pages (Customer, supplier and ETS), and that each of these pages at this point in time are collapsed, ie. not visible/active.

When the user presses the Enter_Button you want your EventHandler to check who the user is, and depending on the outcome they should be directed to either customer.xaml, supplier.xaml or ETS.xaml. For simplicity, I have hardcoded the user name and password, but they should of course be gathered from a database.


private void Enter_button(object sender, RoutedEventArgs e)
{

   if (Mail.Text != "" && Pass.Password != "")//there is some content.
   {

     if (Mail.Text == "customer@gmail.com" && Pass.Password == "customer")
     {
        Customer.Visibility = System.Windows.Visibility.Visible; //customer = Name of .xaml page
         Login.Visibility = System.Windows.Visibility.Collapsed; //Login = inner grid at MainPage.xaml
     }
     else if (Mail.Text == "supplier@gmail.com" && Pass.Password == "supplier")
     {
      Supplier.Visibility = System.Windows.Visibility.Visible;
      Login.Visibility = System.Windows.Visibility.Collapsed;
     }
     else if (Mail.Text == "ets@gmail.com" && Pass.Password == "ets")
     {
     ETS.Visibility = System.Windows.Visibility.Visible;
     Login.Visibility = System.Windows.Visibility.Collapsed;
     }
     else
     {
     MessageBox.Show("Please enter correct e-mail and password.");
     }
  }
  else
  {
  MessageBox.Show("Please type your e-mail and password.", "Error", MessageBoxButton.OK);
  }
}

Notice that the two things happen: 1) The users' own page is set to visible (Customer.Visibility = System.Windows.Visibility.Visible;) and the current page, the MainPages' Login-grid, is closed/collapsed (Login.Visibility = System.Windows.Visibility.Collapsed;).

Voilà!

Filed under: C#, Silverlight, XAML No Comments
8May/100

Adding Images

Here is just a quick guide to inserting images into your Silverlight application, and how to make them link to other pages. In this example I will just hardcode the name of the image, but I will soon write another post about how to insert images dynamically.

First, you need to create an images folder. If the name of your application is MyApp, go to the ClientBin folder located at MyApp >> MyApp.Web >> ClientBin, and create the images folder.

The following simple code snippet shows how you insert an image located in your images folder.


<Image Source="../images/picture.jpg" Height="100" />

If you want your image to link to another Silverlight page, you simply create the image as an HypelinkButton:


<HyperlinkButton HorizontalAlignment="Center" NavigateUri="/mypage.xaml">
        <HyperlinkButton.Content>
              <Image Source="../images/picture.jpg" Height="100" />
        </HyperlinkButton.Content>
</HyperlinkButton>

Oh, and remember that Silverlight only supports .png and .jpg images.

Filed under: Silverlight, XAML No Comments
5May/101

Views – Navigation Framework

When developing your Silverlight application you will almost always need to create several pages and subpages. Thus, you will need a navigation framework. Silverlight allows you to browse between different pages while enabling forward and backward navigation through the history using the browser's back and forward buttons.

This post provides a complete walk trough on how to implement a menu in your Silverlight application, and how to create and access subpages. You will end up with something like the application showed below (but without the formatting):

Navigation Framework - Silverlight - Menu

The working application is temporarily hosted here.

Step 1: Add support for the Navigation Framework by adding a reference to System.Windows.Controls.Navigation.dll by right clicking on the References folder in your Silverlight Project, and choosing Add reference.

Reference

Step 2: Go to the .Net tab, and select the System.Windows.Controls.Navigation, as seen below:

Add Reference

Notice that it says "Filtered to: Silverlight 4" at the top. If you happen to have converted your Silverlight application from Silverlight 3, then it will say "Filtered to: Silverlight 3", and you will need to update this by right-clicking on you project >> Properties >> and set Target Silverlight Application to "Silverlight 4".

Step 3: You will need to add System.Windows.Controls.Navigations to your UserControl definition. Insert the following code at the top of your MainPage.xaml:


    xmlns:navigation ="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

Step 4: Create a StackPanel within your Grid in your MainPage.xaml, and insert HyperlinkButtons, that will be used as the menu. Something like this:

<!--Menu -->
            <StackPanel Orientation="Horizontal"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Bottom"
                            Grid.Column="1"
                            Grid.Row="0">

                <HyperlinkButton Content="Home"
                                 Click="LinkClick"
                                 Tag="/Home.xaml"
                                 Padding="5"/>
                <HyperlinkButton Content="Find supplier"
                                 Click="LinkClick"
                                 Tag="/Find_Supplier.xaml"
                                 Padding="5"/>
                <HyperlinkButton Content="Questionnaire data"
                                 Click="LinkClick"
                                 Tag="/Options.xaml"
                                 Padding="5"/>
            </StackPanel>
<!--Navigation Frame -->
            <navigation:Frame x:Name="ContentFrame"
                              Margin="10"
                              Grid.Row="1"
                              Grid.Column="1"
                              BorderBrush="Black"
                              BorderThickness="2"
                              Source="/Home.xaml"
                              />

Note:
Lines 2-6 define how the menu will look like, so you can of course change this as you please.
Line 10, 14, and 18: You will create each of these pages in the next step. Change the Tag so it matches the name of your subpages.
Line 9, 13, 17: The LinkClick-method is created later.
Line 28: The Source states the opening page, ie. the frontpage of your application.

The above code basically just creates the menu that the user will click on to navigate through the application. The XAML page you insert this into will not ever be shown: It is merely used to define the framework and the links to subpages. What you need to do next is actually create all the subpages, that will hold all content.

Step 5: Add a new page by right-clicking on your Project >> Add >> New Item >> New Page. It looks like this:

Add page

Remember to name the pages like you stated in the HypperlinkButton above.

Step 6: Open your new subpages and add simple Textblocks, preferably including the name of the page, so you can identify it when you run the program.

Step 7: The last step is to add a LinkClick method in your MainPage.xaml.cs.


 private void LinkClick(object sender, RoutedEventArgs e)
        {
            HyperlinkButton button = (HyperlinkButton)sender;
            string viewSource = button.Tag.ToString();
            ContentFrame.Navigate(new Uri(viewSource, UriKind.Relative));

        }

And that's it!

Inner Views

If you need to create a new page that is not one of the 'main' pages shown in the menu, you need to create an InnerView. You just do like shown above (you create a button with a Click property, but without the Tag property, and you create a new page). The main difference lies in step 7. You have to insert the code below instead:


 private void LinkClick(object sender, RoutedEventArgs e)
        {
           NavigationService.Navigate(new Uri ("/InnerView.xaml", Uri.Kind.Relative));

        }

Enjoy!

22Apr/100

External Links

Here is just a quick and small code snippet for linking to external pages from your Silverlight application.

I found quite a few potential solutions to the problem, but the first 2 or 3 just didn't compile. Most people recommended the Hyperlink with some sort of System.Diagnostics.Process.Start("http://www.link.com");attached, but I simply couldn't make it work. But finally I found a solution:

Insert a TextBlock like the one below. Notice the MouseLeftButtonDown.


            <TextBlock Grid.Column="2" Grid.Row="0"
                       VerticalAlignment="Top"
                       Text="www.aook.dk/blog"
                       TextDecorations="Underline"
                       MouseLeftButtonDown="TextBlock_MouseEnter"
                       HorizontalAlignment="Right"
             />

Next, you just need to create an EventHandler like the one below, but make sure to first add an using System.Windows.Browser; at the top.

        private void TextBlock_MouseEnter(object sender, MouseButtonEventArgs e)
        {
            HtmlPage.Window.Navigate(new Uri("http://www.aook.dk/blog"));

        }

Enjoy!

Filed under: C#, Silverlight, XAML No Comments
19Apr/100

Input validation

I needed to create a login feature, so only registered users could gain access to the Silverlight application, and I wanted to create a data validator, that would check the users' input. In the this example I just check if the user has entered an e-mail address.

Red lineError messageNo error
In the first image to the left, I have just entered my name in the E-mail address field, and entered my password. The textbox is marked with red, because there has not been registered an "@" in the field (of course, this is a very simple example. One could easily find more thorough ways of validating an e-mail address, which I will probably make a blog post about some time soon).

In the second image, the user has selected the textbox, and immediately an error message appears, explaining the user why there is a problem.

In the third image, the user has inserted a valid e-mail address and can proceed with the login feature.

So, the way to implement this feature is pretty straight-forward, especially since there is a brilliant video tutorial showing exactly how to do it. I just followed the steps on the tutorial, but I will nonetheless quickly go through the code here.

The first thing you need to know is that - as brilliant as the tutorial mentioned above may be - the code will not compile if you have the default settings on Visual Studio 2008. The tutorial does not explain that you need to make one quick settings change in order to avoid an "Arguement Exception was unhandled by User"-error message, that prevents the code from compiling.

You need to the following: Go to Visual Studio Options and then Debugging/General, and just uncheck the 'Enable Just my Code'. And that's it.

The tutorial also fails to mention a small piece of code necessary to compile, but Visual Studio helps by telling you where you need to insert the following code:

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

We'll come back to this.

How to implement the input validation:

Open your MainPage.xaml, and find the textbox whose input you want to validate. Then insert Binding statements into TextBox.Text like shown below. Notice the Path called Mail. We will use this name later.


  <TextBox width = "390" >
           <TextBox.Text>
                <Binding Mode="TwoWay"
                        Path="Mail"
                        ValidatesOnExceptions="True"
                        NotifyOnValidationError="True"
                />
          </TextBox.Text>
 </TextBox>

So, we now have a textbox that needs to be binded to a data object, so we can check when there are any changes made to it. We do this by creating a new class: Right click on your application name in the Solutions Explorer, choose Add and Class. I named my class validation.cs.

The first thing to do is to insert using System.ComponentModel; at the top, and you can delete all others system statements except the using System;. This supports the INotifyPropertyChanged, that ensures two-way binding, that is, checks if any changes are made to the object, and if there is, then the UI is updated accordingly.

Insert the following code into your validation.cs:

using System;
using System.ComponentModel;

namespace QM_v1
{
    public class validation : INotifyPropertyChanged
    {
        private string mail;

        public string Mail
        {
            get { return mail; }
            set {
                if (value.IndexOf('@') == -1)
                {
                    throw new ArgumentException("This is not a valid email address");
                }
                NotifyPropertyChanged("Mail"); // The code crashes here, if you don't change your settings, as described above.

                mail = value; }
        }

        public event PropertyChangedEventHandler propertyChanged; //mandatory event for this class. Checks the object for changes.

        private void NotifyPropertyChanged(string propertyName)
        {
                if (propertyChanged != null)
                {
                    propertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}

Notice lines 33-37: This code snippet is needed for the program to compile, but is not mentioned in the tutorial mentioned above.
Lastly, open your MainPage.xaml.cs and enter the following code in your class:

  public MainPage()
        {
            InitializeComponent();

            var m = new validation();
            LayoutRoot.DataContext = m;  //LayoutRoot = name of your Grid.

        }

And that's it!

See the working example here.

Filed under: C#, Silverlight, XAML No Comments
12Apr/100

How To Get Your Silverlight Application Online

Microsoft has made it very easy to get your Silverlight Application online.

You need to retrieve two files from your application project. If your project is called "MyApp" and you're running Visual Studio 2008, the folder you are looking for is located on the following path:

Documents -> Visual Studio 2008 -> Projects -> MyApp -> MyApp -> Bin -> Debug.

Here you will find a file called 'TestPage.html and a file called 'MyApp.zap'. Open an FTP program and access your website's server, and copy-paste the two files into the root folder. Rename your 'TestPage.html' to 'index.html' (no capitals).

Enjoy!

Filed under: Silverlight No Comments