How To Apply Checkbox Check All To WPF DataGrid Using MVVM Pattern In VB.NET
Good day Gents!
DBUtil.vb
ViewModelBase.vb
RelayCommand.vb
MultiParamConverter.vb
Products.vb
ProductsViewModel.vb
MainWindow.xaml
Output
Cheers!
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.
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>
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
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
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
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
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
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
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>
Source Code: WPF DataGrid MVVM Checkbox CheckAll VB.NET
Cheers!
Comments
Post a Comment