Donate

How To Apply Checkbox Check All To WPF DataGrid Using MVVM Pattern In VB.NET

Good day Gents!

In this post, I will demonstrate the VB.NET version on how to create a WPF DataGrid application that has a check all checkbox behavior for selecting all rows using the MVVM Pattern. First is we need to create a .NET WPF Visual Basic project that targets the .NET framework with a project structure similar to the screenshot below.
How To Apply Checkbox Check All To WPF DataGrid Using MVVM Pattern In VB.NET
App.config
Update your App.config's connection string with your local database connection.
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
    </startup>
    <connectionStrings>
      <add name="products"
           connectionString="data source=.;Initial Catalog=TestDatabase;Trusted_Connection=True;"
           providerName="System.Data.SqlClient" />
    </connectionStrings>
</configuration>
DBUtil.vb
The DBUtil class will retrieve records from the database and update the discontinued field of a particular product.
Imports System.Configuration
Imports System.Data
Imports System.Data.SqlClient

Module DBUtil

    Function GetProduct() As DataTable
        Dim ds As DataSet = New DataSet()
        Dim query As String = "Select * from Products;"
        Try
            Using conn As SqlConnection = New SqlConnection(ConfigurationManager.ConnectionStrings("products").ConnectionString.ToString())
                Dim cmd As SqlCommand = New SqlCommand(query, conn)
                conn.Open()
                Dim da As SqlDataAdapter = New SqlDataAdapter(cmd)
                da.Fill(ds)
                conn.Close()
            End Using
        Catch ex As Exception
            Throw ex
        End Try

        Return ds.Tables(0)
    End Function

    Function UpdateProductDiscontinue(ByVal value As Boolean, ByVal productID As Integer) As Integer
        Dim result As Integer = 0
        Dim query As String = String.Format("Update Products set discontinue = '{0}' where productID = '{1}' ;", value, productID)
        Try
            Using conn As SqlConnection = New SqlConnection(ConfigurationManager.ConnectionStrings("products").ConnectionString.ToString())
                Dim cmd As SqlCommand = New SqlCommand(query, conn)
                conn.Open()
                result = cmd.ExecuteNonQuery()
                conn.Close()
            End Using
        Catch ex As Exception
            Throw ex
        End Try

        Return result
    End Function
End Module
ViewModelBase.vb
The ViewModelBase class implements the INotifyPropertyChanged interface. This is useful for any property changes used in databinding.
Public Class ViewModelBase : Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler _
         Implements INotifyPropertyChanged.PropertyChanged

    Protected Sub OnPropertyChanged(ByVal propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub
End Class
RelayCommand.vb
The RelayCommand class that implements the ICommand interface. Commands are used for handling events in WPF with regards to the MVVM Architectural Pattern. The sole purpose of a command is to relay or distribute its functionality to other objects by invoking delegates. The default return value for a CanExecute method is true.
Public Class RelayCommand : Implements ICommand

    Private ReadOnly _execute As Action(Of Object)
    Private ReadOnly _canExecute As Predicate(Of Object)

    Public Sub New(ByVal execute As Action(Of Object))
    End Sub

    Public Sub New(ByVal execute As Action(Of Object), ByVal canExecute As Predicate(Of Object))
        If execute Is Nothing Then Throw New ArgumentNullException("execute")
        _execute = execute
        _canExecute = canExecute
    End Sub

    Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
        Return (_canExecute Is Nothing) OrElse _canExecute(parameter)
    End Function

    Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

        AddHandler(ByVal value As EventHandler)
            AddHandler CommandManager.RequerySuggested, value
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            AddHandler CommandManager.RequerySuggested, value
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            CommandManager.InvalidateRequerySuggested()
        End RaiseEvent
    End Event

    Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
        _execute(parameter)
    End Sub
    
End Class
MultiParamConverter.vb
This class will clone the values of all items in the DataGrid. This is useful when the DataGrid's header check all checkbox is checked, the parameters will then be passed to the UpdateAllProducts() in the ViewModel and process each record to verify if the product item will be discontinued.
Public Class MultiParamConverter :Implements IMultiValueConverter

    Public Function Convert(ByVal values As Object(), ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IMultiValueConverter.Convert
        Return values.Clone()
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetTypes As Type(), ByVal parameter As Object, ByVal culture As CultureInfo) As Object() Implements IMultiValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function
   
End Class
Products.vb
This class inherits the ViewModelBase class and contains the properties of a product that is bound to the DataGrid. Any changes made by the user will reflect in any of these properties since they implement the OnPropertyChanged() method.
Public Class Products
    Inherits ViewModelBase

    Private _id As Integer

    Public Property Id As Integer
        Get
            Return _id
        End Get

        Set(ByVal value As Integer)
            _id = value
            OnPropertyChanged("Id")
        End Set
    End Property

    Private _name As String

    Public Property ProductName As String
        Get
            Return _name
        End Get

        Set(ByVal value As String)
            _name = value
            OnPropertyChanged("ProductName")
        End Set
    End Property

    Private _price As Decimal

    Public Property UnitPrice As Decimal
        Get
            Return _price
        End Get

        Set(ByVal value As Decimal)
            _price = value
            OnPropertyChanged("UnitPrice")
        End Set
    End Property

    Private _quantity As String

    Public Property QuantityPerUnit As String
        Get
            Return _quantity
        End Get

        Set(ByVal value As String)
            _quantity = value
            OnPropertyChanged("QuantityPerUnit")
        End Set
    End Property

    Private _discontinue As Boolean

    Public Property Discontinue As Boolean
        Get
            Return _discontinue
        End Get

        Set(ByVal value As Boolean)
            _discontinue = value
            OnPropertyChanged("Discontinue")
        End Set
    End Property
End Class
ProductsViewModel.vb
The View Model class is the main piece of the project. This is were all the processing is executed. From firing of check event handler, populating the datagrid from the database, updating a single product discontinued property and modifying all product item's discontinued property. This is the DataContext object for the MainWindow class.
Public Class ProductsViewModel
    Inherits ViewModelBase

    #Region "Commands"

    Private _updateItemCommand As ICommand
    Public ReadOnly Property UpdateItemCommand As ICommand
        Get
            If _updateItemCommand Is Nothing Then
                _updateItemCommand = New RelayCommand(Sub(param) UpdateItem(param), Nothing)
            End If

            Return _updateItemCommand
        End Get
    End Property

    Private _updateAllCommand As ICommand
    Public ReadOnly Property UpdateAllCommand As ICommand
        Get
            If _updateAllCommand Is Nothing Then
                _updateAllCommand = New RelayCommand(Sub(param) UpdateAllProducts(param), Nothing)
            End If

            Return _updateAllCommand
        End Get
    End Property

    #End Region

    #Region "Members"
    Public Property MultiParamConverter As MultiParamConverter

    Private _productList As ObservableCollection(Of Products)
    Public Property ProductList As ObservableCollection(Of Products)
        Get
            Return _productList
        End Get

        Set(ByVal value As ObservableCollection(Of Products))
            _productList = value
            OnPropertyChanged("ProductList")
        End Set
    End Property

    Private _checkAll As Boolean
    Public Property CheckAll As Boolean
        Get
            Return _checkAll
        End Get

        Set(ByVal value As Boolean)
            _checkAll = value
            OnPropertyChanged("CheckAll")
        End Set
    End Property

    #End Region
    
    #Region "ViewModel Methods"
    
    Public Sub New()
        MultiParamConverter = New MultiParamConverter()
        GetAllProducts()
    End Sub

    Private Sub UpdateItem(ByVal param As Object)
        Dim product = CType(param, Products)
        DBUtil.UpdateProductDiscontinue(product.Discontinue, product.Id)
        GetAllProducts()
    End Sub

    Private Sub UpdateAllProducts(ByVal param As Object)
        Dim values = CType(param, Object())
        Dim check = CBool(values(0))
        Dim productsItemCollection = CType(values(1), ItemCollection)
        If productsItemCollection.Count > 0 Then
            For Each item In productsItemCollection
                If item IsNot Nothing Then
                    DBUtil.UpdateProductDiscontinue(check, (CType(item, Products)).Id)
                End If
            Next
        End If

        GetAllProducts()
    End Sub

    Private Sub GetAllProducts()
        Dim flag As Boolean = True
        ProductList = New ObservableCollection(Of Products)(Products(DBUtil.GetProduct()))
        For Each product In ProductList
            If Not product.Discontinue Then
                flag = False
                CheckAll = False
            End If
        Next

        If flag Then
            CheckAll = True
        End If
    End Sub

    Private Function Products(ByVal dt As DataTable) As ObservableCollection(Of Products)

        Dim convertedList =(From rw In dt.AsEnumerable()
                Select New Products() _
                With {
                .Id = Convert.ToInt32(rw("productID")), 
                .ProductName = rw("ProductName").ToString(), 
                .UnitPrice = Convert.ToDecimal(rw("UnitPrice")), 
                .QuantityPerUnit =(rw("QuantityPerUnit")).ToString(), 
                .Discontinue = CBool(rw("Discontinue"))}).ToList()
        Return New ObservableCollection(Of Products)(convertedList)

    End Function
    #End Region
    
End Class
MainWindow.xaml
The UI has two window resources specifically the ProductsViewModel classwhich is the window's datacontext object and the MultiParamConverter class which is the header checkbox's MultiBinding converter. The UI's main component is a DataGrid control that shows the products and has an discontintued checkbox for each record. The DataGrid's header has a check all checkbox that will set all of the product's discontinued property either true or false when checked.
<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MVVMCheckboxAlIDataGridVB"
        mc:Ignorable="d" WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="550" Width="600">
    <Window.Resources>
        <local:ProductsViewModel x:Key="ProductsViewModel" />
        <local:MultiParamConverter x:Key="MultiParamConverter"/>
    </Window.Resources>
    <Grid Width="580">
        <Grid.RowDefinitions>
            <RowDefinition Height="20" />
            <RowDefinition Height="100" />
            <RowDefinition Height="50" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="260" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <StackPanel Grid.Row="1" Grid.RowSpan="3" Grid.ColumnSpan="2" DataContext="{Binding Source={StaticResource ProductsViewModel}}">
            <DataGrid Name="DgProducts" AutoGenerateColumns="False" CanUserAddRows="False"
                      ItemsSource="{Binding ProductList}">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Name" Binding="{Binding Path=Id}" Visibility="Hidden" Width="215"/>
                    <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.HeaderStyle>
                            <Style TargetType="{x:Type DataGridColumnHeader}">
                                <Setter Property="HorizontalContentAlignment" Value="Center"/>
                            </Style>
                        </DataGridTemplateColumn.HeaderStyle>
                        <DataGridTemplateColumn.Header>
                            <CheckBox Name="chkCheckAll" HorizontalAlignment="Center" 
                                      IsChecked="{Binding Path=DataContext.CheckAll, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource FindAncestor,
                                            AncestorType={x:Type StackPanel}}}" 
                                      Command="{Binding DataContext.UpdateAllCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}}">
                                <CheckBox.CommandParameter>
                                    <MultiBinding Converter="{StaticResource MultiParamConverter}">
                                        <Binding ElementName="chkCheckAll" Path="IsChecked"/>
                                        <Binding ElementName="DgProducts" Path="Items"/>
                                    </MultiBinding>
                                </CheckBox.CommandParameter>
                            </CheckBox>
                        </DataGridTemplateColumn.Header>
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox Name="chkDiscontinue" IsChecked="{Binding Discontinue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
                                    Command="{Binding DataContext.UpdateItemCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}}" 
                                    CommandParameter="{Binding}" Margin="45 2 0 0" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>
        </StackPanel>
    </Grid>
</Window>
Output
How To Apply Checkbox Check All To WPF DataGrid Using MVVM Pattern In VB.NET
How To Apply Checkbox Check All To WPF DataGrid Using MVVM Pattern In VB.NET


Cheers!

Comments

Donate

Popular Posts From This Blog

WPF CRUD Application Using DataGrid, MVVM Pattern, Entity Framework, And C#.NET

TypeScript Error Or Bug: The term 'tsc' is not recognized as the name of a cmdlet, function, script file, or operable program.

Invalid nested tag div found, expected closing tag input