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