Results 1 to 10 of 10

Thread: [RESOLVED] DataGridView Autocomplete TextBox

  1. #1

    Thread Starter
    Hyperactive Member
    Join Date
    Mar 2013
    Location
    San Francisco, CA
    Posts
    487

    Resolved [RESOLVED] DataGridView Autocomplete TextBox

    I have a DataGridView that is ReadOnly. I would like to implement "type ahead" functionality for a column (DataGridViewTextBoxColumn) of the grid.

    I have seen examples for how this is done. For example:

    http://vb.net-informations.com/datagridview/vbauto.htm

    The example in the above link works for a grid that is not read-only, but it doesn't work for a read-only grid because the EditingControlShowing event is never raised.

    This example makes use of the DataGridViewEditingControlShowingEventArgs to cast the selected cell as a TextBox.

    Code:
    TryCast(e.Control, TextBox)
    Is there a way to do this for a read-only grid?
    Last edited by Mark@SF; May 14th, 2021 at 10:14 AM.

  2. #2
    PowerPoster
    Join Date
    Sep 2005
    Location
    Modesto, Ca.
    Posts
    5,196

    Re: DataGridView Autocomplete TextBox

    Sorry, don't have an answer but I'm confused about wanting a textbox autocomplete when the grid is read only. What are you trying to accomplish?

  3. #3

    Thread Starter
    Hyperactive Member
    Join Date
    Mar 2013
    Location
    San Francisco, CA
    Posts
    487

    Re: DataGridView Autocomplete TextBox

    Quote Originally Posted by wes4dbt View Post
    Sorry, don't have an answer but I'm confused about wanting a textbox autocomplete when the grid is read only. What are you trying to accomplish?
    This application is a password manager. It has a TableLayoutPanel with a DataGridView on the left-hand side of the panel (dgvAccounts). The grid is a listing of all accounts. The user (me) clicks an account in the grid and the account details are shown in the right-hand side of the panel (password, security questions, notes, etc.).

    I am interested in doing the following:

    1. Give the Focus to the dgvAccounts grid.
    2. Press a key (e.g., "b") and have the dgvAccounts selected item change to the first account in the grid that begins with the pressed key ("b").
    3. Press a another key (e.g., "e") and now the selected item changes to the first account in the grid that begins with these 2 keys ("be").
    4. Repeat #2 and #3 until the the user (me) finds the account of interest.

    I can do this with the dgvAccounts.KeyPress method and a class-level Timer and a class-level Stopwatch...

    Code:
        Private WithEvents autotypeTimer As New Timer
        Private WithEvents autotypeStopWatch As New Stopwatch
        Dim autotypeText As String
    Code:
    Private Sub timerAutoType_Tick(sender As Object, e As EventArgs) Handles autotypeTimer.Tick
    
        Dim strMethodName = New System.Diagnostics.StackTrace().GetFrame(0).GetMethod().Name    '...this procedure's name
    
        Dim flgDebug As Boolean = False   '...debug/test purposes only
    
        Try
            If autotypeStopWatch.IsRunning Then
                autotypeStopWatch.Stop()
                autotypeStopWatch.Reset()
                autotypeTimer.Stop()
                autotypeText = ""
            End If
    
        Catch ex As Exception
            MessageBox.Show(ex.GetType.ToString & vbCrLf & vbCrLf & ex.Message & " (err=" & Err.Number & ")", strMethodName, MessageBoxButtons.OK, MessageBoxIcon.Error)
    
        End Try
    
    End Sub
    Code:
    Private Sub dgvAccounts_KeyPress(sender As Object, e As KeyPressEventArgs) Handles dgvAccounts.KeyPress
    
        Dim strMethodName = New System.Diagnostics.StackTrace().GetFrame(0).GetMethod().Name    '...this procedure's name
    
        Dim flgDebug As Boolean = True   '...debug/test purposes only
        Dim strFilter As String
        Dim index As Integer
        Dim rows() As DataRow
    
        Try
            If Not autotypeStopWatch.IsRunning Then
                autotypeStopWatch.Start()
                autotypeTimer.Start()
            End If
    
            autotypeText += e.KeyChar
            strFilter = "Account LIKE '" & autotypeText & "%'"
    
            If rdoActive.Checked Then
                strFilter += " AND Status = True"
            ElseIf rdoInactive.Checked Then
                strFilter += " AND Status = False"
            ElseIf rdoBoth.Checked Then
                '...returns active and inactive accounts
            End If
    
            If cbxFavorites.Checked Then
                strFilter += " AND IsFavorite = True"
            End If
    
            rows = dsMain.Tables("Accounts").Select(filterExpression:=strFilter, sort:="Account")
    
            If flgDebug Then Debug.WriteLine("{0}, {1}, Count = {2}", autotypeText, strFilter, rows.Length)
    
            If rows.Length > 0 Then
                If flgDebug Then
                    For i As Integer = 0 To rows.Count - 1
                        Debug.WriteLine("{0}, {1}", i, rows(i)("Account"))
                    Next
                End If
                index = bsAccounts.Find("Account", rows(0).Item("Account").ToString)
                bsAccounts.Position = index
            End If
    
        Catch ex As Exception
            MessageBox.Show(ex.GetType.ToString & vbCrLf & vbCrLf & ex.Message & " (err=" & Err.Number & ")", strMethodName, MessageBoxButtons.OK, MessageBoxIcon.Error)
    
        Finally
            rows = Nothing
    
        End Try
    
    End Sub
    When I came across the example for how to use the DataGridViewEditingControlShowingEventArgs to cast the selected cell as a TextBox, it peaked my curiosity as to whether or not I could do the same for my read-only grid.
    Last edited by Mark@SF; May 14th, 2021 at 02:18 PM.

  4. #4
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,302

    Re: DataGridView Autocomplete TextBox

    Forget about auto-complete. In order to complete something automatically, there has to be something to complete. If your grid is read-only then there's no data being entered so there's nothing to complete. Forget about auto-complete, unless you want to use a TextBox control that is completely separate to the grid.

    Handling a keyboard event is the right way to go but it seems to me that you're complicating things terribly. What exactly is the point of the Stopwatch and Timer? It seems to me that you're clearing the stored text if the user doesn't type anything for a period of time. In that case, you don't need both a Stopwatch and a Timer and you could actually do without either. Here's a simple demo:
    vb.net Code:
    1. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2.     Dim table As New DataTable
    3.  
    4.     table.Columns.Add("Name", GetType(String))
    5.  
    6.     With table.Rows
    7.         .Add("Peter")
    8.         .Add("Paul")
    9.         .Add("Mary")
    10.         .Add("John")
    11.         .Add("James")
    12.         .Add("Janet")
    13.     End With
    14.  
    15.     BindingSource1.DataSource = table
    16.     DataGridView1.DataSource = BindingSource1
    17. End Sub
    18.  
    19. Private Sub DataGridView1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles DataGridView1.KeyPress
    20.     Console.WriteLine("DataGridView1_KeyPress")
    21.     'The search text will be reset if nothing is typed for 3 seconds.
    22.     Dim timeThreshold = TimeSpan.FromSeconds(3)
    23.  
    24.     Static lastEventTime As Date = Date.MinValue
    25.     Static searchText As String = String.Empty
    26.  
    27.     If Date.Now - lastEventTime > timeThreshold Then
    28.         'The time limit since the last key press has expired so reset the search text.
    29.         searchText = e.KeyChar
    30.     Else
    31.         'Append to the current search text.
    32.         searchText += e.KeyChar
    33.     End If
    34.  
    35.     lastEventTime = Date.Now
    36.  
    37.     For i = 0 To BindingSource1.Count - 1
    38.         Dim row = DirectCast(BindingSource1(i), DataRowView)
    39.         Dim name = CStr(row("Name"))
    40.  
    41.         If name.StartsWith(searchText, StringComparison.CurrentCultureIgnoreCase) Then
    42.             BindingSource1.Position = i
    43.             Exit For
    44.         End If
    45.     Next
    46. End Sub
    That would need a bit more work to be production-ready, e.g. if there's no match then you have to wait three seconds before trying again, but it demonstrates the principle. You could use a Timer or a Stopwatch to control when the search text gets reset but you definitely don't need both. Maybe I'm misinterpreting something but, with no explanatory comments in your code, that can happen.

  5. #5

    Thread Starter
    Hyperactive Member
    Join Date
    Mar 2013
    Location
    San Francisco, CA
    Posts
    487

    Re: DataGridView Autocomplete TextBox

    jmc -

    Forget about auto-complete
    Roger that.

    Thanks for the demo code. I agree, your approach is much cleaner. My initial effort was a trial-and-error approach to just get something working. I'll rework it to make it cleaner.

    I've never used the StartsWith method and, once again, you've shown me something new. Thanks for that.

    I am going to take a look at whether or not using a For...End For loop to iterate the rows of the BindingSource is faster than calling the Select method on the bound DataSet.

    Thanks again for all your help.

  6. #6
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,302

    Re: DataGridView Autocomplete TextBox

    Quote Originally Posted by Mark@SF View Post
    I am going to take a look at whether or not using a For...End For loop to iterate the rows of the BindingSource is faster than calling the Select method on the bound DataSet.
    One issue I can see with Select is that it will always check every row, because it returns an array that can contain multiple matches, while my code stops searching when it finds the first match. If you wanted to highlight all matches then Select might be a better option. Also, working with the DataTable means that it will ignore any filtering and sorting. You may not be using any but using the BindingSource will respect either or both.

  7. #7

    Thread Starter
    Hyperactive Member
    Join Date
    Mar 2013
    Location
    San Francisco, CA
    Posts
    487

    Re: DataGridView Autocomplete TextBox

    jmc -

    It looks like calling the Select method is usually faster than iterating the rows of the BindingSource. Here's some of the results (the first number after "search: " is the ElapsedTicks of a Timer and the second number is either the BindingSource rows examined or the DataTable selected rows count).

    Code:
    5/15/2021 6:39:24 AM, a
    bsAccounts search:  147537, 3
    DataTable search:  9031, 29
    5/15/2021 6:39:25 AM, al
    bsAccounts search:  123834, 9
    DataTable search:  7584, 5
    5/15/2021 6:39:35 AM, 2
    bsAccounts search:  146151, 0
    DataTable search:  8202, 1
    5/15/2021 6:39:42 AM, y
    bsAccounts search:  318427, 262
    DataTable search:  9644, 2
    5/15/2021 6:40:44 AM, b
    bsAccounts search:  293516, 32
    DataTable search:  8652, 17
    5/15/2021 6:40:44 AM, bl
    bsAccounts search:  82666, 36
    DataTable search:  7678, 2
    The BindingSource has 268 rows.

    I am filtering the DataTable with the Select method. You can see that in the above results. For example, looking at the last 6 lines (KeyPress "b" followed by KeyPress "l"). The first KeyPress returned 17 rows from the Select method and the second KeyPress returned 2 rows. When iterating the BindingSource, the first KeyPress exited the loop at row #32 and the second KeyPress exited the loop at row #36.

    Thanks again for your help!
    Last edited by Mark@SF; May 15th, 2021 at 05:44 AM.

  8. #8
    Fanatic Member Delaney's Avatar
    Join Date
    Nov 2019
    Location
    Paris, France
    Posts
    845

    Re: DataGridView Autocomplete TextBox

    Quote Originally Posted by jmcilhinney View Post
    One issue I can see with Select is that it will always check every row, because it returns an array that can contain multiple matches,
    I always thought it stops as soon as it finds the first matching case and exit the select afterward. I tested (VB 2010, FW 4.0) it with this simple code :

    Code:
     Dim var = 3
            Dim result = ""
            Select Case var
                Case 3
                    result += " =3"
                Case Is > 2
                    result += " >2"
            End Select
            MsgBox(result)
    and it never goes to the case >2.

    Did I miss something?
    Last edited by Delaney; May 15th, 2021 at 03:55 PM.
    The best friend of any programmer is a search engine
    "Don't wish it was easier, wish you were better. Don't wish for less problems, wish for more skills. Don't wish for less challenges, wish for more wisdom" (J. Rohn)
    “They did not know it was impossible so they did it” (Mark Twain)

  9. #9
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,302

    Re: DataGridView Autocomplete TextBox

    Quote Originally Posted by Delaney View Post
    I always thought it stops as soon as it finds the first matching case and exit the select afterward. I tested (VB 2010, FW 4.0) it with this simple code :

    Code:
     Dim var = 3
            Dim result = ""
            Select Case var
                Case 3
                    result += " =3"
                Case Is > 2
                    result += " >2"
            End Select
            MsgBox(result)
    and it never goes to the case >2.

    Did I miss something?
    My comment was nothing to do with the Select Case statement. It was about the DataTable.Select method.

  10. #10
    Fanatic Member Delaney's Avatar
    Join Date
    Nov 2019
    Location
    Paris, France
    Posts
    845

    Re: DataGridView Autocomplete TextBox

    Ah, Ok, misreading, Sorry
    The best friend of any programmer is a search engine
    "Don't wish it was easier, wish you were better. Don't wish for less problems, wish for more skills. Don't wish for less challenges, wish for more wisdom" (J. Rohn)
    “They did not know it was impossible so they did it” (Mark Twain)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width