Keep changed value in cell when sorting and before saving in DataGridView

Posted on 2014-12-01
Last Modified: 2014-12-02
I have a datagridview With bindingsource where I list many Products, and in the first cell the user can enter how many items they want of each Product in the quantity cell, and then they click the "Order" button.

The problem is when the user has set how many Items they want and then want to sort by name, the values in the quantity cell is gone.

How is the most easy way to keep the entered quantity values when they sort the Grid ?
Question by:thorv71
  • 2
LVL 33

Expert Comment

ID: 40473729
Sounds like you are not storing the value entered by the user back to the datasource.  What is the code you are using to trigger the editing of the cell and saving the value entered.


Author Comment

ID: 40473740
No code at that point, the Cell is just open for editing.

The saving happens when you hit the button "Send order". Then I loop all the rows and check if the row has got a Quantity, if it has, I save the Quantity and the productID of the row.
LVL 33

Accepted Solution

it_saige earned 500 total points
ID: 40474166
You need to save the cell back to the datasource in order to retain the value.  If you need to disconnect the data in the grid with that of the data in the database then use a SortableBindingList made up of a class of objects that represents each item in the grid and save the order value to that.  Perhaps something like:

Form1.vb -
Imports System.IO
Imports System.ComponentModel
Imports System.Reflection

Public Class Form1
	Private DataFile As New FileInfo("Products.xml")
	Private ReadOnly Products As New SortableBindingList(Of Product)()
	Private Binding As New BindingSource()

	Private Sub OnCellValidating(ByVal sender As Object, ByVal e As DataGridViewCellValidatingEventArgs) Handles DataGridView1.CellValidating
		DataGridView1.Rows(e.RowIndex).ErrorText = String.Empty
		If DataGridView1.Rows(e.RowIndex).IsNewRow Then Return

		If DataGridView1.Columns(e.ColumnIndex).Name = "colQuantity" Then
			Dim tempQuantity As Integer = -1
			If Not Integer.TryParse(e.FormattedValue, tempQuantity) Then
				e.Cancel = True
				DataGridView1.Rows(e.RowIndex).ErrorText = "Invalid quantity entered.  The value entered must be an integer."
			End If
		End If
	End Sub

	Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
		If DataFile.Exists Then
			For Each table As DataTable In ProductsDataSet.Tables
				For Each row As DataRow In table.Rows
					Products.Add(New Product(row("ID"), row("Name"), row("Cost")))

			Binding.DataSource = Products
			DataGridView1.DataSource = Binding
		End If
	End Sub

	Private Sub OnColumnHeaderMouseClick(ByVal sender As Object, ByVal e As DataGridViewCellMouseEventArgs) Handles DataGridView1.ColumnHeaderMouseClick
		Dim newColumn As DataGridViewColumn = DataGridView1.Columns(e.ColumnIndex)
		Dim oldColumn As DataGridViewColumn = DataGridView1.SortedColumn
		Dim direction As ListSortDirection

		' If oldColumn is null, then the DataGridView is not currently sorted. 
		If oldColumn IsNot Nothing Then

			' Sort the same column again, reversing the SortOrder. 
			If oldColumn Is newColumn AndAlso DataGridView1.SortOrder = _
			    SortOrder.Ascending Then
				direction = ListSortDirection.Descending

				' Sort a new column and remove the old SortGlyph.
				direction = ListSortDirection.Ascending
				oldColumn.HeaderCell.SortGlyphDirection = SortOrder.None
			End If
			direction = ListSortDirection.Ascending
		End If

		' Sort the selected column.
		DataGridView1.Sort(newColumn, direction)
		If direction = ListSortDirection.Ascending Then
			newColumn.HeaderCell.SortGlyphDirection = SortOrder.Ascending
			newColumn.HeaderCell.SortGlyphDirection = SortOrder.Descending
		End If
	End Sub

	Private Sub OnDataBindingComplete(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewBindingCompleteEventArgs) Handles DataGridView1.DataBindingComplete
		' Put each of the columns into programmatic sort mode. 
		For Each column As DataGridViewColumn In DataGridView1.Columns
			column.SortMode = DataGridViewColumnSortMode.Programmatic
	End Sub
End Class

Public Class Product
	Private ReadOnly _ID As Integer
	Private ReadOnly _Name As String
	Private ReadOnly _Cost As Decimal

	Public ReadOnly Property ID() As Integer
			Return _ID
		End Get
	End Property

	Public ReadOnly Property Name() As String
			Return _Name
		End Get
	End Property

	Public ReadOnly Property Cost() As Decimal
			Return _Cost
		End Get
	End Property

	Public Property Quantity() As Integer

	Public ReadOnly Property Total() As Decimal
			Return (_Cost * Quantity)
		End Get
	End Property

	Public Sub New(ByVal ID As Integer, ByVal Name As String, ByVal Cost As Decimal)
		_ID = ID
		_Name = Name
		_Cost = Cost
	End Sub
End Class

Public Class SortableBindingList(Of T)
	Inherits BindingList(Of T)
	Private Property IsSorted As Boolean
	Private Property SortDirection As ListSortDirection
	Private Property SortProperty As PropertyDescriptor

	Protected Overrides ReadOnly Property SupportsSortingCore() As Boolean
			Return True
		End Get
	End Property

	Protected Overrides ReadOnly Property SortDirectionCore() As ListSortDirection
			Return _SortDirection
		End Get
	End Property

	Protected Overrides ReadOnly Property SortPropertyCore() As PropertyDescriptor
			Return _SortProperty
		End Get
	End Property

	Protected Overrides Sub ApplySortCore(ByVal [property] As PropertyDescriptor, ByVal direction As ListSortDirection)
		Dim items As List(Of T) = TryCast(Me.Items, List(Of T))
		If items Is Nothing Then
			IsSorted = False
			Dim comparer As New PropertyCompare(Of T)([property].Name, direction)
			IsSorted = True
			SortDirection = direction
			SortProperty = [property]
		End If
		OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
	End Sub

	Protected Overrides ReadOnly Property IsSortedCore() As Boolean
			Return _IsSorted
		End Get
	End Property

	Protected Overrides Sub RemoveSortCore()
		_IsSorted = False
	End Sub

	Sub New(ByVal list As ICollection(Of T))
		MyBase.New(CType(list, IList(Of T)))
	End Sub

	Sub New()
	End Sub

	Private Class PropertyCompare(Of T)
		Implements IComparer(Of T)

		Private Property PropertyInfo() As PropertyInfo
		Private Property SortDirection() As ListSortDirection
		Friend Sub New(ByVal [property] As String, ByVal direction As ListSortDirection)
			PropertyInfo = GetType(T).GetProperty([property])
			SortDirection = direction
		End Sub

		Friend Function Compare(ByVal x As T, ByVal y As T) As Integer Implements IComparer(Of T).Compare
			Return If(SortDirection = ListSortDirection.Ascending,
			  Comparer.[Default].Compare(PropertyInfo.GetValue(x, Nothing), PropertyInfo.GetValue(y, Nothing)),
			  Comparer.[Default].Compare(PropertyInfo.GetValue(y, Nothing), PropertyInfo.GetValue(x, Nothing)))
		End Function
	End Class
End Class

Open in new window

Form1.Designer.vb -
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
    Inherits System.Windows.Forms.Form

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing AndAlso components IsNot Nothing Then
            End If
        End Try
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
		Me.Button1 = New System.Windows.Forms.Button()
		Me.DataGridView1 = New System.Windows.Forms.DataGridView()
		Me.colID = New System.Windows.Forms.DataGridViewTextBoxColumn()
		Me.colName = New System.Windows.Forms.DataGridViewTextBoxColumn()
		Me.colUnitCost = New System.Windows.Forms.DataGridViewTextBoxColumn()
		Me.colQuantity = New System.Windows.Forms.DataGridViewTextBoxColumn()
		Me.colTotal = New System.Windows.Forms.DataGridViewTextBoxColumn()
		Me.ProductsDataSet = New System.Data.DataSet()
		CType(Me.DataGridView1, System.ComponentModel.ISupportInitialize).BeginInit()
		CType(Me.ProductsDataSet, System.ComponentModel.ISupportInitialize).BeginInit()
		Me.Button1.Location = New System.Drawing.Point(447, 227)
		Me.Button1.Name = "Button1"
		Me.Button1.Size = New System.Drawing.Size(75, 23)
		Me.Button1.TabIndex = 0
		Me.Button1.Text = "Button1"
		Me.Button1.UseVisualStyleBackColor = True
		Me.DataGridView1.AllowUserToAddRows = False
		Me.DataGridView1.AllowUserToDeleteRows = False
		Me.DataGridView1.AllowUserToOrderColumns = True
		Me.DataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
		Me.DataGridView1.Columns.AddRange(New System.Windows.Forms.DataGridViewColumn() {Me.colID, Me.colName, Me.colUnitCost, Me.colQuantity, Me.colTotal})
		Me.DataGridView1.Location = New System.Drawing.Point(13, 13)
		Me.DataGridView1.Name = "DataGridView1"
		Me.DataGridView1.Size = New System.Drawing.Size(509, 208)
		Me.DataGridView1.TabIndex = 1
		Me.colID.DataPropertyName = "ID"
		Me.colID.HeaderText = "ID"
		Me.colID.Name = "colID"
		Me.colID.Visible = False
		Me.colName.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill
		Me.colName.DataPropertyName = "Name"
		Me.colName.HeaderText = "Product Name"
		Me.colName.Name = "colName"
		Me.colUnitCost.DataPropertyName = "Cost"
		Me.colUnitCost.HeaderText = "Unit Cost"
		Me.colUnitCost.Name = "colUnitCost"
		Me.colQuantity.DataPropertyName = "Quantity"
		Me.colQuantity.HeaderText = "Quantity"
		Me.colQuantity.Name = "colQuantity"
		Me.colTotal.DataPropertyName = "Total"
		Me.colTotal.HeaderText = "Total"
		Me.colTotal.Name = "colTotal"
		Me.ProductsDataSet.DataSetName = "NewDataSet"
		Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
		Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
		Me.ClientSize = New System.Drawing.Size(534, 262)
		Me.Name = "Form1"
		Me.Text = "Form1"
		CType(Me.DataGridView1, System.ComponentModel.ISupportInitialize).EndInit()
		CType(Me.ProductsDataSet, System.ComponentModel.ISupportInitialize).EndInit()

	End Sub
	Friend WithEvents Button1 As System.Windows.Forms.Button
	Friend WithEvents DataGridView1 As System.Windows.Forms.DataGridView
	Friend WithEvents colID As System.Windows.Forms.DataGridViewTextBoxColumn
	Friend WithEvents colName As System.Windows.Forms.DataGridViewTextBoxColumn
	Friend WithEvents colUnitCost As System.Windows.Forms.DataGridViewTextBoxColumn
	Friend WithEvents colQuantity As System.Windows.Forms.DataGridViewTextBoxColumn
	Friend WithEvents colTotal As System.Windows.Forms.DataGridViewTextBoxColumn
	Friend WithEvents ProductsDataSet As System.Data.DataSet

End Class

Open in new window

Products.xml -
<?xml version="1.0" encoding="utf-8" ?>
		<Name>Cough Syrup</Name>
		<Name>Bar Soap</Name>
		<Name>Liquid Soap</Name>

Open in new window

Set Products.xml to always copy to Output Directory -Capture.JPG
This is just a quick example, but when the user enters a quantity, the total is calculated and the quantity is saved to the object in the list that the product represents.  I didn't worry to much about saving it back to an XML file or some other static medium because I believe you can take it from here.

Here are some sample output snippets:On first run.  Data from xml is entered into the gridview.Data entered into grid and totals calculated.Sorted on unit cost.Now sorted on Quantity.

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Generate Unique ID in VB.NET 21 99
how to just get time from a date 6 46
TFS 2015 Access denied. 1 29
C# XML Get Values 4 33
This article will show, step by step, how to integrate R code into a R Sweave document
This article shows how to deploy dynamic backgrounds to computers depending on the aspect ratio of display
This video teaches viewers about errors in exception handling.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

680 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question