Как показать записи, добавленные в datagridview в режиме реального времени - PullRequest
0 голосов
/ 02 ноября 2019

Сотрудник должен выполнить поиск в нашей сети, и ее поиск в Проводнике файлов не работает должным образом. Я быстро собрал это приложение, чтобы она могла искать, и оно работает хорошо. Результаты записываются в сетку данных, но результаты не отображаются до тех пор, пока поиск не будет завершен.

Я хотел бы, чтобы сетка данных показывала записи по мере их добавления и позволяла ей отменить поиск, если она этого захочет.

Используя фонового работника, я попытался обновить сетку, но как только она нашла совпадение, код перестает работать. Нет никаких ошибок, он просто перестает работать.

Так как я могу заставить сетку обновляться при продолжении поиска?

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 ]

1 голос
/ 02 ноября 2019

Вот пример того, как вы можете сделать это, используя предложенный метод 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или метод, вызванный из него. ТОЛЬКО касайтесь пользовательского интерфейса в потоке пользовательского интерфейса. Это означает, что текст в вашем TextBoxes должен быть извлечен ДО вызова RunWorkerAsync. Вы можете либо передать Strings в качестве аргументов, либо назначить их полям и получить к ним доступ в любом потоке. НИКОГДА не получайте доступ к элементу управления, кроме потока пользовательского интерфейса. Иногда это будет работать, иногда это будет работать, но не будет работать так, как задумано, и иногда это приведет к сбою вашего приложения. Чтобы вам не приходилось помнить, какие конкретные сценарии вызывают какой-либо результат, избегайте такого сценария в целом.

Я не проверял этот код, поэтому я не уверен, но вам, возможно, придется вызвать Refresh насетка или форма после добавления новой строки в DataTable.

0 голосов
/ 02 ноября 2019

Переменные

Хорошо, давайте начнем с вершины с некоторыми переменными уровня класса:

'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

Start

В событии нажатия кнопки запуска выполните необходимые проверки, назначьтезначения к их переменным, и запустить фоновый рабочий:

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()

Отмена

Чтобы отменить поиск, в случае нажатия кнопки отмены я предполагаю, 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 - RunWorkerCompleted

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

Столбец изображения

Обрабатывать событие RowsAdded 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 - файл значков на ваш выбордля записей в папке.

Удачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...