如何实时显示添加到datagridview的记录

时间:2019-11-02 02:37:45

标签: vb.net datagridview

某位同事需要搜索我们的网络,而她的“文件资源管理器”搜索效果不佳。我迅速将这个应用程序放在一起,以便她进行搜索,并且效果很好。结果被写入到datagridview中,但是直到搜索完成才显示结果。

我希望datagridview在添加记录时显示它们,并允许她取消搜索。

我使用后台工作人员尝试刷新网格,但是一旦找到匹配项,代码就会停止运行。没有错误,它只是停止运行。

那么如何在网格继续搜索时更新网格?

Public dtResults As DataTable
Dim myDataSet As New DataSet
Dim myDataRow As DataRow

Dim colType As DataColumn
Dim colResult As DataColumn

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    dtResults = New DataTable()

    colType = New DataColumn("Type", Type.GetType("System.String"))
    colResult = New DataColumn("Search Result", Type.GetType("System.String"))

    dtResults.Columns.Add(colType)
    dtResults.Columns.Add(colResult)

    DataGridView1.DataSource = dtResults
    DataGridView1.Columns(1).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill


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

    btnSearch.Enabled = False
    sbStatusBar.Text = "Searching..."
    dtResults.Clear()

    BackgroundWorker1.RunWorkerAsync()

End Sub

Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

    LoopSubFolders(txtSearchLocation.Text)

End Sub
Public Sub LoopSubFolders(sLocation As String)

    Dim di = New DirectoryInfo(sLocation)
    Dim mySearchterm As String = LCase(txtSearchTerm.Text)
    Dim fiArr As FileInfo() = di.GetFiles()
    Dim sSearchTarget As String

    sbStatusBar.Text = "Searching " & sLocation
    'Search File names in 
    If cbFileNames.Checked = True Then
        For Each myFile In fiArr
            sSearchTarget = LCase(myFile.Name)
            If sSearchTarget.Contains(mySearchterm) Then

                myDataRow = dtResults.NewRow()
                myDataRow(dtResults.Columns(0)) = "File"
                myDataRow(dtResults.Columns(1)) = Path.Combine(sLocation, myFile.Name)
                dtResults.Rows.Add(myDataRow)

            End If
        Next
    End If

    For Each d In Directory.GetDirectories(sLocation)
        If cbFolderNames.Checked = True Then
            sSearchTarget = LCase(d)
            If sSearchTarget.Contains(mySearchterm) Then

                myDataRow = dtResults.NewRow()
                myDataRow(dtResults.Columns(0)) = "Folder"
                myDataRow(dtResults.Columns(1)) = d
                dtResults.Rows.Add(myDataRow)

            End If
        End If

        LoopSubFolders(d)

    Next

End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted

    btnSearch.Enabled = True
    sbStatusBar.Text = "Complete"
    DataGridView1.DataSource = Nothing
    DataGridView1.DataSource = dtResults
    DataGridView1.Columns(1).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill

End Sub

2 个答案:

答案 0 :(得分:1)

这是一个使用建议的ReportProgress方法和ProgressChanged事件的方法示例:

Private table As New DataTable

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    'Configure table here.

    DataGridView1.DataSource = table
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    'Setup UI here.

    'Note that you MUST pass in the TextBox data as you MUST NOT touch the UI directly on the secondary thread.
    BackgroundWorker1.RunWorkerAsync({TextBox1.Text, TextBox2.Text})
End Sub

Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    'Get the data passed in and separate it.
    Dim arguments = DirectCast(e.Argument, String())
    Dim folderPath = arguments(0)
    Dim searchTerm = arguments(1)

    SearchFileSystem(folderPath, searchTerm)
End Sub

Private Sub SearchFileSystem(folderPath As String, searchTerm As String)
    For Each filePath In Directory.GetFiles(folderPath)
        If filePath.IndexOf(searchTerm, StringComparison.InvariantCultureIgnoreCase) <> -1 Then
            'Update the UI on the UI thread.
            BackgroundWorker1.ReportProgress(0, {"File", filePath})
        End If
    Next

    For Each subfolderPath In Directory.GetDirectories(folderPath)
        If subfolderPath.IndexOf(searchTerm, StringComparison.InvariantCultureIgnoreCase) <> -1 Then
            'Update the UI on the UI thread.
            BackgroundWorker1.ReportProgress(0, {"Folder", subfolderPath})
        End If

        SearchFileSystem(subfolderPath, searchTerm)
    Next
End Sub

Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    'Get the data passed out and separate it.
    Dim data = DirectCast(e.UserState, String())

    'Update the UI.
    table.Rows.Add(data)
End Sub

请注意,切勿直接在DoWork事件处理程序或其中调用的方法中触摸UI。仅触摸UI线程上的UI。这意味着TextBoxes中的文本必须在调用RunWorkerAsync之前提取出来。您可以将Strings作为参数传递给eithewr,也可以将它们分配给字段并在任何线程上从那里访问它们。请勿访问UI线程以外的控件的成员。有时它会起作用,有时它看起来会起作用,但不能按预期运行,有时会崩溃您的应用程序。这样您就不必记住哪些特定情况会导致哪种结果,请完全避免此类情况。

我尚未测试此代码,所以不确定,但是在将新行添加到Refresh之后,您可能必须在网格或表单上调用DataTable

答案 1 :(得分:0)

变量

好吧,让我们从一些类级别的变量开始:

'Notice the enabled properties.
Private WithEvents BackgroundWorker1 As New BackgroundWorker With {.WorkerReportsProgress = True, .WorkerSupportsCancellation = True}
'To monitor the cancellation, set by the Cancel Button.
Private bgwCancel As Boolean = False
'The DGV source.
Private dtResults As New DataTable
'The start directory.
Private startDir As String
'The search keyword.
Private searchWord As String
'Whether to search the sub directories, from a check box for example.
Private includeSubDirectories As Boolean = True
'Whether to search the files, from another check box.
Private includeFiles As Boolean = True

构造函数

在这里准备您的DGV以及您需要的其他任何东西。

Sub New()
    dtResults.Columns.Add(New DataColumn("Type", Type.GetType("System.String")))
    dtResults.Columns.Add(New DataColumn("Search Result", Type.GetType("System.String")))

    DataGridView1.DataSource = dtResults
    DataGridView1.Columns(1).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill

    'Make sure you add the image column after binding the data source.
    Dim imgCol As New DataGridViewImageColumn(False)

    With imgCol
        .Image = Nothing
        .Name = "imgCol"
        .HeaderText = ""
        .Width = 50
        .DefaultCellStyle.NullValue = Nothing
    End With
    DataGridView1.Columns.Insert(0, imgCol)
End Sub

迭代器

现在,让我们编写搜索例程。我可以通过Iterator函数来做到这一点:

Private Iterator Function IterateFolders(startDir As String, includeFiles As Boolean, includeSubDir As Boolean) As IEnumerable(Of String)
    For Each dirName In IO.Directory.EnumerateDirectories(startDir)
        Yield dirName

        If includeFiles Then
            For Each fileName In IO.Directory.EnumerateFiles(startDir)
                Yield fileName
            Next
        End If

        If includeSubDir Then
            For Each subDir In IterateFolders(dirName, includeFiles, includeSubDir)
                Yield subDir
            Next
        End If
    Next
End Function

主线程更新器

由工作线程调用的例程,用于更新DataTable和属于主线程的任何控件:

Private Sub AddSearchResult(path As String)
    If InvokeRequired Then
        Invoke(Sub() AddSearchResult(path))
    Else
        dtResults.Rows.Add(If(IO.File.Exists(path), "File", "Folder"), path)
        sbStatusBar.Text = $"Searching {path}" 
    End If
End Sub

开始

在开始按钮的click事件中,进行必要的验证,将值分配给它们的变量,然后启动背景工作程序:

If String.IsNullOrEmpty(txtSearchKeyword.Text) Then Return
If String.IsNullOrEmpty(txtSearchLocation.Text) Then Return

bgwCancel = False
dtResults.Rows.Clear()
startDir = txtSearchLocation.Text
searchWord = txtSearchKeyword.Text.ToLower
includeSubDirectories = chkIncludeSubDirs.Checked
includeFiles = chkFiles.Checked
btnSearch.Enabled = False
sbStatusBar.Text = "Searching..."
BackgroundWorker1.RunWorkerAsync()

取消

要取消搜索,我假设在取消按钮的click事件中, True bgwCancel变量:

bgwCancel = True

BackgroundWorker-DoWork

Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    For Each item As String In IterateFolders(startDir, includeFiles, includeSubDirectories)
        If bgwCancel Then
            BackgroundWorker1.CancelAsync()
            Return
        End If
        If item.ToLower.Contains(searchWord) Then
            AddSearchResult(item)
        End If
        Threading.Thread.Sleep(100)
    Next
End Sub

请注意,优良作法是通过该线程的Sleep(ms)方法为冗长的例程提供呼吸。

BackgroundWorker-ProgressChanged

我认为您在这里不需要它。

BackgroundWorker-已完成RunWorker

Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    If bgwCancel Then
        sbStatusBar.Text = "Canceled!"
        MessageBox.Show("Canceled by you!")
    ElseIf e.Error IsNot Nothing Then
        sbStatusBar.Text = "Error!"
        MessageBox.Show(e.Error.Message)
    Else
        sbStatusBar.Text = "Complete"

        'YOU DO NOT NEED TO DO THIS. Remove the following
        'DataGridView1.DataSource = Nothing
        'DataGridView1.DataSource = dtResults
        'DataGridView1.Columns(1).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
    End If
    btnSearch.Enabled = True
End Sub

图片列

按以下方式处理DGV的行收藏事件:

Private Sub DataGridView1_RowsAdded(sender As Object, e As DataGridViewRowsAddedEventArgs) Handles DataGridView1.RowsAdded
    If DataGridView1.Columns.Count < 3 Then Return

    'if you want to get rid of the default x image.
    If e.RowIndex = 0 Then
        DataGridView1.Rows(e.RowIndex).Cells("imgCol").Value = Nothing
    End If

    Dim path As String = DataGridView1.Rows(e.RowIndex).Cells(2).Value?.ToString

    If Not String.IsNullOrEmpty(path) Then
        If IO.File.Exists(path) Then
            DataGridView1.Rows(e.RowIndex).Cells("imgCol").Value = Icon.ExtractAssociatedIcon(path).ToBitmap
        Else
            DataGridView1.Rows(e.RowIndex).Cells("imgCol").Value = My.Resources.Folder
        End If
    End If
End Sub

其中 My.Resources.Folder 是您选择的文件夹条目的图标文件。

祝你好运。