Solved

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

Posted on 2014-12-01
3
348 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 33

Expert Comment

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

-saige-
0
 

Author Comment

by:thorv71
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.
0
 
LVL 33

Accepted Solution

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

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering 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

This article will show, step by step, how to integrate R code into a R Sweave document
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
This theoretical tutorial explains exceptions, reasons for exceptions, different categories of exception and exception hierarchy.
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…

840 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