Thursday, January 26, 2012

VB.NET - GetChanges (and original values) from DataSet

If you use a DataSet on a DataGridView for table maintenance and edits have been made, you may want to be able to see what the original values were compared to the newly entered ones.  You may want to do this to keep record of what values were before the user saves the changes to the database.

My example does one row at a time.  This is good if you do row validation with the RowValidating event.  Assume my code fires off in the RowValidating event. (m_ds is my DataSet that is attached to my grid)

*This is inside of the Row Validating event {

If m_ds.HasChanges then

         GetUserChanges()

End if


}





    ''' <summary>
    '''This code will go through and get all original values from the row and/or the new ones depending on what time of RowState the
    ''' row is in.  These values are retrieved before changes are committed to the dataset with the m_ds.update statement
    ''' </summary>
    ''' <remarks>jefz</remarks>
    Private Sub GetUserChanges()


        Dim dtChanges As New DataTable
        Dim dtRow As DataRow

        Dim strOriginalValue As String = ""
        Dim strNewValue As String = ""
        Dim strType As String = ""

        Try

            'check to see what kind of changes were made to the DataSet: Modified, Added, or Deleted
            If m_ds.HasChanges(DataRowState.Modified) Then
                dtChanges = m_ds.Tables.Item(0).GetChanges(DataRowState.Modified)
                strType = "M"
            ElseIf m_ds.HasChanges(DataRowState.Added) Then
                dtChanges = m_ds.Tables.Item(0).GetChanges(DataRowState.Added)
                strType = "A"
            ElseIf m_ds.HasChanges(DataRowState.Deleted) Then
                dtChanges = m_ds.Tables.Item(0).GetChanges(DataRowState.Deleted)
                strType = "D"
            Else
                Throw New Exception("No changes were found ... Should not be trying to save")
            End If

            dtRow = dtChanges.Rows(0)


            Select Case strType
                'if the row was modified then get the original value as well as the new value from the dataset
                Case "M"
                    For Each Column As DataColumn In dtChanges.Columns
                        strOriginalValue = dtRow(Column, DataRowVersion.Original).ToString
                        strNewValue = dtRow(Column, DataRowVersion.Current).ToString
                    Next
                    'if the row was added then you can only get the new values
                Case "A"
                    For Each Column As DataColumn In dtChanges.Columns
                        strNewValue = dtRow(Column, DataRowVersion.Current).ToString
                    Next
                    'if is going to be deleted then you can only get the old rows
                Case "D"
                    For Each Column As DataColumn In dtChanges.Columns
                        strOriginalValue = dtRow(Column, DataRowVersion.Original).ToString
                    Next
                Case Else
                    Throw New Exception("No changes were found ... Should not be trying to save")
            End Select


        Catch ex As Exception
            Throw ex
        End Try

    End Sub






You can do whatever you want with the values within the select case or if you want to store these values for later use, instead of storing their values into a string variable, you can add them to an array or collection.

Wednesday, January 25, 2012

VB.NET - Calendar Control in DataGridView

DataGridView does not support a calendar control (date picker) by default. In order to use a calendar control you must first add a class to your project.  I named mine "clsCalendar".  Then paste this code into the class:


Imports System
Imports System.Windows.Forms


Public Class clsCalendar




    Public Class CalendarColumn
        Inherits DataGridViewColumn


        Public Sub New()
            MyBase.New(New CalendarCell())
        End Sub


        Public Overrides Property CellTemplate() As DataGridViewCell
            Get
                Return MyBase.CellTemplate
            End Get
            Set(ByVal value As DataGridViewCell)


                ' Ensure that the cell used for the template is a CalendarCell.
                If (value IsNot Nothing) AndAlso _
                    Not value.GetType().IsAssignableFrom(GetType(CalendarCell)) _
                    Then
                    Throw New InvalidCastException("Must be a CalendarCell")
                End If
                MyBase.CellTemplate = value


            End Set
        End Property


    End Class


    Public Class CalendarCell
        Inherits DataGridViewTextBoxCell


        Public Sub New()
            ' Use the short date format.
            Me.Style.Format = "d"
        End Sub


        Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, _
            ByVal initialFormattedValue As Object, _
            ByVal dataGridViewCellStyle As DataGridViewCellStyle)


            ' Set the value of the editing control to the current cell value.
            MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, _
                dataGridViewCellStyle)


            Dim ctl As CalendarEditingControl = _
                CType(DataGridView.EditingControl, CalendarEditingControl)


            ' Use the default row value when Value property is null.
            ' extra condition added to make sure that if there is nothing entered it goes to the correct condition
            If (Me.Value Is Nothing) Or (IsDBNull(Me.Value)) Then
                ctl.Value = CType(Me.DefaultNewRowValue, DateTime)
            Else
                ctl.Value = CType(Me.Value, DateTime)
            End If
        End Sub


        Public Overrides ReadOnly Property EditType() As Type
            Get
                ' Return the type of the editing control that CalendarCell uses.
                Return GetType(CalendarEditingControl)
            End Get
        End Property


        Public Overrides ReadOnly Property ValueType() As Type
            Get
                ' Return the type of the value that CalendarCell contains.
                Return GetType(DateTime)
            End Get
        End Property


        Public Overrides ReadOnly Property DefaultNewRowValue() As Object
            Get
                ' Use the current date and time as the default value.
                Return DateTime.Now
            End Get
        End Property


    End Class


    Class CalendarEditingControl
        Inherits DateTimePicker
        Implements IDataGridViewEditingControl


        Private dataGridViewControl As DataGridView
        Private valueIsChanged As Boolean = False
        Private rowIndexNum As Integer


        Public Sub New()
            Me.Format = DateTimePickerFormat.Short
        End Sub


        Public Property EditingControlFormattedValue() As Object _
            Implements IDataGridViewEditingControl.EditingControlFormattedValue


            Get
                Return Me.Value.ToShortDateString()
            End Get


            Set(ByVal value As Object)
                Try
                    ' This will throw an exception of the string is 
                    ' null, empty, or not in the format of a date.
                    Me.Value = DateTime.Parse(CStr(value))
                Catch
                    ' In the case of an exception, just use the default
                    ' value so we're not left with a null value.
                    Me.Value = DateTime.Now
                End Try
            End Set


        End Property


        Public Function GetEditingControlFormattedValue(ByVal context _
            As DataGridViewDataErrorContexts) As Object _
            Implements IDataGridViewEditingControl.GetEditingControlFormattedValue


            Return Me.Value.ToShortDateString()


        End Function


        Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As  _
            DataGridViewCellStyle) _
            Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl


            Me.Font = dataGridViewCellStyle.Font
            Me.CalendarForeColor = dataGridViewCellStyle.ForeColor
            Me.CalendarMonthBackground = dataGridViewCellStyle.BackColor


        End Sub


        Public Property EditingControlRowIndex() As Integer _
            Implements IDataGridViewEditingControl.EditingControlRowIndex


            Get
                Return rowIndexNum
            End Get
            Set(ByVal value As Integer)
                rowIndexNum = value
            End Set


        End Property


        Public Function EditingControlWantsInputKey(ByVal key As Keys, _
            ByVal dataGridViewWantsInputKey As Boolean) As Boolean _
            Implements IDataGridViewEditingControl.EditingControlWantsInputKey


            ' Let the DateTimePicker handle the keys listed.
            Select Case key And Keys.KeyCode
                Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, _
                    Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp


                    Return True


                Case Else
                    Return Not dataGridViewWantsInputKey
            End Select


        End Function


        Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) _
            Implements IDataGridViewEditingControl.PrepareEditingControlForEdit


            ' No preparation needs to be done.


        End Sub


        Public ReadOnly Property RepositionEditingControlOnValueChange() _
            As Boolean Implements _
            IDataGridViewEditingControl.RepositionEditingControlOnValueChange


            Get
                Return False
            End Get


        End Property


        Public Property EditingControlDataGridView() As DataGridView _
            Implements IDataGridViewEditingControl.EditingControlDataGridView


            Get
                Return dataGridViewControl
            End Get
            Set(ByVal value As DataGridView)
                dataGridViewControl = value
            End Set


        End Property


        Public Property EditingControlValueChanged() As Boolean _
            Implements IDataGridViewEditingControl.EditingControlValueChanged


            Get
                Return valueIsChanged
            End Get
            Set(ByVal value As Boolean)
                valueIsChanged = value
            End Set


        End Property


        Public ReadOnly Property EditingControlCursor() As Cursor _
            Implements IDataGridViewEditingControl.EditingPanelCursor


            Get
                Return MyBase.Cursor
            End Get


        End Property


        Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)


            ' Notify the DataGridView that the contents of the cell have changed.
            valueIsChanged = True
            Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
            MyBase.OnValueChanged(eventargs)


        End Sub


    End Class
End Class


I use my Calendar control on the Cell Enter event for the DataGridView.  Within the Cell Enter Event I have this code calling my sub:


Dim calCellTemp As New clsCalendar.CalendarCell


CheckAndSetCalendarCells(e.ColumnIndex, e.RowIndex, calCellTemp)


I pass in the cell's current column index, row index, and as well as a "CalendarCell" which is a new type created in the class that was just added.

The code for my 'CheckAndSetCalendarCells' Sub is:


Private Sub CheckAndSetCalendarCells(ByVal aintColIndex As Integer, ByVal aintRowIndex As Integer, ByVal acalCellTemp As clsCalendar.CalendarCell)





If DataGridView1.Columns(aintColIndex).Name = "Date" Then
                         DataGridView1 (aintColIndex, aintRowIndex) = acalCellTemp
                    End If


End Sub


I check to make sure the cell I just entered is in the correct column and if it is then I set the cell equal to a CalendarCell






*source code came from msdn.microsoft.com - Host controls in Windows Forms DataGridView Cells