Cascading comboboxes refers to having a combobox being populated depending on the selected item of another combobox. In my example, I have what is called a Group Questionnaire, which can contain any number of Questionnaires.

The main purpose with this post is to show how to deal with the Silverlight bug that entails that the binding of the SelectedItem to the second combobox is broken the second time the first combobox is updated, if it is bound to an ObservableCollection.

The actual populating of the Cascading comboboxes is not at all different than when a combobox is populated normally, but I will show it very quickly anyway.

The first thing you need to do is create a new class that will inherit the normal ComboBox:

Step 1: Create the following class in your ViewModel, which inherits the ComboBox.

Step 2: Create the UploadFile command that the button is bound. If you have not already created the DelegateCommand.cs needed to handle your commands from buttons, add the following class to your ViewModel:

Remember that you have to make changes to the .config files on both the client and the server, in order to allow larger files to be sent.

Step 4: Be sure to have a table in your database, where you can upload the files: You'll need a table with the following 3 columns: An Int attribute to store the unique value, a varchar(50) for the name of the file, and a varbinary(MAX) for containing the bite array that constitutes the file.

Step 5: I am not going show how to add the [ServiceContract] etc. in the Model, as it has been showed many times before, so I am just showing the UploadFile() that is defined in the Model:

The most important thing to notice is that the user has to 'confirm' the download twice: When he first clicks the button, and again when it has been downloaded, and the user can chose to 'Save' or 'Cancel' to open the dialog box, where the user determines where on his desktop he want to save the file.

Step 1. Create the object that is returned from the database containing the file you want to download. The file object has two attributes: The name of the file, and the file itself, which is saved as a bite array, and is of type SqlBytes:

Step 4: In the ViewModel, create the command that executes when the "Download File" button is clicked, and that calls the GetFile() method described in step 2.

You will have to notice several things: First, the executeDownloadFile calls the GetFile(), and when the call is completed, the WebService_GetFileCompleted method is called, and return value (the file) is saved in a newly created object of type FileDTO , i.e. the class you created in step 1.

//Property bound to the button
private DelegateCommand downloadFile;
public DelegateCommand DownloadFile
{
get
{
if (downloadFile == null)
downloadFile = new DelegateCommand(executeDownloadFile, canExecuteDownloadFile);
return downloadFile;
}
}
//Defines if the button is clickable
private bool canExecuteDownloadFile(object parameter) //Definerer om knappen er klik-bar.
{
return true; //always clickable
}
private void executeDownloadFile(object parameter)
{
//Shows progress bar while downloading
ShowText = "Downloading file";
BusyWindow = true;
WebService = new Service1Client();
//defines the method that is called when it returns with a value
WebService.GetFileCompleted += new EventHandler<GetFileCompletedEventArgs>(WebService_GetFileCompleted);
//Calls the method
WebService.GetFileAsync(AID);
}
//Method is called when the Model has returned with a value
void WebService_GetFileCompleted(object sender, GetFileCompletedEventArgs e)
{
// Call the method CallBackFromService with the result (the file) as a parameter
CallBackFromService(e.Result);
}
//Create new instance of FileDTO.
FileDTO fileDTO;
public void CallBackFromService(FileDTO fileDTOCallBack)
{
Set to the local version of the fileDTO, close progressbar
fileDTO = fileDTOCallBack;
BusyWindow = false;
//Makes button from (3) visible: The user has to confirm that he wants to download the file.
AfterDownload = Visibility.Visible;
}

Step 6: After the CallBackFromService has been called, the user must confirm that he wants to download the file. The button in "Save File" in window (3) shown above is bound to the command SaveAttachment:

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:

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.

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.

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:

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;
}

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.

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:

(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:

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.

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.

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.

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.