A
Aaron S_
Guest
Does anyone use the DataGridView with combo boxes in *editable* (type-in) mode?
Here is some code that makes it a lot easier. I combined the best examples I could find from the FAQ, the forum, and tech support, along with some changes of my own.
It is a customized DataGridView wrapper that handles the dirty work for you so that all combo boxes are editable by default.
To use it:
(1) Paste this code into a new VB class file called APSDataGridView.vb
(2) Then go to Project -> ...Properties -> Application tab -> Root Namespace, and clear the root namespace.
(3) Recompile your project, and it will appear at the top of the toolbox.
(4) Use the APSDataGridView control where you would use a DataGridView.
This code requires Visual Basic 2005 or later.
Let me know if you want a C# version, or if you have questions or problems.
Regards,
Aaron Stewart
APSDataGridView.vb
Wrapper for DataGridView
Modifies default behavior so that Combo Boxes are editable (type-in) by default.
This can be handy because type-in DataGridView combo boxes are a lot more
involved than you might think.
Aaron Stewart, 2007
Instructions:
(1) Create a new Windows Forms project in VB.NET 2005 or higher.
(2) Create a new class (not a user control) named APSDataGridView.vb
and paste this code into it, replacing what is there.
(3) Go to Project -> ...Properties -> Application tab -> Root Namespace,
and clear the root namespace. (This is necessary for VB, not C#)
(4) Compile the project. The APSDataGridView is now available in the Toolbox, at the top.
(5) Now go to your form (such as Form1.vb) and add an APSDataGridView control using the designer.
Dont make it too small.
(6) Now in your form code you can paste the example or write your own code.
(7) Compile and run
(8) You can try it with the test cases below.
Some differences when using APSDataGridView:
(1) If you want to set Combo Box parameters with the EditingControlShowing event,
use the EditingControlShowing2 event instead. If you are not dealing with Combo Boxes,
you can use the regular EditingControlShowing event.
(2) Do not use the CellValidating event. Use CellValidating2 instead.
(This allows combo box validation handler to work without conflicts.)
The DataGridView does not directly support type-in (editable) ComboBox mode,
and all of the published examples I have seen are incomplete.
To get the DataGridView to work properly with ComboBoxes, do ALL of the following:
(1) In DataGridView.EditingControlShowing, set ComboBox.DropDownStyle = ComboBoxStyle.DropDown for the desired column(s) (Source: DataGridView FAQ)
(2) Set DataGridView.NotifyCurrentCellDirty(True) in DataGridView.EditingControlShowing (Source: Microsoft Support)
(3) In DataGridView1.CellValidating, force the ComboBox SelectedIndex : cbo.SelectedIndex = cbo.FindStringExact(cbo.Text.Trim) (Source: Forum member Aspnot, http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=254131&SiteID=1 ; See also http://msdn.microsoft.com/msdnmag/issues/06/01/DevQA/ )
(4) In ComboBox.PreviewKeyDown, if e.KeyCode = Keys.Enter then force ComboBox.DroppedDown = False (Source: My own experiments)
(5) In ComboBox.PreviewKeyDown, if e.KeyCode = Keys.Escape then set ComboBox.Text = "" . (Source: My own experiments)
Even with all of these, the behavior isnt as nice as with Access or Excel.
Motivation for these:
#1 - This puts the combo box into type-in mode
#2 - Without this the DataGridView stays in an unfinished edit mode even after you leave the cell
(Also, test case #2 below will fail if this is done in ComboBox.TextChanged instead of
DataGridView.EditingControlShowing and the ComboBox is set to AutoCompleteMode.SuggestAppend)
#3 - Similar to #2 - the DataGridView wont work right without it
#4 - Resolves an issue with ComboBoxes where if you open the drop-down and type an entry and press ENTER, it loses the value.
#5 - For test case #11. Allows ESC to work properly with the DataGridView. Normally ESC works fine, but if AutoComplete (Append) is enabled AND you enter text that matches something on the list, then something interferes with the ESC key.
Test cases for validating an editable ComboBox :
Try all of these cases on the new row at the bottom of the DataGridView (and if you like, also try them on an interior row).
Here are the test cases:
(1) Drop-down, click an item, submit
(2) Type a known value, submit (try this with and without matching capitalization)
(3) Drop-down, type a known value (do not use the arrow keys to select it), press ENTER
(4) Drop-down, select a value using the arrow keys or mouse, press ENTER
(5)* Drop-down, start typing a known value, then try to use the arrow keys to select a different value (this case does not work cleanly like in Access or Excel)
(6) Enter a value other than the first value, submit, return to this cell, Clear it, press ENTER.
(7)* Set AutoCompleteMode.SuggestAppend, AutoCompleteSource.ListItems. It works but it isnt as nice as Access or Excel.
(8)* At the new row, drop-down but dont enter a value, submit. (This is probably acceptable behavior).
(9) Start entering a known value. Then drop-down. Then TAB. (http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=254131&SiteID=1)
(10) Start typing a value not matching the list, then press ESC to cancel it. The new row should completely disappear.
(11) Start typing a value that matches an item in the list (so that AutoComplete kicks in), then press ESC to cancel it. The new row should completely disappear. It might be tolerable if it leaves a blank but no pencil icon.
Cases marked with an asterisk(*) do not work as nicely as Access or Excel.
Notes:
(i) All of these cases start with an empty combo box, unless otherwise specified.
(ii) Drop-down : by clicking on the mouse or pressing Alt+Down
(iii) Submit : Normally clicking off the control, pressing TAB, and
pressing ENTER are all ways submit the value. However, in some
cases a specific submit method is specified.
(iv) Clear: Press Ctrl+0 or edit and delete the text or edit and press delete.
Imports System.Windows.Forms
Namespace APS.Windows.Forms
Derived DataGridView that takes care of the details so combo boxes use type-in (editable) mode.
Public Class APSDataGridView
Inherits System.Windows.Forms.DataGridView
It seems to generate the delegate [event]EventHandler automatically from the event declaration...
Public Delegate Sub ComboBoxItemNotInListEventHandler(ByVal dgv As DataGridView, ByVal cbo As ComboBox, ByVal cboColumn As DataGridViewComboBoxColumn, ByVal e As DataGridViewCellValidatingEventArgs)
Public Event ComboBoxItemNotInList(ByVal dgv As DataGridView, ByVal cbo As ComboBox, ByVal cboColumn As DataGridViewComboBoxColumn, ByVal e As DataGridViewCellValidatingEventArgs)
Constructor
Public Sub New()
MyBase.New()
End Sub
This makes combo boxes "editable" so you can type in them. See the DataGridView FAQ.
Protected Overrides Sub OnEditingControlShowing( _
ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs)
Dim dgv As DataGridView = Me sender
If (TypeOf e.Control Is ComboBox) Then
Dim cbo As ComboBox = DirectCast(e.Control, ComboBox)
Optional: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=254131&SiteID=1
cbo.AutoCompleteMode = AutoCompleteMode.SuggestAppend
cbo.AutoCompleteSource = AutoCompleteSource.ListItems
This makes it a type-in combo box
cbo.DropDownStyle = ComboBoxStyle.DropDown
Adjust the ComboBox behavior:
AddHandler cbo.PreviewKeyDown, New PreviewKeyDownEventHandler(AddressOf comboBox_PreviewKeyDown)
(Thanks to Ying Liu, Microsoft Support[Broken External Image]:http://forums.microsoft.com/MSDN/emoticons/emotion-1.gif
dgv.NotifyCurrentCellDirty(True)
End If
Call registered event handlers...
MyBase.OnEditingControlShowing(e)
End Sub
Validate typed-in values...
Protected Overrides Sub OnCellValidating(ByVal e As DataGridViewCellValidatingEventArgs)
Dim dgv As DataGridView = Me sender
If Not TypeOf (dgv.EditingControl) Is ComboBox Then
Every control path should call MyBase.OnCellValidating
MyBase.OnCellValidating(e)
Else
Dim cbo As ComboBox = dgv.EditingControl
If (cbo Is Nothing) Then
MyBase.OnCellValidating(e)
Exit Sub cbo is Nothing if the user did not edit the cell
End If
This does two things:
(1) This makes the combo box case-insensitive.
(2) You have to force the SelectedIndex when using type-in mode or it will not properly recognize the change
This little gem comes from
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=254131&SiteID=1
cbo.SelectedIndex = cbo.FindStringExact(cbo.Text.Trim)
You could customize the handling below on a column-by-column basis...
If cbo.SelectedIndex = -1 Then The item wasnt in the list...
Is it because the value is empty?
If (cbo.Text.Trim = String.Empty) Then
We assume that its okay to have an empty value here...
Let the user delete the value...
Dont Raise Event ComboBoxItemNotInList...
Else
A value was entered but it was not in the list...
Dim cboColumn As DataGridViewComboBoxColumn = dgv.Columns(e.ColumnIndex)
RaiseEvent ComboBoxItemNotInList(dgv, cbo, cboColumn, e)
If (e.Cancel = True) Then
How do we cancel a value if it creates a new row?
Beep() Set a breakpoint here
? force dgv value to empty???
Dont return yet - we still need to call the base class event handler
Else
If the user added the item to the combo box, we need to accept the new value...
Again, from http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=254131&SiteID=1
cbo.SelectedIndex = cbo.FindStringExact(cbo.Text.Trim)
End If
End If
End If
End If
Call the base class handler and user handlers...
MyBase.OnCellValidating(e)
End Sub
Protected Shared Sub comboBox_PreviewKeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs)
There is a glitch if you are using a ComboBox either in a DataGridView or standalone.
If you open the ComboBox dropdown but then type a value and press ENTER, the value is lost.
This error does not appear if you press TAB.
This is the workaround.
If (e.KeyCode = Keys.Enter) Then
Dim cbo As ComboBox = sender
If cbo.DroppedDown Then
cbo.DroppedDown = False
End If
End If
Allow the ESC key to cancel changes even if AutoComplete mode has been active.
If (e.KeyCode = Keys.Escape) Then
Dim cbo As ComboBox = sender
I dont know why this works, but it does:
cbo.Text = ""
End If
End Sub
End Class
End Namespace
Continue reading...