Data Grid Customization
I need to customize the datagrid so that it works much like Excel. When a user moves left and right they need to jump from cell to cell. If they type text it should simply overwrite the data in that cell or they could press F2 to edit the cell. I also need to be able to handle mouse clicks to change the current cell.
I have derived a class from datagrid to handle the mouse clicks. That works just fine. I have also derived a class from DataGridTextBoxColumn and I am overriding the Edit method to only display the textbox if the user is "editing" a cell. Unfortunately the way I am doing this does not seem to work... The whole thing works just fine with the mouse, however, if you try to cursor to the left most column and type no text appears, however, if you type on any other cells they seem to work (as long as you type more than one character!) Why can't I edit the cells on the left without clicking on them? And why when I enter just one letter on all the other cells does it lose my changes?
I've provided some code below. Please let me know what I am doing wrong!
Public Class MyDataGrid
Inherits DataGrid
Public blnEdit As Boolean = False
Private Sub MyDataGrid_CurrentCellChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.CurrentCellChanged
blnEdit = False
End Sub
Private Sub MyDataGrid_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
' Use the HitTest method to get a HitTestInfo object.
Dim hi As DataGrid.HitTestInfo
Dim grid As DataGrid = CType(sender, DataGrid)
hi = grid.HitTest(e.X, e.Y)
' Test if the clicked area was a cell.
If hi.Type = DataGrid.HitTestType.Cell Then
blnEdit = True
End If
End Sub
Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, ByVal keyData As System.Windows.Forms.Keys) As Boolean
If msg.WParam.ToInt32() = CInt(Keys.Enter) Then
SendKeys.Send("{Tab}")
Return True
End If
End Function 'ProcessCmdKey
Private Sub MyDataGrid_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
blnEdit = True
End Sub
End Class
Public Class MyDataGridTextBoxColumn
Inherits DataGridTextBoxColumn
Protected Overloads Overrides Sub Edit(ByVal source As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer, ByVal bounds As System.Drawing.Rectangle, ByVal [readOnly] As Boolean, ByVal instantText As String, ByVal cellIsVisible As Boolean)
If Not CType(Me.DataGridTableStyle.DataGrid, MyDataGrid).blnEdit Then
Me.HideEditBox()
Else
MyBase.Edit(source, rowNum, bounds, [readOnly], instantText, cellIsVisible)
End If
End Sub
End Class
Thanks for the quick response Ken. I just did exactly the same thing... created a new project and pasted the code into it... I then realized that I had failed to mention that I had added CustomDataTableStyles with my MyDataGridTextBoxColumn class. So here's ALL of my code thus far. You can simply create a new project and paste all this code into Form1.vb. Oddly enough, everything seems to work now except that you can't type text into the leftmost column without double clicking it or pressing "F2".
Public Class Form1
Inherits System.Windows.Forms.Form
Private MyDataSet As DataSet
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
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.
Friend WithEvents DataGrid1 As MyDataGrid
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.DataGrid1 = New MyDataGrid
CType(Me.DataGrid1, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SuspendLayout()
'
'DataGrid1
'
Me.DataGrid1.DataMember = ""
Me.DataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText
Me.DataGrid1.Location = New System.Drawing.Point(8, 8)
Me.DataGrid1.Name = "DataGrid1"
Me.DataGrid1.Size = New System.Drawing.Size(520, 440)
Me.DataGrid1.TabIndex = 0
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(536, 454)
Me.Controls.Add(Me.DataGrid1)
Me.Name = "Form1"
Me.Text = "Form1"
CType(Me.DataGrid1, System.ComponentModel.ISupportInitialize).EndInit()
Me.ResumeLayout(False)
End Sub
#End Region
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Create Data Set
MyDataSet = New DataSet("MyDataSet")
Dim myTable As New DataTable("Customers")
Dim myColumn As New DataColumn("CustomerID")
myTable.Columns.Add(myColumn)
myColumn = New DataColumn("CustomerName")
myTable.Columns.Add(myColumn)
MyDataSet.Tables.Add(myTable)
Dim myRow As DataRow = myTable.NewRow()
myRow("CustomerID") = "100"
myRow("CustomerName") = "John Doe"
myTable.Rows.Add(myRow)
myRow = myTable.NewRow()
myRow("CustomerID") = "200"
myRow("CustomerName") = "Mary Lou"
myTable.Rows.Add(myRow)
'Bind Data Grid
DataGrid1.SetDataBinding(MyDataSet, "Customers")
'Add Custom DataTableStyle
Dim myTableStyle As New DataGridTableStyle
myTableStyle.MappingName = "Customers"
Dim myTextColumn As New MyDataGridTextBoxColumn
myTextColumn.MappingName = "CustomerID"
myTextColumn.HeaderText = "Customer ID"
myTextColumn.Width = 100
myTableStyle.GridColumnStyles.Add(myTextColumn)
myTextColumn = New MyDataGridTextBoxColumn
myTextColumn.MappingName = "CustomerName"
myTextColumn.HeaderText = "Customer Name"
myTextColumn.Width = 200
myTableStyle.GridColumnStyles.Add(myTextColumn)
DataGrid1.TableStyles.Add(myTableStyle)
End Sub
End Class
Public Class MyDataGrid
Inherits DataGrid
Public blnEdit As Boolean = False
Private Sub MyDataGrid_CurrentCellChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.CurrentCellChanged
blnEdit = False
End Sub
Private Sub MyDataGrid_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
' Use the HitTest method to get a HitTestInfo object.
Dim hi As DataGrid.HitTestInfo
Dim grid As DataGrid = CType(sender, DataGrid)
hi = grid.HitTest(e.X, e.Y)
' Test if the clicked area was a cell.
If hi.Type = DataGrid.HitTestType.Cell Then
blnEdit = True
End If
End Sub
Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, ByVal keyData As System.Windows.Forms.Keys) As Boolean
If msg.WParam.ToInt32() = CInt(Keys.Enter) Then
SendKeys.Send("{Tab}")
Return True
End If
blnEdit = True
Return MyBase.ProcessCmdKey(msg, keyData)
End Function 'ProcessCmdKey
Private Sub MyDataGrid_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
blnEdit = True
End Sub
End Class
Public Class MyDataGridTextBoxColumn
Inherits DataGridTextBoxColumn
Protected Overloads Overrides Sub Edit(ByVal source As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer, ByVal bounds As System.Drawing.Rectangle, ByVal [readOnly] As Boolean, ByVal instantText As String, ByVal cellIsVisible As Boolean)
If Not CType(Me.DataGridTableStyle.DataGrid, MyDataGrid).blnEdit Then
Me.HideEditBox()
Else
MyBase.Edit(source, rowNum, bounds, [readOnly], instantText, cellIsVisible)
End If
End Sub
Protected Overloads Overrides Sub Paint(ByVal g As System.Drawing.Graphics, ByVal bounds As System.Drawing.Rectangle, ByVal source As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer, ByVal backBrush As System.Drawing.Brush, ByVal foreBrush As System.Drawing.Brush, ByVal alignToRight As Boolean)
If DataGridTableStyle.DataGrid.CurrentRowIndex = rowNum _
And DataGridTableStyle.DataGrid.CurrentCell.ColumnNumber = DataGridTableStyle.GridColumnStyles.IndexOf(Me) Then
backBrush = New SolidBrush(Color.Red)
End If
MyBase.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight)
End Sub
End Class