Solved

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

Posted on 2014-12-01
3
332 Views
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 ?
0
Comment
Question by:thorv71
  • 2
3 Comments
 
LVL 32

Expert Comment

by:it_saige
Comment Utility
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.

-saige-
0
 

Author Comment

by:thorv71
Comment Utility
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.
0
 
LVL 32

Accepted Solution

by:
it_saige earned 500 total points
Comment Utility
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
			ProductsDataSet.ReadXml(DataFile.FullName)
			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")))
				Next
			Next

			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
			Else

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

		' Sort the selected column.
		DataGridView1.Sort(newColumn, direction)
		If direction = ListSortDirection.Ascending Then
			newColumn.HeaderCell.SortGlyphDirection = SortOrder.Ascending
		Else
			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
		Next
	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
		Get
			Return _ID
		End Get
	End Property

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

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

	Public Property Quantity() As Integer

	Public ReadOnly Property Total() As Decimal
		Get
			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
		Get
			Return True
		End Get
	End Property

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

	Protected Overrides ReadOnly Property SortPropertyCore() As PropertyDescriptor
		Get
			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
		Else
			Dim comparer As New PropertyCompare(Of T)([property].Name, direction)
			items.Sort(comparer)
			IsSorted = True
			SortDirection = direction
			SortProperty = [property]
		End If
		OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
	End Sub

	Protected Overrides ReadOnly Property IsSortedCore() As Boolean
		Get
			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()
		MyBase.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)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        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.SuspendLayout()
		'
		'Button1
		'
		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
		'
		'DataGridView1
		'
		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
		'
		'colID
		'
		Me.colID.DataPropertyName = "ID"
		Me.colID.HeaderText = "ID"
		Me.colID.Name = "colID"
		Me.colID.Visible = False
		'
		'colName
		'
		Me.colName.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill
		Me.colName.DataPropertyName = "Name"
		Me.colName.HeaderText = "Product Name"
		Me.colName.Name = "colName"
		'
		'colUnitCost
		'
		Me.colUnitCost.DataPropertyName = "Cost"
		Me.colUnitCost.HeaderText = "Unit Cost"
		Me.colUnitCost.Name = "colUnitCost"
		'
		'colQuantity
		'
		Me.colQuantity.DataPropertyName = "Quantity"
		Me.colQuantity.HeaderText = "Quantity"
		Me.colQuantity.Name = "colQuantity"
		'
		'colTotal
		'
		Me.colTotal.DataPropertyName = "Total"
		Me.colTotal.HeaderText = "Total"
		Me.colTotal.Name = "colTotal"
		'
		'ProductsDataSet
		'
		Me.ProductsDataSet.DataSetName = "NewDataSet"
		'
		'Form1
		'
		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.Controls.Add(Me.DataGridView1)
		Me.Controls.Add(Me.Button1)
		Me.Name = "Form1"
		Me.Text = "Form1"
		CType(Me.DataGridView1, System.ComponentModel.ISupportInitialize).EndInit()
		CType(Me.ProductsDataSet, System.ComponentModel.ISupportInitialize).EndInit()
		Me.ResumeLayout(False)

	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" ?>
<Products>
	<Product>
		<ID>1</ID>
		<Name>Toothpaste</Name>
		<Cost>2.99</Cost>
	</Product>
	<Product>
		<ID>2</ID>
		<Name>Cough Syrup</Name>
		<Cost>5.49</Cost>
	</Product>
	<Product>
		<ID>3</ID>
		<Name>Deodorant</Name>
		<Cost>3.29</Cost>
	</Product>
	<Product>
		<ID>4</ID>
		<Name>Bar Soap</Name>
		<Cost>1.19</Cost>
	</Product>
	<Product>
		<ID>5</ID>
		<Name>Liquid Soap</Name>
		<Cost>3.49</Cost>
	</Product>
</Products>

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.
-saige-
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

More often than not, we developers are confronted with a need: a need to make some kind of magic happen via code. Whether it is for a client, for the boss, or for our own personal projects, the need must be satisfied. Most of the time, the Framework…
This article is meant to give a basic understanding of how to use R Sweave as a way to merge LaTeX and R code seamlessly into one presentable document.
This video teaches viewers about errors in exception handling.
This tutorial will introduce the viewer to VisualVM for the Java platform application. This video explains an example program and covers the Overview, Monitor, and Heap Dump tabs.

744 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

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now