My Master's Thesis Problems and solutions encountered…

22Jul/1016

Open Child Window in a MVVM’ish design pattern

This post explains how to open a ChildWindow, pass data to the ChildWindow, and how to pass data back from the ChildWindow to the MainPage, using a MVVM Design pattern in a Silverlight 4 application.

The method I am using is not strickly MVVM, because I actually create my ChildWindows in the ViewModel, which is usually a no-go. But considering the endless Googling after alternatives only to find guides that use all kinds of toolkits in order for a ChildWindow to appear (see fx., here, here and (the one that takes the price in length and complexity) here), I consider this workaround the best way to do it. I hope and expect that an easier and strickly MVVM solution will be possible with Silverlight 5.

This post is just meant as a proof of concept, and is based on a very simple example: The MainPage.xaml contains two textboxes, Name and Address, respectively, and the ChildWindow, the same. When the user enters their Name in the MainPage and presses the button, the ChildWindow appears, with the Name value entered (see picture below). The user can then enter an address in the ChildWindow and press the button, and return to the MainPage, where the value has been inserted in to the Address textbox of the MainPage.

ChildWindow, MVVM

So. The solution is simple. Just create the ChildWindow in the View Model project, so you can parse data through the ChildWindow and MainPage. Then create properties for each of the 4 textboxes (2 for each), and create the commands for the buttons, that will open/close the Child Window, and connect the properties. Notice that the ChildWindow does not have a ViewModel, and everything thus happens in the ViewModel of the MainPage:

Step 1: Create a new ChildWindow in your ViewModel. Right-click on project >> Add >> Silverlight ChildWindow. I have named mine MyChildWindow.xaml.

Step 2: Create a ViewModel for your MainPage. I have named mine MainPage_ViewModel.cs.

Step 3: Connect the MainPage.xaml.cs and MyChildWindow.xaml.cs to the MainPage_ViewModel.cs:

MainPage.xaml.cs:


namespace View
{
    public partial class MainPage : UserControl
    {

        public MainPage()
        {
            InitializeComponent();
            this.DataContext = new MainPage_ViewModel();
         }
     }
}

MyChildWindow.xaml.cs:


namespace ViewModel
{
    public partial class MyChildWindow : ChildWindow
    {
        public MyChildWindow(MainPage_ViewModel ma)
        {
            InitializeComponent();
            this.DataContext = ma;
        }

    }
}

Step 4: Create properties for the textboxes of the MainPage and ViwModel (both have two textboxes), in the MainPage_ViewModel.cs:

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

namespace ViewModel
{
    public class MainPage_ViewModel : INotifyPropertyChanged
    {
//Properties of Mainpage
private string myNameVM = "";
        public string MyNameVM
        {

            get { return myNameVM; }
            set {
                myNameVM = value;
                RaisePropertyChanged("myNameVM");
            }
        }

        private string myAddressVM = "";
        public string MyAddressVM
        {

            get { return myAddressVM; }
            set
            {
                myAddressVM = value;
                RaisePropertyChanged("MyAddressVM");
            }
        }

//Properties of ChildWindow
        private string myNameCW = "";
        public string MyNameCW
        {

            get { return myNameCW; }
            set
            {
                myNameCW = value;
                RaisePropertyChanged("MyNameCW");
            }
        }

        private string myAddressCW = "";
        public string MyAddressCW
        {

            get { return myAddressCW; }
            set
            {
                myAddressCW = value;
                RaisePropertyChanged("MyAddressCW");
            }
        }

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

 }

(Step 5): If you have not created one eralier, you will need to create a DelegateCommand class in your ViewModel, that will handle the button commands needed for the next step. So. Create a new class, name it DelegateCommand.cs, and insert the following:


using System;
using System.Windows.Input;

namespace ViewModel
{
    public class DelegateCommand : ICommand //
    {
        private Predicate<object> _canExecute;
        private Action<object> _method;
        public event EventHandler CanExecuteChanged;

        public DelegateCommand(Action<object> method)
            : this(method, null)
        {
        }

        public DelegateCommand(Action<object> method, Predicate<object> canExecute)
        {
            _method = method;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }

            return _canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            _method.Invoke(parameter);
        }

        protected virtual void OnCanExecuteChanged(EventArgs e)
        {
            var canExecuteChanged = CanExecuteChanged;

            if (canExecuteChanged != null)
                canExecuteChanged(this, e);
        }

        public void RaiseCanExecuteChanged()
        {
            OnCanExecuteChanged(EventArgs.Empty);
        }
    }
}

Step 6: Create the commands in the MainPage_ViewModel.cs, that you will bind to the buttons in the MainPage.xaml and MyChildWindow, respectively.

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

namespace ViewModel
{
    public class MainPage_ViewModel : INotifyPropertyChanged
    {
//Properties of Mainpage
private string myNameVM = "";
        public string MyNameVM
        {
            get { return myNameVM; }
            set {myNameVM = value;
                RaisePropertyChanged("myNameVM");
                 }
            }

        private string myAddressVM = "";
        public string MyAddressVM
        {

            get { return myAddressVM; }
            set
            { myAddressVM = value;
                RaisePropertyChanged("MyAddressVM");
            }
        }

//Properties of ChildWindow
        private string myNameCW = "";
        public string MyNameCW
        {

            get { return myNameCW; }
            set
            {  myNameCW = value;
                RaisePropertyChanged("MyNameCW");
            }
        }

        private string myAddressCW = "";
        public string MyAddressCW
        {

            get { return myAddressCW; }
            set
            {  myAddressCW = value;
                RaisePropertyChanged("MyAddressCW");
            }
        }

         //When the button is pressed in MainPage, executes method ExecuteOpenChildWindow
        private DelegateCommand _openChildWindow;
        public DelegateCommand OpenChildWindow
        {
            get
            {
                if (_openChildWindow == null)
                    _openChildWindow = new DelegateCommand(executeOpenChildWindow);

                return _openChildWindow;
            }
        }

        // New instance of ChildWindow. Sets the NameProperty of the ChildWindow equal to the Name entered in the MainPage.
        MyChildWindow cw;
        private void executeOpenChildWindow(object parameter)
        {
            cw = new MyChildWindow(this);
            MyNameCW = MyNameVM;
            cw.Show();
        }

      //When OK-button is pressed in ChildWindow
        private DelegateCommand _okChildWindow;
        public DelegateCommand OkChildWindow
        {
            get {
                if (_okChildWindow == null)
                    _okChildWindow = new DelegateCommand(OkSaveChildWindow);

                return _okChildWindow;
            }
        }
        //MainPage Address property is set to the value entered in the address textbox in Child Window. Child Window is closed.
        private void OkSaveChildWindow(object parameter)
        {
            MyAddressVM = MyAddressCW;
            cw.Close();
        }

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

Step 7: Add the textboxes in the MainPage.xaml and MyChildWindow.xaml, and bind them to the properties created in MainPage_ViewModel. Also Create the buttons, that will bind to the commands.

MainPage.xaml:

<StackPanel>

        <Grid Margin="0 10 0 5" Width="350">

    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>

    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="150"/>
        <ColumnDefinition Width="*"/>

    </Grid.ColumnDefinitions>

    <!--TextBlocks-->
    <TextBlock Text="Name:" TextWrapping="Wrap" Margin="5,5,0,5" Grid.Row="0"  />
    <TextBlock Text="Address:" Grid.Row="1" Grid.Column="0"  />

   <!--TextBox, where the users enters data. Binds to the properties of MainPage_ViewModel-->
  <TextBox Text="{Binding MyNameVM, Mode=TwoWay}" Grid.Row="0" Grid.Column="1"/>
  <TextBox Text="{Binding MyAddressVM, Mode=TwoWay}"  Grid.Row="1" Grid.Column="1"/>

    <Button Content="Open Child Window"
            VerticalAlignment="Center"
            HorizontalAlignment="left"
            Width="auto"
            Margin="5"
            Grid.Row="2"
            Command="{Binding OpenChildWindow}" <!--Binds to CommandDelegate from the ViewModel -->
        />
        </Grid>
    </StackPanel>

MyChildWindow.xaml:


    <Grid Margin="0 30 0 5" Width="350">

        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="205*"/>

        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition Width="*"/>

        </Grid.ColumnDefinitions>

        <TextBlock Text="Name: " Grid.Row="0" />
        <TextBlock Text="Address:" Grid.Row="1" Grid.Column="0"  />

        <!-- TextBoxes are bind to the properties from the ViewModel.  -->
        <TextBox x:Name="InputName" Text="{Binding MyNameCW, Mode=TwoWay}" Grid.Row="0" Grid.Column="1" Height="20"/>
        <TextBox x:Name="OutputAddress" Text="{Binding MyAddressCW, Mode=TwoWay}" Grid.Row="1" Grid.Column="1" Height="20"/>

       <!-- Button comand bind to CommandDelegate from ViewModel -->
      <Button x:Name="OKButton" Command="{Binding OkChildWindow}" Content="OK" Width="75" Height="23" Margin="0,12,79,0" Grid.Row="2" Grid.Column="1"/>
    </Grid>

And that's it! :)

Filed under: C#, MVVM, Silverlight 4 16 Comments
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
11Jul/100

How to add background-color to cells in a DataGrid depending on content

When working on my Silverlight application I found need of giving cells in a DataGrid a specific color, so the user would get a better overview of the shown data. In my application, I have a questionnaire with a list of questions regarding the working and environmental conditions of a company. Now, the answer to each question is beforehand given a color: Red, yellow or green, depending on the severity of answering yes/no to the question.

I wanted to create a Datagrid that gave the creator of the questionnaire an overview over the answers he had created, and the color he given them. I ended up with this:

Step 1: In you .xaml page, create a DataGrid like the one shown below. Notice the Fill="{Binding Converter={StaticResource QuestionsResultConverterYES}}" in the Rectangle.

(Below is only shown the code for the two first columns).

...

xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
...
 <data:DataGrid x:Name="AllQuestionsGrid" MinHeight="10" IsReadOnly="True" Width="350"  AutoGenerateColumns="False" ItemsSource="{Binding QuestionList}" MaxHeight="320" VerticalScrollBarVisibility="Visible" Margin="5 10 5 0" >

    <data:DataGrid.Columns>
        <!--Question Column. -->
        <data:DataGridTemplateColumn Header="Questions" Width="190">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock TextWrapping="Wrap" Text="{Binding Question}" /> <!-- OT:  This is how you wrap text inside a Datagrid. Simply make TemplateColumn and insert a TextBlock.  -->
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>

        <!--Yes-column-->
        <data:DataGridTemplateColumn Header="Yes" Width="auto">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <Rectangle Margin="4" x:Name="MyRectangle" Fill="{Binding Converter={StaticResource QuestionsResultConverterYES}}" /><!--We will create the Static Resource QuestionsResultConvertYES in the next step -->
                       </Grid>
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>

<!-- Repeat for the other no-column-->

    </data:DataGrid.Columns>
</data:DataGrid>

Step 2: Create a new class in your View project. I added a new folder, called Converter, where I put all the Converter classes. In this class we will create a new instance of the object that we want to insert into the datagrid, in my case called QuestionGrid. One of the attributes is called AnswerYes and contains a string with the values "Red", "Yellow" or "Green". So we insert a if-elseif-else that will return a specific SolidColorBrush value depending on the content of the string:


using System.Windows.Data;
using ViewModel.QMServiceReference;

namespace View.Converters
public class QuestionsResultConverterYES : IValueConverter //Important!
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            QuestionGrid ColorYes = value as QuestionGrid;

            if (ColorYes == null)
            {
                return value;
            }
            else if (ColorYes.AnswerYes == "Red")
            {
                return new SolidColorBrush(Color.FromArgb(255, 204, 51, 51)); //red
            }
            else if (ColorYes.AnswerYes == "Yellow")
            {
                return new SolidColorBrush(Color.FromArgb(255, 255, 255, 102)); //yellow
            }
            else if (ColorYes.AnswerYes == "Green")
            {
                return new SolidColorBrush(Color.FromArgb(255, 51, 102, 0));//green
            }
            else
            {
                return new SolidColorBrush(Color.FromArgb(255, 220, 220, 220)); //grey
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

Notice that we return a color of the type ARGB. It's basically just normal RGB colors (the last three values), and the first just sets the opacity (255 = 100%, 0 = 0%, that is, transparent).

Step 3: In the App.xaml (where you probably have all your your style deifnitions), you will make the QuestionsResultConverterYES available to your .xaml page.

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:layout="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"
              xmlns:nav="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"
             xmlns:converters="clr-namespace:View.Converters" //add
             x:Class="View.App"
             >
        <Application.Resources>
                  <converters:QuestionsResultConverterYES x:Key="QuestionsResultConverterYES"/>
        </Application.Resources>
</Application>

And that's it!

Filed under: Misc. 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" />
4Jul/102

Retrieving data from a MS SQL DB

This post is basically just a recap of the former, and aims to quickly show how to retrieve data form the MS SQL database through WCF Services all the way through my MVVM design pattern and to my Silverlight application. The steps in this posts will be repeated many times in my application, as I will go through them each time I have to retrieve a piece of data from the database.

This posts presupposes that the user has followed the steps in my recent post, explaining how to setup the MVVM design pattern: MVVM Structure. Also, it's preferable that the user has followed the steps in my last post, as it explains how to overcome some of the small problems that I encountered when deploying the WCF Service: This blog post just recaps the whole thing (but ads some smaller structure changes, in steps 1-3), and will not be as thorough as my last blog post.

I have made two changes:

First, I have decided to add two new folders in my Model project, called Model and Data_access, respectively. The Model folder contains all classes whose attributes we want to retrieve to the Silverlight application. The Data_access folder contains all classes that actually calls the database, runs the query, and returns the desired values. This has been done so as to ease the overview, as I otherwise would end up with way to much code in IService1.svc.cs

Also, I have made a DBconnection.cs class in the Data_access folder that all other classes in the folder inherit.

So, from the start:

Step 1: Create two folders in the Model project, called Model and Data_access, respectively.

Step 2: Add a DBconnection class in your Model folder containing the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.SqlClient;

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

            return dbConn;
        }

    }
}

Step 3: Create a new class in your Model folder named after the place in your application, where you want to insert the data you retrieve from the database. I have called mine Client_Home.cs. In this class you will merely define the attributes of the object, and create their properties. Notice the DataContract and DataMember.

*
using System.Runtime.Serialization;

namespace Model.Model
{
    [DataContract]
    public class Client_Home
    {

        string active_parentQuestionnaire_;

        [DataMember]
        public string Active_parentQustionnaire
        {
            get { return active_parentQuestionnaire_; }
            set { active_parentQuestionnaire_ = value; }
        }

    }
}

Step 4: Go to your Data_access folder, and create the class that will call the database and populate the attributes defined in the Client_Home class created in the last step. Depending on what you use your data for, it could be usefull to name the class after the tables you mainly retrieve the data from... but I have decided (for now) just to name it after the class it is related to from the Model folder. Hence, I have called mine Client_HomeDA.cs.

Step 5: Now, in the class that you have just created: For each attribute that you have declared in your Client_Home.cs, you will create a method that retrieves that data from the database. My first (and for now, only) attribute in my Client_Home is named ActiveQuestionnaire, as I just want to retrieve the name of the active questionnaire of the current user. So, in my Client_HomeDA.cs I will create a method that has a userID as a parameter:

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

namespace Model.Data_access
{
    public class Client_Home_DA : DBConnection
    {

  public string GetActiveQuestionnare(int UID)  //In this case we just want to retrieve a string, and not a list of objects, like in the previous example.
        {

            string client =""; //Empty string
            SqlConnection dbConn = connectToDB();
            string _query = string.Format("SELECT parent_questionnaire.title FROM parent_questionnaire, accessible WHERE accessible.active = 1 AND accessible.pq_ID = parent_questionnaire.pq_ID AND u_ID = " + UID);

            try
            {
                dbConn.Open();
                SqlCommand cmd = new SqlCommand(_query, dbConn);
                SqlDataReader rdr = cmd.ExecuteReader();
                while (rdr.Read())
                {

                    client = (string)rdr[0];
                }

            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            return client;
        }
  }

}

Notice that the class inherits the DBConnection class.

Step 6: Go to the IService1.cs, where you will make the GetActiveQuestionnaire available. This class is basically the link between the server and the client, and whatever methods you define in this class have to be also in the one of the classes you have created in the Data_access folder.


*
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using Model.Model;

namespace Model
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string GetActiveQuestionnare(int UID);
    }
}

Step 7: Go to your Service1.svc.cs, where you will create a new instance of the Client_Home, and call the Client_Home_DA, that will populate the attributes with data from the database, and return it. Notice that we have added the Using Model.Model and Model.Data_access.

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

namespace Model
{

    public class Service1 : IService1
    {
          public string GetActiveQuestionnare(int UID)
        {
            string cli;
            Client_Home_DA cliDA = new Client_Home_DA();
            cli = cliDA.GetActiveQuestionnare(UID);
            return cli; 

        }
    }
}

Step 8: Publish by right-clicking on you Model project >> Publish.

Step 9: Right-click on your Service Reference that you created in step 11 in my last post, and click 'Update Service Reference'.

Step 10: The next step is to create a class in your ViewModel, that will be connected to the .xaml page, where you want to insert the data. I want to insert the data in a page called Client_Home, so I create a class called Client_Home_ViewModel.cs, that will contain the necessary properties and eventhandlers. Remember that you have to have published and updated your Service References before you can execute the code in the constructor.

Also notice that you have to inherit the INotifyPropertyChanged .

*
using ViewModel.QMServiceReference;
using System.ComponentModel; 

namespace ViewModel
{
    public class Client_Home_ViewModel : INotifyPropertyChanged //Important!!!!
    {
        private string activeQuestionnaire_;
        public string ActiveQuestionnaire
        {//Property
            get { return activeQuestionnaire_;  }
            set { activeQuestionnaire_ = value;
            RaisePropertyChanged("ActiveQuestionnaire");
            }
        }
         public Client_Home_ViewModel()
        {//Constructor, calls WebService
            int UID = 2; //Temporary hardcoding.
            QMServiceReference.Service1Client WebService = new Service1Client();
            WebService.GetActiveQuestionnareCompleted += new EventHandler<GetActiveQuestionnareCompletedEventArgs>(WebService_GetActiveQuestionnareCompleted);
            WebService.GetActiveQuestionnareAsync(UID); 

        }

         void WebService_GetActiveQuestionnareCompleted(object sender, GetActiveQuestionnareCompletedEventArgs e)
         {
             ActiveQuestionnaire = e.Result; //Return value, sets property

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

    }
}

Step 11: The next step is of course to define the textblock in your .xaml code (mine is Client_Home.xaml), where you want to display the result of your query. Notice that we bind to ActiveQuestionnaire, which is the property that we defined in the code in the previous step:

 <TextBlock Grid.Row="1" Grid.ColumnSpan="4" Grid.Column="1" Foreground="#FF696969" Text="{Binding ActiveQuestionnaire}"/>

Step 12: The very last thing to do is to connect your Client_Home.xaml to the Client_Home_ViewModel, which is done by opening your Client_Home.xaml.cs and instantiating the Client_Home_ViewModel():

*
using ViewModel;

namespace View
{
    public partial class Client_Home : Page
    {
        public Client_Home()
        {
            InitializeComponent();
            this.DataContext = new Client_Home_ViewModel();
        }

       protected void Page_Loaded(object sender, RoutedEventArgs e)
        {
         }

    }
}

And so, these are the steps that must be repeated again and again and again, everytime data is retrieved from the database...

Enjoy!

Ps. In this post I have hardcoded the UserID that I needed, but I will soon blog about how to parse the data from one page to another, and how you can save data locally... but for now, I will just work on how figuring out what data I need, how the application will look like, which queries are necessary, and how to insert all this into my project.

Filed under: C#, Silverlight 4 2 Comments