My Master's Thesis Problems and solutions encountered…

21Aug/103

Create a Pie Chart with Silverlight toolbox – MVVM

This post quickly shows how to create a pie chart in your MVVM Silverlight application, like the one shown below:

Pie chart MVVM Silverlight

Step 1: Add the System.Windows.Controls.DataVisualization.Toolkit into your project, and insert this at the top of your MyPage.xaml in the View:

   xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"    

Step 2: Insert the pie chart in MyPage.xaml. Notice the ItemSource, which we will bind to in the ViewModel.

 <charting:Chart HorizontalAlignment="Stretch"
                BorderBrush="Transparent"
                VerticalAlignment="Top" Height="170"
                Name="pieChart" Margin="0 -10 0 -10">

                <charting:Chart.Series >
                    <charting:PieSeries ItemsSource="{Binding ChartItems}"
                          IndependentValueBinding="{Binding Key}"
                          DependentValueBinding="{Binding Value}" >
                     </charting:PieSeries>
                </charting:Chart.Series>
</charting:Chart>

Step 3: In the ViewModel, create the following property, that will contain the array of the chart:


  private KeyValuePair<string, int>[] _chartItems;
         public KeyValuePair<string, int>[] ChartItems
         {
             get
             {
                 return _chartItems;
             }

             set
             {
                 _chartItems = value;
             }
         }

Step 4: In your ViewModels constructor, create the ChartItems and populate them.


    ChartItems = new KeyValuePair<string, int>[4];

             ChartItems[0] = new KeyValuePair<string, int>("Lorem ipsum dolor sit amet", 20);
             ChartItems[1] = new KeyValuePair<string, int>("Maecenas iaculis dapibus", 90);
             ChartItems[2] = new KeyValuePair<string, int>("Nulla facilisi. Curabitur laoreet", 112);
             ChartItems[3] = new KeyValuePair<string, int>("Pellentesque non turpis elit", 160); 

             //RaisePropertyChanged("ChartItems"); //Only necessary of you create the ChartItems outside the constructor

And that's it!

Of course, you will want to populate the chart with integers retrieved from a database, and in that case, just create properties for each of the items.

Filed under: C#, MVVM, Silverlight 4 3 Comments
7Aug/106

CRUD operations in Silverlight Datagrid – MVVM

This post will explain how you can add, edit or delete data from the database, by having it inserted into a Datagrid in your Silverlight application, and editing it from there.

The premise for following the guide in this post is that you have a project with the MVVM design pattern, and that you have already created a Datagrid that is populated with data from a database. Furthermore, you should be willing to open ChildWindows using the slightly un-MVVM'ish method described in my last post. If anybody wants to follow the guide I provide in this post, it is definitely recommended first to have followed the guide on how to open ChildWindows, although the main aspects of it also are covered here.

Now, I use my grid in order to display questions that are used for a questionnaire, that determines whether a company is eligible for becoming certified for the standard SA8000, ie. whether their working conditions are acceptable. Therefore, the questions in the example are all related to working conditions. The colors each question is given represents the severity associated with a company answering 'yes' or 'no' to the question (obviously, the companies filling out the questionnaire can't see the colors - it's just used afterwards, to get an overview over how well the company did).

To simplify the example, I will only post the code related to editing of the question. The add and delete methods are very much alike, and after having gone through the how-to, you can probably figure out how to change what needs to be changed. If not, drop a comment and I'll add it. To simplify further, I will not add the editing of the groups or the colors.

My whole grid looks like this:

MVVM edit/add/delete data in datagrid

So, what I wish to achieve is this: When I select a row from the grid and press the edit button, a child window has to appear with the data from the selected row inserted into an editable textbox. When I hit the save button in the child window, the database has to be updated, and the datagrid in the main window has to be updated as well, having inserted the changes from the database.

To sum up, I want my ChildWindow to look like this:

Again, you can disregard the colors and the group section, as I will only be focusing on the textbox with the question.

OBS: All code for both the MainPage and the ChildWindow are in the ViewModel for the MainPage.

So. From the start:

Step 1: In the ViewModel for you MainPage, you'll add properties for the grid, the selected row in the grid, the question in the mainpage, and the question in the ChildWindow. Notice that the SelectedQuestion property for the MainPage is set in the SelectedQuestionRow which is updated everytime the user clicks on a new row in the grid.

Notice that the object I will populate my grid with is called QuestionGrid, which was of course defined in the Model. You'll just have to update this to whatever you named yours.


//The property that will be tied to the ItemSource in XAML
   private QuestionGrid _questionGrid;
        public QuestionGrid QuestionGrid
        {
            get { return _questionGrid; }
            set
            {
                _questionGrid = value;
                RaisePropertyChanged("QuestionGrid");
            }

        }

        //Selected row.  THe property that will be binded to SelectedItem in XAML.
        private QuestionGrid _selectedQuestionRow;
        public QuestionGrid SelectedQuestionRow
        {
            get
            {
                return _selectedQuestionRow;
            }
            set
            {

                _selectedQuestionRow = value;

 //sets the value for the string that will be passed to the childwindow
                SelectedQuestion = value.Question;

 //We want to retrieve the ID as well, for when we update the database
               SelectedQuestionID = value.Q_UI; 

            }
        }
//QuestionID, MainPage
  public int selectedQuestionID;
        public int SelectedQuestionID
        {
            get
            {
                return selectedQuestionID;
            }
            set
            {
                selectedQuestionID = value;
                RaisePropertyChanged("SelectedQuestionID");
            }
        }

//Question for mainpage
        public string selectedQuestion;
        public string SelectedQuestion
        {
            get
            {
                return selectedQuestion;
            }
            set
            {
                selectedQuestion = value;
                RaisePropertyChanged("SelectedQuestion");
            }
        }

 //Properties for the grid. 

   //The question

            private string question_EditCW;
            public string Question_EditCW
            {
                get { return question_EditCW; }
                set
                {
                    question_EditCW = value;
                    RaisePropertyChanged("Question_EditCW");
  //SaveEdit is the name of the command we will create later, to save the changes to the database
                    SaveEdit.RaiseCanExecuteChanged();

                }
            }  

So, now I have created the properties, and now we will define the xaml to both the MainPage and the ChildWindow:

Step 2: Create the grid and button in the MainPage (the command binded to the button will open the child window)

Notice that the grid binds to QuestionGrid and SelectedQuestionRow, created earlier, but also notice that the question itself in the Textblock in the gird is binded to the attribute name of the object, created in the Model.


<!-- MainPage: DATAGRID -->

 <data:DataGrid x:Name="AllCompaniesGrid"
                SelectedItem="{Binding SelectedQuestionRow, Mode=TwoWay}"
                ItemsSource="{Binding QuestionGrid}"   

                AutoGenerateColumns="False"
                VerticalScrollBarVisibility="Visible" >

    <data:DataGrid.Columns>
        <!--Question Column. -->
        <data:DataGridTemplateColumn Header="Questions">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Question}" />
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
 </data:DataGrid>

<!-- The button! -->

 <Button Content="Edit" Style="{StaticResource Knap1}" Width="60" Command="{Binding EditQuestion}"  />

Step 3: Create textbox and button in the ChildWindow (the command binded to the button will update the database, close the window, and update the grid).


<!-- The textbox -->
 <TextBox Text="{Binding Question_EditCW, Mode=TwoWay}"     />

<!-- The button! -->

  <Button x:Name="Add" Command="{Binding SaveEdit}" Content="Editquestion"  />

Step 4: Define the command for button in the MainPage, in the ViewModel. I will go through this very quickly, but look at my old post here, if you have doubts about the DelegateCommand, or anything else.

But remember: The delegate command is binded to a button and is divided into three parts: 1) The property part that creates the property that will bind to the edit button, and calls the CanExecute and Execute methods, 2) the CanExecute part that tells wether the button is clickable or not (returns true or false), and 3) the Execute part, that executes if the button is pressable and is pressed: In our case, the Execute will open the new ChildWindow, called EditQuestionCW.


//property for the button command
            private DelegateCommand editQuestion;
            public DelegateCommand EditQuestion
            {
                get
                {
                    if (editQuestion == null)
                        editQuestion = new DelegateCommand(executeEditQuestion, canExecuteEditQuestion);
                    return editQuestion;
                }
            }
//Is the button clickaable? True = yes, False = no.
            private bool canExecuteEditQuestion(object parameter)
            { //Button only clickable if row in grid is selected
                if (SelectedQuestion != null) { return true; }
                else { return false; }
            }

            //New instance of the childwindow. My ChildWindow EditQuestionCW is in the folder ChildWindows.
            ChildWindows.EditQuestionCW EditQuestionCW;
            private void executeEditQuestion(object parameter)
            {

                //Sets the question
                Question_EditCW = SelectedQuestion;
                RedYes_EditCW = true;

                //Opens Child Window
                EditQuestionCW = new ChildWindows.EditQuestionCW(this);
                EditQuestionCW.Show();
           }

These next steps differentiates this post from the former. Here we will define what happens, when the SaveEdit button is pressed in the childWindow, and therefore how we update the changes to the database. But first: Creating the SQL query.

Step 5: Create the SQL query that will update the database and insert the new data. This will happen in your Model . Mine looks somethink like this:

public class EditAddQuestionDA : DBConnection
    {
  public void EditQuestion(int q_ID, string question) //The id and the question as parameters
        {
            SqlConnection dbConn = connectToDB();
            SqlCommand cmd;
            string updateQuestion = string.Format("update question set question = '" + question + "' where q_ID = " + q_ID + "; "); 

            try
            {
                dbConn.Open();

                if (question != "")
                {
                    cmd = new SqlCommand(updateQuestion, dbConn);
                    cmd.ExecuteNonQuery();
                }
                dbConn.Close();
            }
            catch (Exception e) { Console.WriteLine(e.ToString()); }

        }
}

Step 6: In the Service1.svc.cs make the method available to the ViewModel:

 public void EditQuestion(int q_ID, string question)
        {
            EditAddQuestionDA allDA = new EditAddQuestionDA();
            allDA.EditQuestion(q_ID, question);
        }

Step 7: In the IService1.cs:

 [ServiceContract]
    public interface IService1
    {
  [OperationContract]
        void EditQuestion(int q_ID, int G_ID, string question, int yes, int no);
}

So, now the query can be accessed from the ViewModel, so we can create the DelegateCommand for the button in the ChildWindow, that will call the EditQuestion() method:

Step 8: In the ViewModel we create the SaveEdit DelegateCommand, that is divided - like before - to three parts: 1) the DelegateCommand SaveEdit property, 2) The boolean method that determines if the button is clickable, and 3) The Execution of the code that will save the new question to the database, and update the grid in the MainPage.


      private DelegateCommand saveEdit;
            public DelegateCommand SaveEdit
            {
                get
                {
                    if (saveEdit == null)
                        saveEdit = new DelegateCommand(executeSaveEdit, canSaveEDit);
                    return saveEdit;
                }
            }

//Can Execute - Defines if the button should be clickable.
//True = Yes, false = no. Yes if the question is not identical to the one in the grid = no changes made
            private bool canSaveEDit(object parameter) //Definerer om knappen er klik-bar.
            {

              if (SelectedQuestion == Question_EditCW)
                {
                    return false;  //Only clickable if changes have been made
                }

                else { return true; } //Not identical -> return true
            }

//When the button is pressed:
            private void executeSaveEdit(object parameter)
            {
                //updates the grid (only visually, not in the DB
                SelectedQuestionRow.Question = Question_EditCW;

                //WE cose the childwindow, and call the method that will insert the new question into the database

                EditQuestionCW.Close();

                QMServiceReference.Service1Client WebService.EditQuestionCompleted += new EventHandler<AsyncCompletedEventArgs>(WebService_EditQuestionCompleted); //Defines the method that will be called when the database has been called.
                WebService.EditQuestionAsync(SelectedQuestionID, Question_EditCW); //Calls the method

            }

//The method that is called when the database has been updated: 

  void WebService_EditQuestionCompleted(object sender, AsyncCompletedEventArgs e)
        {
           //When the database has been updated we update the grid in the MainPage. 

            WebService.GetQuestionGridCompleted += new EventHandler<GetQuestionGridCompletedEventArgs>(WebService_GetQuestionGridCompleted);
            WebService.GetQuestionGridAsync(SelectedQuestionnaireCB);

        }

//When the GetQuestionGrid query has been run - insert the results into the grid: 

        void WebService_GetQuestionGridCompleted(object sender, GetQuestionGridCompletedEventArgs e)
        {

            QuestionGrid = e.Result;
        }

Enjoy!

Filed under: C#, MVVM, Silverlight 4, XAML 6 Comments