Introduction

In LightSwitch Beta 1, I'm pretty sure that we had the ability to select multiple rows in a grid. In Beta 2, that ability disappeared, and has been sorely missed by many ever since.

People have come up with a couple of workarounds, like adding a boolean property to an entity being displayed in the grid, so that each row has a checkbox in it that can be ticked. But I've never been all that happy with having to do anything like that.

I did some research, and discovered that the Silverlight 4 grid was in fact capable of multiple selection, but for some reason the functionality hadn't been exposed in B2. The situation didn't change for the RTM.

I've been pretty vocal in the community about the fact that I felt that this was a pretty basic grid requirement, and should definitely be included in the default grid. It was one of the first things that my clients asked for when I demoed their application to them for the first time.  When I told them that you couldn't do it they were very surprised, and not very happy. They mournfully pointed out that this functionality had been available in the MS Access version of the previous program I had written for them.

So my search continued, & today (2011/08/26) I finally found the solution. I'm going to share that solution with you now, in this article.

Building the Sample

The prerequisites for this sample are:

Description

The first step is to create a LightSwitch project.

To create a LightSwitch project:

  1. On the Menu Bar in Visual Studio:

    • choose File

    • choose New Project

  2. In the New Project dialog box

    • select the LightSwitch node

    • then select LightSwitch Application (Visual Basic)

    • in the Name field, type AddUrlToNavigationMenu as the name for your project

    • click the OK button to create a solution that contains the LightSwitch project

Next, we need to add a table to hold some data.

To add the table:

  1. In the Solution Explorer in:

    • right-click the Data Sources folder

    • select Add Table (see Figure 1)

      Figure 1. Adding new table

    • change the Name to DemoItem (see Figure 2)

      Figure 2. Changing table name

    • double-click the table to open it in the Table Designer

    • add two string properties (see Figure 3)

      Figure 3. Adding two properties

    • save & close the table

Now we need to add a screen that has a grid on it.

To add the screen:

  1. In the Solution Explorer in:

    • right-click the Screens folder

    • select Add Screen

    • select Editable Grid Screen (see figure 4-1)

    • set Screen Data to DataItems (see figure 4-2)

    • set Screen Name to DemoItemList (see figure 4-3)

    • click OK

      Figure 4. Adding new screen

  2. You'll now have a screen, with a grid that has two columns (see Figure 5)

    Figure 5. Screen with two-column grid

Now let's test the application so far, add some data, & demonstrate, that by default we can only select one grid row at a time.

To test the application:

  1. Press F5:

    • in the screen's grid, enter a few rows of data (see Figure 6)

      Figure 6. Adding data

    • click on the screen's Save button

    • click on any row

    • notice that you can't select multiple rows, using the Control and Shift keys, as you normally would in other applications

With just a little bit of code, we can enable multiple row selection.

To enable multiple row selection:

  1. We're going to need a reference to System.Windows.Controls.Data (to be able to access the Silverlight grid control), so let's do that now.

    In Solution Explorer:

    • click the View button (see Figure 7)

    • select File View

      Figure 7. File view

    • right-click the Client project

    • select Add Reference

    • find and click System.Windows.Controls.Data (see Figure 8)

    • click OK

      Figure 8. Adding System.Windows.Controls.Data reference

  2. Switch back to Logical View

    • click the View button (see Figure 9)

    • select Logical View

      Figure 9. Selecting Logical view

  3. In the Screen Designer:

    • click the Write Code dropdown (see Figure 10)

    • select DemoItemList_InitializeDataWorkspace

      Figure 10. Writing screen code

  4. Replace the all of the code that was created for you in DemoItemList.vb, with the code in Listing 1

      Listing 1. DemoItemList class code

      C#Visual Basic
      Edit|Remove
      //we need this to be able to use DataGrid in our code 
      using System.Windows.Controls; 
       
      namespace LightSwitchApplication 
      { 
          public class DemoItemList 
          { 
              //although not strictly necessary for this small example 
              //it's good practice to use constants/variables like these 
              //in case you want to use them in more than just the ControlAvailable method 
       
              //this has to match the actual name of the grid control (by default it gets called "grid") 
              private const string ITEMS_CONTROL = "grid"; 
       
              //this is somewhere to store a reference to the grid control 
              private DataGrid _itemsControl = null; 
       
              #region  Event Handlers 
              private void DemoItemList_InitializeDataWorkspace(System.Collections.Generic.List<Microsoft.LightSwitch.IDataService> saveChangesTo) 
              { 
                  //here we're adding an event handler to get a reference to the grid control, when it becomes available 
                  //and we have no way of knowing when that will be 
                  this.FindControl(ITEMS_CONTROL).ControlAvailable += DemoItems_ControlAvailable; 
              } 
       
              private void DemoItems_ControlAvailable(object send, ControlAvailableEventArgs e) 
              { 
                  //we know that the control is a grid, but we use TryCast, just in case 
                  _itemsControl = e.Control as DataGrid; 
       
                  //if the cast failed, just leave, there's nothing more we can do here 
                  if (_itemsControl == null) 
                  { 
                      return; 
                  } 
       
                  //set the property on the grid that allows multiple selection 
                  _itemsControl.SelectionMode = DataGridSelectionMode.Extended; 
                  _itemsControl.SelectionChanged += new SelectionChangedEventHandler(ItemsControl_SelectionChanged); 
              } 
              #endregion 
       
          } 
      }

Let's test the application again, & check that now we can in fact select multiple grid rows.

To test the application:

  1. Press F5:

    • in the screen's grid, select any number rows of data, by holding the Control key while you click each row (see Figure 11)

      Figure 11. Selecting multiple rows

  2. Close the application when you've finshed testing.

Now that we're able to select multiple rows, it'd be nice to be able to do something with them. We'll add a button to the grid, that processes the selected rows when you click it.

To process multiple rows:

  1. In the Screen Designer (double-click the DemoItemList screen, if it's not already open):

    • expand the data grid's Command Bar (see Figure 12-1)

    • expand the Add dropdown (see Figure 12-2)

    • select New Button (see Figure 12-3)

      Figure 12. Adding a grid button

    • set the Name to ProcessItems (see Figure 13)

      Figure 13. New Method name

  2. In the Left Pane:

    • click on the ProcessItems method (see Figure 14)

      Figure 14. ProcessItems method

  3. In the Properties Pane:

    • click on the Edit CanExecute Code link (see Figure 15-1)

    • add the code for the CanExecute method (see Listing 2)

      Listing 2. CanExecute method code

      C#Visual Basic
      Edit|Remove
      //insert this event handler wireup in the screen's DemoItems_ControlAvailable method 
      _itemsControl.SelectionChanged += new System.EventHandler(ItemsControl_SelectionChanged); 
       
      //this is an optional advanced technique, discussed in the book 
      //that Tim Leung & I are currently writing 
      //Pro Visual Studio LightSwitch 2011 Development 
      private void ItemsControl_SelectionChanged() 
      { 
          switch (_itemsControl == null) 
          { 
              case true: 
                  _selectedCount = 0; 
       
                  break; 
              case false: 
                  _selectedCount = _itemsControl.SelectedItems.Count; 
                  break; 
          } 
      } 
       
      private void ProcessItems_CanExecute(ref bool result) 
      { 
          //only enable rows have actually been selected 
          result = (_selectedCount > 0); 
      }
    • click on the Edit Execute Code link (see Figure 15-2)

    • add the code for the Execute method (see Listing 3)

      Listing 3. Execute method code

      C#Visual Basic
      Edit|Remove
      private void ProcessItems_Execute() 
      { 
          //if the variable hasn't been set, just leave 
              //there's nothing more we can do here 
          if (_itemsControl == null)    { return; } 
       
          StringBuilder names = new StringBuilder(); 
       
          //loop through the selected rows  
          //we're casting each selected row as a DemoItem 
          //so we get access to all the properties of the entity that the row represents 
          foreach (DemoItem item in _itemsControl.SelectedItems) 
          { 
              //you would normally process each row 
              //but here we're just concatenating the properties as 
                      //proof that we are processing the selected rows 
              names.Append(item.PropertyOne); 
              names.Append("; "); 
          } 
       
          //simply display the result 
          this.ShowMessageBox(string.Format("The values of the selected rows are: {0}", names), caption:"Demo Items", button:MessageBoxOption.Ok); 
      }

      Figure 15. Code links

  4. The code in the DemoItemList.vb file should now look like Listing 4

      Listing 4. Complete DemoItemList class code

      C#Visual Basic
      Edit|Remove
      //we need this to be able to use DataGrid in our code 
      using System.Windows.Controls; 
       
      //this is for the StringBuilder 
      using System.Text; 
       
      namespace LightSwitchApplication 
      { 
          public class DemoItemList 
          { 
              //although not strictly necessary for this small example 
              //it's good practice to use constants/variables like these 
              //in case you want to use them in more than just the ControlAvailable method 
       
              //this has to match the actual name of the grid control (by default it gets called "grid") 
              private const string ITEMS_CONTROL = "grid"; 
       
              //this is somewhere to store a reference to the grid control 
              private DataGrid _itemsControl = null; 
       
              //this is somewhere to store the selected row count 
              private int _selectedCount = 0; 
       
              #region  Event Handlers 
              private void DemoItemList_InitializeDataWorkspace(System.Collections.Generic.List<Microsoft.LightSwitch.IDataService> saveChangesTo) 
              { 
                  //here we're adding an event handler to get a reference to the grid control 
                  //when it becomes available 
                  //and we have no way of knowing when that will be 
                  this.FindControl(ITEMS_CONTROL).ControlAvailable += DemoItems_ControlAvailable; 
              } 
       
              private void DemoItems_ControlAvailable(object send, ControlAvailableEventArgs e) 
              { 
                  //we know that the control is a grid, but we use TryCast, just in case 
                  _itemsControl = e.Control as DataGrid; 
       
                  //if the cast failed, just leave, there's nothing more we can do here 
                  if (_itemsControl == null) 
                  { 
                      return; 
                  } 
       
                  //set the property on the grid that allows multiple selection 
                  _itemsControl.SelectionMode = DataGridSelectionMode.Extended; 
                  _itemsControl.SelectionChanged += new SelectionChangedEventHandler(ItemsControl_SelectionChanged); 
              } 
       
              //this is an optional advanced technique, discussed in the book 
              //that Tim Leung & I are currently writing 
              //Pro Visual Studio LightSwitch 2011 Development 
              private void ItemsControl_SelectionChanged() 
              { 
                  switch (_itemsControl == null) 
                  { 
                      case true: 
                          _selectedCount = 0; 
                          break; 
       
                      case false: 
                          _selectedCount = _itemsControl.SelectedItems.Count; 
                          break; 
                  } 
              } 
              #endregion 
       
              #region  Process Items 
              private void ProcessItems_CanExecute(ref bool result) 
              { 
                  //only enable the button if the variable has been initialised 
                  //& the rows have actually been selected 
                  result = (_selectedCount > 0); 
              } 
       
              private void ProcessItems_Execute() 
              { 
                  //if the variable hasn't been set, just leave, there's nothing more we can do here 
                  if (_itemsControl == null) { return; } 
       
                  StringBuilder names = new StringBuilder(); 
       
                  //loop through the selected rows  
                  //we're casting each selected row as a DemoItem 
                  //so we get access to all the properties of the entity that the row represents 
                  foreach (DemoItem item in _itemsControl.SelectedItems) 
                  { 
                      //you would normally process each row 
                      //but here we're just concatenating the properties as 
                      //proof that we are processing the selected rows 
                      names.Append(item.PropertyOne); 
                      names.Append("; "); 
                  } 
       
                  //simply display the result 
                  this.ShowMessageBox(string.Format("The values of the selected rows are: {0}", names), caption: "Demo Items", button: MessageBoxOption.Ok); 
              } 
              #endregion 
          } 
      }
  5. Run the program again. Select some rows, & click on the Process Items button.

More Information

For more information on LightSwitch Development, see the LightSwitch Developer Center, or the LightSwitch Forum.