Sunday, September 23, 2012

Add checkbox in WPF Datagrid DatagridTemplateColumnHeader(for checkall)

Here's how to add checkbox in WPF Datagrid DatagridTemplateColumn to simulate checkall. I have included the TSQL script, XAML and C# codes. The source code is available for download here: WPF DataGrid Check/Uncheck All Functionality

Perform these steps below
1. Create an MSSQL database with the following Product Table schema below:
Table: Products
Fields:
-    ProductID (int, autoincrement)
-    ProductName (varchar)
-    UnitPrice (decimal)
-    QuantityPerUnit (varchar)
-    Discontinue (bit) 
SQL Script:
 USE [Your_Database_Name]  
 GO  
 /****** Object: Table [dbo].[Products]  Script Date: 05/27/2013 14:07:39 ******/  
 SET ANSI_NULLS ON  
 GO  
 SET QUOTED_IDENTIFIER ON  
 GO  
 SET ANSI_PADDING ON  
 GO  
 CREATE TABLE [dbo].[Products](  
      [ProductID] [int] NOT NULL,  
      [ProductName] [varchar](50) NULL,  
      [UnitPrice] [decimal](18, 2) NULL,  
      [QuantityPerUnit] [varchar](50) NULL,  
      [Discontinue] [bit] NULL,  
  CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED   
 (  
      [productID] ASC  
 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]  
 ) ON [PRIMARY]  
 GO  
 SET ANSI_PADDING OFF  
 GO  
Note: You may add values after creating the table.

2. Change the App.config with your database settings.
 <?xml version="1.0"?>   
  <configuration>   
  <connectionStrings>   
   <add name="products"   
    connectionString="data source=testPC\SQLEXPRESS;Initial Catalog=your_test_database;User ID=your_user_id;Password=your_password;"   
    providerName="System.Data.SqlClient" />   
  </connectionStrings>   
  <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>   

3. Update your xaml and classes as presented below.
Here is the DataGrid xaml:
 <DataGrid Name="dgProducts" AutoGenerateColumns="False" Grid.Row="1" Grid.RowSpan="3" Grid.ColumnSpan="2" CanUserAddRows="False">    
    <DataGrid.Columns>    
    <DataGridTextColumn Header="Name" Binding="{Binding Path=ProductName}" Width="215"/>    
    <DataGridTextColumn Header="Price" Binding="{Binding Path=UnitPrice}"/>    
    <DataGridTextColumn Header="Quantity Per Unit" Binding="{Binding Path=QuantityPerUnit}" Width="180"/>    
    <DataGridTemplateColumn>    
     <DataGridTemplateColumn.Header>    
     <CheckBox Content="Discontinue All" Click="CheckBox_Click" />    
     </DataGridTemplateColumn.Header>    
     <DataGridTemplateColumn.CellTemplate>    
     <DataTemplate>    
      <CheckBox Name="chkDiscontinue" IsChecked="{Binding Path=Discontinue,Mode=TwoWay}" Margin="45 2 0 0" Click="chkDiscontinue_Click" />    
     </DataTemplate>    
     </DataGridTemplateColumn.CellTemplate>    
    </DataGridTemplateColumn>    
    </DataGrid.Columns>    
   </DataGrid>    
Main Window class:
public partial class MainWindow : Window
{
 public MainWindow()
 {
  InitializeComponent();
 }

 private void Window_Loaded(object sender, RoutedEventArgs e)
 {
  dgProducts.ItemsSource = Connections.GetProduct().AsDataView();
 }

 /// <summary>
 /// check all checkbox
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 private void CheckBox_Click(object sender, RoutedEventArgs e)
 {
  var row = GetDataGridRows(dgProducts);

  if (((CheckBox)sender).IsChecked == true)
  {
   SetCheckbox(row, true);
  }
  else
  {
   SetCheckbox(row, false);
  }
 }

 //individual checking
 private void chkDiscontinue_Click(object sender, RoutedEventArgs e)
 {
  object a = e.Source;
  CheckBox chk = (CheckBox)sender;

  DataGridRow row = FindAncestor<DataGridRow>(chk);
  if (row != null)
  {
   DataRowView rv = (DataRowView)row.Item;
   
   //LINQ or Database Method to Update Product discontinue status
   Connections.UpdateProductDiscontinue((bool)chk.IsChecked, rv["ProductName"].ToString());
  }
 }
 
 private void SetCheckbox(IEnumerable<DataGridRow> row, bool value)
 {
  //loop through datagrid rows
  foreach (DataGridRow r in row)
  {
   DataRowView rv = (DataRowView)r.Item;
   foreach (DataGridColumn column in dgProducts.Columns)
   {
    if (column.GetType().Equals(typeof(DataGridTemplateColumn)))
    {
     rv.Row["Discontinue"] = value;

     //LINQ or Database Method to Update Product discontinue status
     Connections.UpdateProductDiscontinue(value, rv.Row["productname"].ToString());                                                
    }
   }
  }
 }

 public IEnumerable<DataGridRow> GetDataGridRows(DataGrid grid)
 {
  var itemsSource = grid.ItemsSource as IEnumerable;
  if (null == itemsSource) yield return null;
  foreach (var item in itemsSource)
  {
   var row = grid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
   if (null != row) yield return row;
  }
 }

 #region visual tree helpers

 /// <summary>
 /// Returns the first ancester of specified type
 /// </summary>
 public static T FindAncestor<T>(DependencyObject current) where T : DependencyObject
 {
  current = VisualTreeHelper.GetParent(current);
  while (current != null)
  {
   if (current is T)
   {
    return (T)current;
   }

   current = VisualTreeHelper.GetParent(current);
  };
  return null;
 }

 private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
 {
  for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
  {
   DependencyObject child = VisualTreeHelper.GetChild(obj, i);
   if (child != null && child is childItem)
    return (childItem)child;
   else
   {
    childItem childOfChild = FindVisualChild<childItem>(child);
    if (childOfChild != null)
     return childOfChild;
   }
  }
  return null;
 }       

 #endregion
}
Database Manipulation class:
class Connections
{
 public static DataTable GetProduct()
 {
  DataSet ds = new DataSet();
  string query = "Select * from Products;";                    
   
  try
  {
   using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["products"].ConnectionString.ToString()))
   {
    SqlCommand cmd = new SqlCommand(query, conn);
    conn.Open();
    SqlDataAdapter da = new SqlDataAdapter(cmd);                    
    da.Fill(ds);
    conn.Close();
   }
  }
  catch (Exception ex)
  {
   throw ex;   
  }            
  return ds.Tables[0];
 }

 public static int UpdateProductDiscontinue(bool value, string product_name)
 {
  int result = 0;
  string query = String.Format("Update Products set discontinue = '{0}' where productname = '{1}' ;",value,product_name);

  try
  {
   using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["products"].ConnectionString.ToString()))
   {
    SqlCommand cmd = new SqlCommand(query, conn);
    conn.Open();
    result = cmd.ExecuteNonQuery();
    conn.Close();
   }
  }
  catch (Exception ex)
  {
   throw ex;
  }
  return result;
 }
}

Sample Output:
a. On form load (some of the checkbox are set to true, others are false)

b. Checkbox in column header is selected

c. Checkbox in column header is deselected

10 comments:

  1. Could u send me whole project..
    Because above code giving error may be required namespace are not referenced in my project..
    so please send that project in my mail ID

    AviSatna@gmail.com

    ReplyDelete
  2. Hi,

    The methods used in this tutorial doesn't use third party dlls. Just pure WPF namespace. It should work just fine.

    :)

    ReplyDelete
  3. Where is GetDatagridrows function....

    ReplyDelete
    Replies
    1. Hi!
      Here's the reference when I created the GetDataGridRows() function.

      http://techiethings.blogspot.com/2010/05/get-wpf-datagrid-row-and-cell.html

      Cheers!

      Delete
  4. hello can u send that project or post

    ReplyDelete
    Replies
    1. Hi,

      I already updated this post with the detailed source code.

      Cheers..

      Delete
  5. It helped me a lot. Many thanks!!! \0/

    ReplyDelete