Sue Hernandez's SharePoint Blog

SharePoint and Related Stuff

Category Archives: SPGridView

MOSS – SPGridView with CheckBox Column

In this article, we will explore using the SPGridView control, and adding an editable checkbox column, with JavaScript that allows us to “Select All”.   The full code is at the bottom of the article. I used the WSPBuilder tool to create the project and add the Solution information (i.e. the Feature File, elements file, etc) automatically.

In this example, we have a user cleanup task; we have a list of domain account names that have matching domain account names in another domain.  The stored procedure does the heavy lifting, in examining the account names and determining if there are “matches” based on a set of criteria.  A Requesting user will input their account name, and press Search, and then they will get a list of all users who are members of every site on which the Requesting user has administrative access.  So in short, all their users for their sites they admin.  We will then give them a button which, when clicked, will perform some action on these users, as long as they are checked.

NOTE:  In this article, we will not explore putting the grid in Edit mode, nor paging, nor sorting.  Please read the reference articles for some good tips.

References

First of all, there are many good articles out there explaining the need to instantiate the SPGridView control in the CreateChildControls method, and to set your databinding in the OnLoad event, as long as the page is not posting back.

protected override void CreateChildControls()
  {
   if (!_error)
   {
    try
    {
     base.CreateChildControls();

     // Add spacer
     this.Controls.Add(new LiteralControl("<br />"));

     // Add TextBox and Search Button
     txtUserName = new TextBox();
     this.Controls.Add(txtUserName);
     cmdSearch = new Button();
     cmdSearch.Text = "Search";
     cmdSearch.Click += new EventHandler(cmdSearch_Click);
     this.Controls.Add(new LiteralControl("&nbsp;&nbsp;&nbsp;"));
     this.Controls.Add(cmdSearch);

     // Add spacer and Select All
     this.Controls.Add(new LiteralControl("<br /><br />"));
     this.Controls.Add(new LiteralControl("<a href='javascript:SelectAllCheckBoxes();'>Select/Deselect All</a><br />"));

     // Add GridView
     gridView = new SPGridView();
     CreateBoundFields(ref gridView);
     this.Controls.Add(gridView);

     // Add spacer
     this.Controls.Add(new LiteralControl("<br />"));

     // Add Submit Button
     cmdSubmit = new Button();
     cmdSubmit.Text = "Submit";
     cmdSubmit.Click += new EventHandler(cmdSubmit_Click);
     cmdSubmit.OnClientClick = "return confirm('Are you sure?');";
     this.Controls.Add(cmdSubmit);

     // Add Javascript
     this.Controls.Add(GetJavaScript());
    }
    catch (Exception ex)
    {
     HandleException(ex);
    }
   }
  }
 

Here, we first add a text box and a Search button, so that our user may input an Active Directory account name, which will be used in the data binding when we call the stored procedure.  We add an anchor tag with a javascript function, which is what we’ll use to toggle between Selected and Unselected.   Next we instantiate the SPGridView.  Notice that we did not set the variable here – the variable is in the private variable section, but we don’t instantiate until we get into CreateChildControls.  After that, we add our “Submit” button, which will perform whatever action you want to perform on all of the checked items.  Finally, we add a Literal Control that holds our JavaScript Toggle function.  We use the gridView.ClientID in the JavaScript to identify which input tags are actually the checkboxes that we want to toggle.

The CreateBoundFields method is where we set up our binding, for as it turns out, you always have to set SPGridView.AutoGenerateColumns to false and manually bind the columns to your data source.

  private void CreateBoundFields(ref SPGridView gridView)
  {
   gridView.Columns.Clear();
   gridView.AutoGenerateColumns = false;

   //CheckBoxField cbField = new CheckBoxField();
   //cbField.DataField = "Checked";
   //cbField.HeaderText = "Include";
   //gridView.Columns.Add(cbField);

   TemplateField checkboxCol = new TemplateField();
   checkboxCol.HeaderText = "Include";
   // Call our custom template here
   checkboxCol.ItemTemplate = new CustomSPGridViewTemplate(ListItemType.Item, "Select Item");
   gridView.Columns.Add(checkboxCol);

   BoundField colName = new BoundField();
   colName.DataField = "AccountName";
   colName.HeaderText = "Account Name";
   gridView.Columns.Add(colName);

   colName = new BoundField();
   colName.DataField = "MatchingAccountName";
   colName.HeaderText = "Matching Account Name";
   gridView.Columns.Add(colName);

  }

Notice that I have a commented out section here.  I had originally used a CheckBoxField type and that worked just fine in a sense.  However, number one, it was bound to my data source, so I actually had to create a data table with a “Checked” column in it, and two, you have to enable editing of the SPGridView.  In other words, to change the checkbox column from checked to unchecked or vice versa, you actually had to put the whole row in edit mode (post back), click the checkbox, then update the row (post back).  I just wanted a simple checkbox column that I could easily just check…check…check.

So here what we do, is we actually create a custom Class, inheriting from the ITemplate interface.  I called it CustomSpGridViewTemplate in this case, just like in the article I referenced at the top.  Now contrary to what I think is stated in that article, I am simply instantiating this TemplateField in code, and it’s not giving me any problems, probably because I have not enabled Paging or Sorting.

In the CustomSPGridViewTemplate class, we simply tell the Template what to put when it’s in Header mode, and what to put when it’s in ListItemType mode.  

public class CustomSPGridViewTemplate : ITemplate
 {
  private ListItemType templateType;
  private string columnName;

  public CustomSPGridViewTemplate(ListItemType type, string column)
  {
   this.templateType = type;
   this.columnName = column;
  }

  #region ITemplate Members

  public void InstantiateIn(Control container)
  {
   Literal lc = new Literal();
   switch (templateType)
   {
    case ListItemType.Header:
     lc.Text = "<b>" + columnName + "</b>";
     container.Controls.Add(lc);
     break;
    case ListItemType.Item:
     CheckBox checkBox = new CheckBox();
     checkBox.Checked = true;
     checkBox.ID = "CustomCheckboxID";
     checkBox.Visible = true;
     container.Controls.Add(checkBox);
     break;
   }
  }

  #endregion
 }

When the user clicks the submit button, we simply iterate through the GridViewRowCollection of the SPGridView.  The first cell will be the checkbox column in this example, so we simply get the first control in that cell and cast it as a CheckBox object.  Then we can tell if it’s checked or unchecked.  Next, I also output the text that was in the next cell, which contains my list of domain Account Names.

  private void cmdSubmit_Click(object sender, EventArgs e)
  {
   this.Controls.Add(new LiteralControl("<br><br>Submit Pressed"));

   foreach (GridViewRow row in gridView.Rows)
   {
    CheckBox cb = row.Cells[0].Controls[0] as CheckBox;
    this.Controls.Add(new LiteralControl("<br>" + cb.Checked.ToString() + " " + row.Cells[1].Text));
   }
  }

Finally, we output our JavaScript.

private LiteralControl GetJavaScript()
  {
   StringBuilder sb = new StringBuilder();

   sb.Append("\n\n");
   sb.Append("<script language='javascript'>\n");
   sb.Append("   function SelectAllCheckBoxes() \n");
   sb.Append("   { \n");
   sb.Append("      var checkboxes = document.getElementsByTagName('input'); \n");
   sb.Append("      for(var i=0; i<checkboxes.length; i++) \n");
   sb.Append("      { \n");
   sb.Append("         // Look for a CheckBox \n");
   sb.Append("         var checkbox = checkboxes[i]; \n");
   sb.Append("         \n");
   sb.Append("         // Verify it's the right name \n");
   sb.Append("         var start = '" + gridView.ClientID + "'; \n");
   sb.Append("         var end = 'CustomCheckboxID'; \n");
   sb.Append("         var startsWith = checkbox.id.match('^'+start)==start; \n");
   sb.Append("         var endsWith = checkbox.id.match(end+'$')==end; \n");
   sb.Append("         \n");
   sb.Append("         // Make the switch \n");
   sb.Append("         if(startsWith && endsWith) \n");
   sb.Append("         { \n");
   sb.Append("            checkbox.checked = !checkbox.checked; \n");
   sb.Append("         } \n");
   sb.Append("      } \n");
   sb.Append("   } \n");
   sb.Append("</script>\n");

   return new LiteralControl(sb.ToString());
  }

We first iterate through all elements with a tag name of “input”.  I want to ensure that we have the right checkboxes, so I do a string comparison – I ensure that it starts with the ClientID of the SPGridView Control, and that it ends with the control name that I specified in the custom Class, in this case “CustomCheckboxID”.  If it matches that criteria, I simply toggle the checkbox from checked to unchecked or vice versa.  I could also have done something like looked at the link we clicked and if it said “Select All” , to make sure all the checkboxes were checked, and then change the link’s text to “Deselect All”. 

Following is the full code that the article references.

 [Guid("5cd4210a-6816-4ec2-959e-78b01347df8c")]
 public class MultiDomainUserMatchup : Microsoft.SharePoint.WebPartPages.WebPart
 {
  private bool _error = false;
  private SqlDataReader rdr = null;
  private SPGridView gridView;
  private Button cmdSubmit;
  private Button cmdSearch;
  private TextBox txtUserName;

  public MultiDomainUserMatchup()
  {
   this.ExportMode = WebPartExportMode.All;
  }

  #region Overrides and Event Handlers

  /// <summary>
  /// Create all your controls here for rendering.
  /// Try to avoid using the RenderWebPart() method.
  /// </summary>
  protected override void CreateChildControls()
  {
   if (!_error)
   {
    try
    {
     base.CreateChildControls();

     // Add spacer
     this.Controls.Add(new LiteralControl("<br />"));

     // Add TextBox and Search Button
     txtUserName = new TextBox();
     this.Controls.Add(txtUserName);
     cmdSearch = new Button();
     cmdSearch.Text = "Search";
     cmdSearch.Click += new EventHandler(cmdSearch_Click);
     this.Controls.Add(new LiteralControl("&nbsp;&nbsp;&nbsp;"));
     this.Controls.Add(cmdSearch);

     // Add spacer and Select All
     this.Controls.Add(new LiteralControl("<br /><br />"));
     this.Controls.Add(new LiteralControl("<a href='javascript:SelectAllCheckBoxes();'>Select/Deselect All</a><br />"));

     // Add GridView
     gridView = new SPGridView();
     CreateBoundFields(ref gridView);
     this.Controls.Add(gridView);

     // Add spacer
     this.Controls.Add(new LiteralControl("<br />"));

     // Add Submit Button
     cmdSubmit = new Button();
     cmdSubmit.Text = "Submit";
     cmdSubmit.Click += new EventHandler(cmdSubmit_Click);
     cmdSubmit.OnClientClick = "return confirm('Are you sure?');";
     this.Controls.Add(cmdSubmit);

     // Add Javascript
     this.Controls.Add(GetJavaScript());
    }
    catch (Exception ex)
    {
     HandleException(ex);
    }
   }
  }

  /// <summary>
  /// Ensures that the CreateChildControls() is called before events.
  /// Use CreateChildControls() to create your controls.
  /// </summary>
  /// <param name="e"></param>
  protected override void OnLoad(EventArgs e)
  {
   if (!_error)
   {
    try
    {
     base.OnLoad(e);
     this.EnsureChildControls();

     // Your code here...
     if (!Page.IsPostBack)
     {
      BindGrid("AKG\noUser");
     }
    }
    catch (Exception ex)
    {
     HandleException(ex);
    }
   }
  }

  /// <summary>
  /// Submit Button event handler
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void cmdSubmit_Click(object sender, EventArgs e)
  {
   this.Controls.Add(new LiteralControl("<br><br>Submit Pressed"));

   foreach (GridViewRow row in gridView.Rows)
   {
    CheckBox cb = row.Cells[0].Controls[0] as CheckBox;
    this.Controls.Add(new LiteralControl("<br>" + cb.Checked.ToString() + " " + row.Cells[1].Text));
   }
  }

  /// <summary>
  /// Search Button Event Handler
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void cmdSearch_Click(object sender, EventArgs e)
  {
   BindGrid(txtUserName.Text);
  }
  #endregion

  #region GridView Helper

  private void CreateBoundFields(ref SPGridView gridView)
  {
   gridView.Columns.Clear();
   gridView.AutoGenerateColumns = false;

   //CheckBoxField cbField = new CheckBoxField();
   //cbField.DataField = "Checked";
   //cbField.HeaderText = "Include";
   //gridView.Columns.Add(cbField);

   TemplateField checkboxCol = new TemplateField();
   checkboxCol.HeaderText = "Include";
   // Call our custom template here
   checkboxCol.ItemTemplate = new CustomSPGridViewTemplate(ListItemType.Item, "Select Item");
   gridView.Columns.Add(checkboxCol);

   BoundField colName = new BoundField();
   colName.DataField = "AccountName";
   colName.HeaderText = "Account Name";
   gridView.Columns.Add(colName);

   colName = new BoundField();
   colName.DataField = "MatchingAccountName";
   colName.HeaderText = "Matching Account Name";
   gridView.Columns.Add(colName);

  }

  #endregion

  #region Bind Data

  private void BindGrid(string input)
  {
   // Get Data Fresh
   SqlConnection connection = new SqlConnection("Data Source=YourServer;Initial Catalog=YourDatabaseName;User ID=YourUser;Password=YourPassword");
   SqlCommand cmd = new SqlCommand("lkp_YourStoredProcedure", connection);
   cmd.CommandType = System.Data.CommandType.StoredProcedure;

   cmd.Parameters.Add(new SqlParameter("@userName", CleanInput(input)));

   try
   {
    connection.Open();
    rdr = cmd.ExecuteReader();

    if (rdr != null)
    {
     if (rdr.HasRows)
     {
      gridView.DataSource = rdr;
      gridView.DataBind();
     }
    }
   }
   finally
   {
    try { connection.Close(); }
    catch { }
   }
  }

  #endregion

  #region Helpers

  /// <summary>
  /// Clean the input of a text box for use in SQL Parameter
  /// </summary>
  /// <param name="input"></param>
  /// <returns></returns>
  private string CleanInput(string input)
  {
   // Clean the input for SQL Injection
   return input.Replace("'", "''");
  }

  /// <summary>
  /// Get page Javascript
  /// </summary>
  /// <returns></returns>
  private LiteralControl GetJavaScript()
  {
   StringBuilder sb = new StringBuilder();

   sb.Append("\n\n");
   sb.Append("<script language='javascript'>\n");
   sb.Append("   function SelectAllCheckBoxes() \n");
   sb.Append("   { \n");
   sb.Append("      var checkboxes = document.getElementsByTagName('input'); \n");
   sb.Append("      for(var i=0; i<checkboxes.length; i++) \n");
   sb.Append("      { \n");
   sb.Append("         // Look for a CheckBox \n");
   sb.Append("         var checkbox = checkboxes[i]; \n");
   sb.Append("         \n");
   sb.Append("         // Verify it's the right name \n");
   sb.Append("         var start = '" + gridView.ClientID + "'; \n");
   sb.Append("         var end = 'CustomCheckboxID'; \n");
   sb.Append("         var startsWith = checkbox.id.match('^'+start)==start; \n");
   sb.Append("         var endsWith = checkbox.id.match(end+'$')==end; \n");
   sb.Append("         \n");
   sb.Append("         // Make the switch \n");
   sb.Append("         if(startsWith && endsWith) \n");
   sb.Append("         { \n");
   sb.Append("            checkbox.checked = !checkbox.checked; \n");
   sb.Append("         } \n");
   sb.Append("      } \n");
   sb.Append("   } \n");
   sb.Append("</script>\n");

   return new LiteralControl(sb.ToString());
  }

  /// <summary>
  /// Clear all child controls and add an error message for display.
  /// </summary>
  /// <param name="ex"></param>
  private void HandleException(Exception ex)
  {
   this._error = true;
   this.Controls.Clear();
   this.Controls.Add(new LiteralControl(ex.Message));
  }

  #endregion
 }

 public class CustomSPGridViewTemplate : ITemplate
 {
  private ListItemType templateType;
  private string columnName;

  public CustomSPGridViewTemplate(ListItemType type, string column)
  {
   this.templateType = type;
   this.columnName = column;
  }

  #region ITemplate Members

  public void InstantiateIn(Control container)
  {
   Literal lc = new Literal();
   switch (templateType)
   {
    case ListItemType.Header:
     lc.Text = "<b>" + columnName + "</b>";
     container.Controls.Add(lc);
     break;
    case ListItemType.Item:
     CheckBox checkBox = new CheckBox();
     checkBox.Checked = true;
     checkBox.ID = "CustomCheckboxID";
     checkBox.Visible = true;
     container.Controls.Add(checkBox);
     break;
   }
  }

  #endregion
 }