ctrl F in DataGridView

829 Views Asked by At

I'm trying to implement an equivalent to a ctrl+F function in my project. I want it to work like in an excel where the cursor points at the matching string in any cell. It can also work as a filter that only show the rows with the matching string.

EDIT 1 : Here is what i tried when using @Gabriel Stancu 's method :

        Private Sub btnSearch_Click(sender As Object, e As EventArgs) Handles btnSearch.Click
        'Dim search As New OleDbCommand
        'Dim da As OleDbDataAdapter
        Dim ds As New DataSet
        Dim dt As New DataTable
        Dim dgv As DataGridView

        Dim searchedValue As String
        frmPrinc.dgv.DataSource = dt.DefaultView

        searchedValue = txtBoxName.Text
        MsgBox(searchedValue) 'Displayed
        For Each row As DataGridViewRow In frmPrinc.dgv.Rows 
            MsgBox("yes") 'Not displayed
            For Each cell As DataGridViewCell In row.Cells
                MsgBox("yes2") 'Not displayed
                If cell.Value IsNot DBNull.Value Then
                    MsgBox("yes3") 'Not displayed
                    If cell.Value.ToString().Equals(searchedValue) Then
                        cell.Selected = True
                        MsgBox(cell.Selected)
                        Exit Sub
                    End If
                End If
            Next
        Next

    End Sub

this just cleared my DataGridView display

EDIT 2 : Here's what i'm currently working on using @jmcilhinney 's method :

    Private Sub btnSearch_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnSearch.Click

        Dim myBindingSource As New BindingSource
        Dim myDataTable As New DataTable
        Dim myDataGridView As New DataGridView
        myBindingSource.DataSource = myDataTable
        myDataGridView.DataSource = myBindingSource

        Dim myValue As String

        myValue = txtBoxName.Text
        
        MsgBox(myValue)
        myDataTable.Columns.Add(New DataColumn("NAME", GetType(String)))
        myBindingSource.DataSource = myDataTable

        myBindingSource.Position = myBindingSource.Find("NAME", myValue) 'Running fails on this line


        'myBindingSource.Filter = $"MyColumn LIKE '%{myValue}% OR MyOtherColumn LIKE '%{myValue}%'"

        Dim sourceRows = myBindingSource.Cast(Of DataRowView)().
                                 Where(Function(drv) CStr(drv("NOM")).Contains(myValue)).
                                 ToArray()

        For Each gridRow As DataGridViewRow In myDataGridView.Rows
            gridRow.Selected = sourceRows.Contains(DirectCast(gridRow.DataBoundItem, DataRowView))
        Next

    End Sub

This above is still work in progress but i'm a bit stuck getting a

System.ArgumentException : 'DataMember property' NAME 'could not be found in the DataSource.'

3

There are 3 best solutions below

11
Gabriel Stancu On BEST ANSWER

As jmcilhinney specified in the comments, your code has nothing to do with what you want to achieve. A first approach that comes to my mind (might not be the most efficient) would be to go row by row and column by column. Something like:

    Private Sub SearchCellValue(ByVal searchedValue As String)
        For Each row As DataGridViewRow In dgvSample.Rows
            For Each cell As DataGridViewCell In row.Cells
                If cell.Value IsNot DBNull.Value And cell.Value IsNot Nothing Then 'you also need to check for the "Nothing" value
                    If cell.Value.ToString().Equals(searchedValue) Then
                        dgvSample.SelectionMode = DataGridViewSelectionMode.CellSelect 'add this to only select a cell, not the entire row
                        cell.Selected = True
                        Exit Sub
                    End If
                End If
            Next
        Next
    End Sub

You could also search in the DataTable which holds the data for the DataGridView, get the corresponding indexes (row and column) and then select the corresponding cell in the DataGridView (as it is not quite the best practice to implement code logic directly on the UI which should be separated). Anyway, both approaches seem faster than looking directly in the database, as your approach suggests you intend to do.

You could also make this a Boolean Function and return a value (True if you found the value, False otherwise). This is just a sample code to guide you. Before jumping into complex stuff, make sure you read some documentation or at least follow some tutorials step by step.

2
jmcilhinney On

If you already have a populated grid then the code to search or filter has nothing at all to do with your database. What you should be doing is populating a DataTable, using either a data adapter or a data reader, binding that to a BindingSource and then binding that to your DataGridView, e.g.

myBindingSource.DataSource = myDataTable
myDataGridView.DataSource = myBindingSource

Searching or filtering is then using the BindingSource. To select the first record with a specific value in a specific column:

myBindingSource.Position = myBindingSource.Find("MyColumn", myValue)

To filter out rows that don't contain a partial value in any column:

myBindingSource.Filter = $"MyColumn LIKE '%{myValue}% OR MyOtherColumn LIKE '%{myValue}%'"

To select each row in the grid that matches those conditions without actually filtering:

Dim sourceRows = myBindingSource.Cast(Of DataRowView)().
                                 Where(Function(drv) CStr(drv("MyColumn")).Contains(myValue)).
                                 ToArray()

For Each gridRow As DataGridViewRow In myDataGridView.Rows
    gridRow.Selected = sourceRows.Contains(DirectCast(gridRow.DataBoundItem, DataRowView))
Next
0
FoxLeCredule On

Okay thanks to @Gabriel Stancu this worked for me :

    Private Sub btnSearch_Click(sender As Object, e As EventArgs) Handles btnSearch.Click
       
        Dim da As OleDbDataAdapter
        Dim ds As New DataSet
        Dim dt As New DataTable
        Dim dgv As New DataGridView

        ds.Tables.Add(dt)
        da = New OleDbDataAdapter("*", getConnexion) ' * here are my columns name
        Dim cmdBuilder As New OleDbCommandBuilder(da)
        cmdBuilder.QuotePrefix = "["
        cmdBuilder.QuoteSuffix = "]"
        da.Fill(dt) 'here is what i forgot
        dgv.DataSource = dt.DefaultView
        Dim searchedValue As String
        frmPrinc.dgv.DataSource = dt.DefaultView

        searchedValue = txtBoxName.Text
        'MsgBox(searchedValue) 'breakpoint to make sure the input string was correct
        For Each row As DataGridViewRow In frmPrinc.dgv.Rows
            For Each cell As DataGridViewCell In row.Cells
                If cell.Value IsNot DBNull.Value And cell.Value IsNot Nothing Then 'you also need to check for the "Nothing" value
                    If cell.Value.ToString().Equals(searchedValue) Then
                        frmPrinc.dgv.SelectionMode = DataGridViewSelectionMode.CellSelect 'add this to only select a cell, not the entire row
                        cell.Selected = True
                        Exit Sub
                    End If
                End If
            Next
        Next
    End Sub