"Normal tabbing" with datagrid

rickarde

Newcomer
Joined
Apr 8, 2004
Messages
14
Location
Sweden
Hi!
I wonder how I can get my inherited datagrid to behave like other controls when tabbing around my winform ?
As it is, per default, the tab key, once the datagrid has got focus, moves focus around inside the datagrid, along the columns and rows.
This is what I want the arrow keys to do, while the tab key should "enter" or "leave" the datagrid...

Overriding the isInputKey method is not enough since it, correctly leaves it to me to handle the key events for the tab key, but also makes the selected row unselected...

/Rickard
 
Hi Rickarde,

I've just been able to do what you want.

First, I took the original code from here:

http://www.syncfusion.com/faq/winforms/Files/DataGridTextBoxKeys.zip

After some editings in the above sample code, I was able to prevent datagrid from tabbing inside its cells. Plus, I added code to select the next form control:

PART I

Code:
Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data

Public Class Form1
   Inherits System.Windows.Forms.Form
   Private dataGrid1 As System.Windows.Forms.DataGrid
   Private myDataSet As DataSet
   Private WithEvents TextCol As DataGridKeyTrapTextBoxColumn

   Private components As System.ComponentModel.Container = Nothing


   Public Sub New()
      '
      ' Required for Windows Form Designer support
      '
      InitializeComponent()

      SetUp()
   End Sub 'New


   Private Sub SetUp()
      MakeDataSet()
      dataGrid1.SetDataBinding(myDataSet, "Customers")
      AddCustomDataTableStyle()
   End Sub 'SetUp


   Private Sub MakeDataSet()
      ' Create a DataSet.
      myDataSet = New DataSet("myDataSet")

      Dim tCust As New DataTable("Customers")


      ' Create two columns, and add them to the first table.
      Dim cCustID As New DataColumn("custID")
      Dim cCustName As New DataColumn("custName")
      Dim cCurrent As New DataColumn("custCity")
      tCust.Columns.Add(cCustID)
      tCust.Columns.Add(cCustName)
      tCust.Columns.Add(cCurrent)

      ' Add the tables to the DataSet.
      myDataSet.Tables.Add(tCust)


      ' Populates the tables. For each customer and order, 
      '			creates two DataRow variables. 
      Dim newRow1 As DataRow

      ' Create three customers in the Customers Table.
      Dim i As Integer
      For i = 1 To 3
         newRow1 = tCust.NewRow()
         newRow1("custID") = (100 * i).ToString()
         tCust.Rows.Add(newRow1)
      Next i
      ' Give each customer a distinct name.
      tCust.Rows(0)("custName") = "John Summers"
      tCust.Rows(1)("custName") = "Phil Seagram"
      tCust.Rows(2)("custName") = "Sam Robinson"

      ' And address
      tCust.Rows(0)("custCity") = "Chicago"
      tCust.Rows(1)("custCity") = "Los Angeles"
      tCust.Rows(2)("custCity") = "Washington"
   End Sub 'MakeDataSet


   Private Sub AddCustomDataTableStyle()
      'CType(Me.Container, System.Windows.Forms.Form).SelectNextControl(Me.Container, True, True, True, True)
      Dim ts1 As New DataGridTableStyle()
      ts1.MappingName = "Customers"
      ' Set other properties.
      ts1.AlternatingBackColor = Color.LightGray
      '
      ' Add textbox column style so we can catch textbox mouse clicks
      TextCol = New DataGridKeyTrapTextBoxColumn()
      TextCol.f = Me
      TextCol.MappingName = "custID"
      TextCol.HeaderText = "CustomerID"
      TextCol.Width = 100
      'add handler

      ts1.GridColumnStyles.Add(TextCol)

      TextCol = New DataGridKeyTrapTextBoxColumn()
      TextCol.f = Me
      TextCol.MappingName = "custName"
      TextCol.HeaderText = "Customer Name"
      TextCol.Width = 100
      'add handler

      ts1.GridColumnStyles.Add(TextCol)

      TextCol = New DataGridKeyTrapTextBoxColumn()
      TextCol.f = Me
      TextCol.MappingName = "custCity"
      TextCol.HeaderText = "Customer Address"
      TextCol.Width = 100
      'add handler
      DataGridKeyTrapTextBoxColumn._RowCount = myDataSet.Tables("Customers").Rows.Count

      ts1.GridColumnStyles.Add(TextCol)

      dataGrid1.TableStyles.Add(ts1)
   End Sub 'AddCustomDataTableStyle


   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 'Dispose

#Region "Windows Form Designer generated code"

   '/ <summary>
   '/ Required method for Designer support - do not modify
   '/ the contents of this method with the code editor.
   '/ </summary>
   Friend WithEvents CheckBox1 As System.Windows.Forms.CheckBox
   Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
   Private Sub InitializeComponent()
      Me.dataGrid1 = New System.Windows.Forms.DataGrid()
      Me.CheckBox1 = New System.Windows.Forms.CheckBox()
      Me.TextBox1 = New System.Windows.Forms.TextBox()
      CType(Me.dataGrid1, System.ComponentModel.ISupportInitialize).BeginInit()
      Me.SuspendLayout()
      '
      'dataGrid1
      '
      Me.dataGrid1.Anchor = (((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
                  Or System.Windows.Forms.AnchorStyles.Left) _
                  Or System.Windows.Forms.AnchorStyles.Right)
      Me.dataGrid1.DataMember = ""
      Me.dataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText
      Me.dataGrid1.Location = New System.Drawing.Point(56, 24)
      Me.dataGrid1.Name = "dataGrid1"
      Me.dataGrid1.Size = New System.Drawing.Size(320, 216)
      Me.dataGrid1.TabIndex = 0
      '
      'CheckBox1
      '
      Me.CheckBox1.Location = New System.Drawing.Point(16, 48)
      Me.CheckBox1.Name = "CheckBox1"
      Me.CheckBox1.Size = New System.Drawing.Size(24, 48)
      Me.CheckBox1.TabIndex = 1
      Me.CheckBox1.Text = "CheckBox1"
      '
      'TextBox1
      '
      Me.TextBox1.Location = New System.Drawing.Point(8, 176)
      Me.TextBox1.Name = "TextBox1"
      Me.TextBox1.Size = New System.Drawing.Size(32, 20)
      Me.TextBox1.TabIndex = 2
      Me.TextBox1.Text = "TextBox1"
      '
      'Form1
      '
      Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
      Me.ClientSize = New System.Drawing.Size(456, 277)
      Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.TextBox1, Me.CheckBox1, Me.dataGrid1})
      Me.Name = "Form1"
      Me.Text = "Form1"
      CType(Me.dataGrid1, System.ComponentModel.ISupportInitialize).EndInit()
      Me.ResumeLayout(False)

   End Sub 'InitializeComponent 
#End Region
   <STAThread()> _
   Shared Sub Main()
      Application.Run(New Form1())
   End Sub 'Main

   Private Sub TextCol_EvHandledTab() Handles TextCol.EvHandledTab
      Me.SelectNextControl(Me.ActiveControl, True, True, True, True)
   End Sub
End Class 'Form1

END OF PART I
 
PART II

Code:
Public Class DataGridKeyTrapTextBoxColumn
   Inherits DataGridTextBoxColumn
   Event EvHandledTab()
   Public f As Form = Nothing
   Private WithEvents _keyTrapTextBox As KeyTrapTextBox = Nothing
   Private _source As System.Windows.Forms.CurrencyManager = Nothing
   Private _rowNum As Integer
   Private _isEditing As Boolean = False
   Public Shared _RowCount As Integer = 0


   Public Sub New()
      _keyTrapTextBox = New KeyTrapTextBox()
      _keyTrapTextBox.BorderStyle = BorderStyle.None

      AddHandler _keyTrapTextBox.Leave, AddressOf LeaveKeyTrapTextBox
      AddHandler _keyTrapTextBox.KeyPress, AddressOf TextBoxEditStarted
   End Sub 'New


   Private Sub TextBoxEditStarted(ByVal sender As Object, ByVal e As KeyPressEventArgs)
      _isEditing = True
      MyBase.ColumnStartedEditing(CType(sender, Control))
   End Sub 'TextBoxEditStarted


   Private Sub LeaveKeyTrapTextBox(ByVal sender As Object, ByVal e As EventArgs)
      If _isEditing Then
         SetColumnValueAtRow(_source, _rowNum, _keyTrapTextBox.Text)
         _isEditing = False
         Invalidate()
      End If
      _keyTrapTextBox.Hide()
   End Sub 'LeaveKeyTrapTextBox



   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)


      _RowCount = [source].Count

      MyBase.Edit([source], rowNum, bounds, [readOnly], instantText, cellIsVisible)

      _rowNum = rowNum
      _source = [source]

      _keyTrapTextBox.Parent = Me.TextBox.Parent
      _keyTrapTextBox.Location = Me.TextBox.Location
      _keyTrapTextBox.Size = Me.TextBox.Size
      _keyTrapTextBox.Text = Me.TextBox.Text
      Me.TextBox.Visible = False
      _keyTrapTextBox.Visible = True
      _keyTrapTextBox.BringToFront()
      _keyTrapTextBox.Focus()
   End Sub 'Edit


   Protected Overrides Function Commit(ByVal dataSource As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer) As Boolean
      If _isEditing Then
         _isEditing = False
         SetColumnValueAtRow(dataSource, rowNum, _keyTrapTextBox.Text)
      End If
      Return True
   End Function 'Commit

   Private Sub _keyTrapTextBox_EvHandledTab() Handles _keyTrapTextBox.EvHandledTab
      If Not IsNothing(f) Then
         f.SelectNextControl(f.ActiveControl, True, True, True, True)
      End If
   End Sub
End Class 'DataGridKeyTrapTextBoxColumn


Public Class KeyTrapTextBox
   Inherits TextBox

   Event EvHandledTab()
   Public Sub New()
   End Sub 'New


   Private Const WM_KEYDOWN As Integer = &H100
   Private Const WM_KEYUP As Integer = &H101
   Private Const WM_CHAR As Integer = &H102


   Public Overrides Function PreProcessMessage(ByRef msg As Message) As Boolean
      Dim keyCode As Keys = CType(msg.WParam.ToInt32(), Keys) And Keys.KeyCode
      If msg.Msg = WM_KEYDOWN Then
         Console.WriteLine(("TextBox.WM_KEYDOWN    key: " + keyCode.ToString()))
      End If

      ' for a datagrid, we need to eat the tab key oe else its done twice
      If msg.Msg = WM_KEYDOWN AndAlso keyCode = Keys.Tab Then
         'to ignore a message return true without calling baseclass
         'to let the textbox handle message return false;
         'don't let textbox handle tab
         keyCode = 0
         RaiseEvent EvHandledTab()
         Return True
      End If
      Return MyBase.PreProcessMessage(msg)

      '		//sample handling code. This lets the textbox handle the delete
      '		//& preventing (for example) a delete shortcut on a menu getting it
      '			if((msg.Msg == WM_KEYDOWN)
      '				&& keyCode == Keys.Delete)
      '			{
      '				//to ignore a message return true without calling baseclass
      '				//to let the textbox handle message return false;
      '
      '				//let textbox handle Delete
      '				return false;
      '			}	
      'Return MyBase.PreProcessMessage(msg)
   End Function 'PreProcessMessage

End Class 'KeyTrapTextBox

Hope this will help!
 
Back
Top