My Master's Thesis Problems and solutions encountered…

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" />
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).

13Jun/100

Changes…

So, a while ago I figured out that I needed to make some pretty drastic changes to my project before being able to code the rest of the functionality needed for my application. The problem originated from beginners mentality I suppose, because the very structure of my project was the main problem.

Also, for various reasons I have chosen to use a MS SQL database instead of the MySQL, so I will not be using the ODBC connection that I described in an earlier post.

So, I had to make two changes: I had to structure my project according to the MVVM Design Pattern (Model-View-ViewModel), and I had to set up and connect to a MS SQL database.

Deciding to change the structure of my project was necessary, but pretty frustrating, as I basically have to build up a whole new project and start again from scratch. Of course, most of the functionality from my old project can be reused, and I have fortunately documented and blogged about most of it, so the rebuilding will not take as much time as it did the first time I went through it all.

Wish me luck!

Filed under: Misc. No Comments