?
Solved

BackgroundWorker implimentation assistance

Posted on 2008-10-05
7
Medium Priority
?
553 Views
Last Modified: 2013-11-27
Hello Experts,

I have been working with BackgroundWorker in my latest project thus far.  What I want to do is to be able to process a Text Deliniated file based upon the input from the user.  The import process can take several minute depending on the size of the file so I want to be sure that the user's UI doesn't get messed up.

When the process starts the file, file type and deliniator are entered by the user.  When they click the Preview button the background task starts.  To keep the user informed I have a ProgressBar that will be updated as the data file is processed.  The Status text will indicate "Processing..." with the percentage and the bar gets updated accordingly.

I have successfully setup the application ti import the text fkle and place it into a DataTable that is used as the DataSource for the DataGridView in the foreground.  I think I have properly written the code to do this in the background, but the ProgressBar does not update with the current processing point.  The other problem is that the DataGridView doesn't display the DataTable.  Hmmmmm?????

So I need an extra pare of eyes to look at the attached code please.  So I have two basic questions:

1.  Could you look at the code and assist me with fixing the 'ProgressBar' updates and DataGridView update of the DataSource by the DataTable?

2. Is there a way I could setup the BackgroundWorker in a class that would accept a 'Process' to be done, update a ProgressBar and then return the results whem completed in a modular way?  This way I would not have to keep writing code do handle a backgroun process?

Thanks so very much for the assistance!!!
Imports Microsoft.VisualBasic.FileIO
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.IO
Imports System.Text
Imports System.Threading
Imports System.Windows.Forms
 
 
Public Class Form2
    Inherits System.Windows.Forms.Form
 
    Private WithEvents bgw_ImportDataFile As BackgroundWorker
    Private sFile As String = Nothing
    Private TmpTable As DataTable = Nothing
 
 
 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 
        'Load table data
        Me.taDBStructureListDeliniator.Fill(Me.DsDatabaseStructureLists.tblDBStructureListDeliniator)
        Me.taDBStructureListFormatType.Fill(Me.DsDatabaseStructureLists.tblDBStructureListFormatType)
 
        'Set default Options and Values
        Me.TextBox_SourceFile.Text = "D:\Documents and Settings\Peter Allen\My Documents\Developement\Microsoft Access 2003\Integrated Components and Maintenance Tracking System\Table Data\AssetCenter\SEIDNAME.TXT"
        Me.ComboBox_FileFormat.Text = "DELINIATED"
        Me.ComboBox_FileDeliniator.Text = "Coma"
        Me.DataGridView1.Tag = "Default"
        Me.DataGridView2.Tag = "Default"
        Me.TabControl_FileSpecifications.SelectedTab = TabPage_FileOptions
        Me.StatusStrip_ProgressBar.Value = 0
        Me.StatusStrip_FileStatusText.Text = "Ready"
        Me.StatusStrip_ProgressLabel.Visible = False
        Me.StatusStrip_ProgressBar.Visible = False
        subStateButtons("Default")
 
        subDataGridView_Initialize()
        subDataGridView_Header()
        subDataGridView_DataRow()
 
    End Sub
 
    Private Sub subDataGridView_Initialize()
 
        Select Case Me.DataGridView1.Tag
            Case "Default"
                With Me.DataGridView1
                    .AllowUserToAddRows = False
                    .AllowUserToDeleteRows = False
                    .AllowUserToResizeRows = False
                    .SelectionMode = DataGridViewSelectionMode.FullRowSelect
                    .MultiSelect = False
                    .Dock = DockStyle.None
                End With
        End Select
 
        Select Case Me.DataGridView2.Tag
            Case "Default"
                With Me.DataGridView2
                    .ColumnCount = 3
                    .RowCount = 2
                    .AllowUserToAddRows = False
                    .AllowUserToDeleteRows = False
                    .AllowUserToResizeRows = False
                    .SelectionMode = DataGridViewSelectionMode.FullRowSelect
                    .MultiSelect = False
                    .Dock = DockStyle.None
                End With
        End Select
 
    End Sub
 
    Private Sub subDataGridView_Header()
 
        'DataGridView Heading Style
        With DataGridView2.ColumnHeadersDefaultCellStyle
            .BackColor = Color.Navy
            .ForeColor = Color.White
            .Font = New Font(DataGridView2.Font, FontStyle.Bold)
        End With
 
        'Column Heading Style Attributes
        With DataGridView2
            .Name = "DataGridView1"
            .AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders
            .CellBorderStyle = DataGridViewCellBorderStyle.Single
            .ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single
            .GridColor = Color.Black
            .RowHeadersVisible = False
        End With
 
        'Column Heading Names
        Me.DataGridView2.Columns(0).Name = "Field Name"
        Me.DataGridView2.Columns(1).Name = "Field Type"
        Me.DataGridView2.Columns(2).Name = "Include"
    End Sub
    
 
    Private Sub subDataGridView_DataRow()
 
        Dim iRow As Integer
        Dim iCol As Integer
 
        'Set the Column data
        For iCol = 1 To Me.DataGridView2.ColumnCount
            For iRow = 1 To Me.DataGridView2.RowCount
                Me.DataGridView2.Item(iCol - 1, iRow - 1).Value = "Field"
            Next iRow
        Next iCol
 
    End Sub
 
    <STAThreadAttribute()> _
    Public Shared Sub Main()
        Application.EnableVisualStyles()
        Application.Run(New Form2())
 
    End Sub
 
    Private Sub Button_Preview_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_Preview.Click
 
        Dim o_fncImportDataTable_Processer As New clsDataAccess_ImportDataFile
        Dim bgw_ImportDataFile = New System.ComponentModel.BackgroundWorker
 
        sFile = Me.TextBox_SourceFile.Text
        Me.TabControl_FileSpecifications.SelectedTab = TabPage_FileView
        Me.StatusStrip_ProgressLabel.Visible = True
        Me.StatusStrip_ProgressBar.Visible = True
        subStateButtons("Default")
 
        'Update Tab: View
        Me.DataGridView1.DataSource = ""
        Me.StatusStrip_FileStatusText.Text = "Processing..."
        Me.Refresh()
 
        o_fncImportDataTable_Processer.fncImportDataTable_RowCount(sFile)
        Me.StatusStrip_ProgressBar.Maximum = 100
        Me.StatusStrip_ProgressBar.Minimum = 0
        Me.StatusStrip_ProgressBar.Step = 10
        Me.TextBox_RowCount.Text = Str(o_fncImportDataTable_Processer.p_RowCount)
        Me.TextBox_FieldCount.Text = Str(o_fncImportDataTable_Processer.p_ColCount)
 
        bgw_ImportDataFile.WorkerReportsProgress = True
        bgw_ImportDataFile.WorkerSupportsCancellation = True
        bgw_ImportDataFile.RunWorkerAsync(TmpTable)
 
        'Me.DataGridView1.DataSource = o_fncImportDataTable_Processer.fncImportDataTable_Processer(sFile)
        For iCol As Integer = 1 To DataGridView1.ColumnCount
            Me.DataGridView1.AutoResizeColumn(iCol - 1)
            Me.DataGridView1.Columns(iCol - 1).ReadOnly = True
        Next iCol
 
        'Update Tab: Structure
        Me.DataGridView2.ColumnCount = 3
        Me.DataGridView2.RowCount = o_fncImportDataTable_Processer.p_ColCount + 1
        subDataGridView_Header()
        subDataGridView_DataRow()
 
        Me.StatusStrip_FileStatusText.Text = "Ready"
        Me.Refresh()
 
    End Sub
 
    Private Sub Button_Close_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_Close.Click
 
        Me.Close()
 
    End Sub
 
    Private Sub subStateButtons(ByVal sAction As String)
 
        If Me.TextBox_SourceFile.TextLength > 0 Then
            Me.Button_Preview.Enabled = True
        Else
            Me.Button_Preview.Enabled = False
        End If
 
    End Sub
 
    Private Sub bgw_ImportDataFile_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgw_ImportDataFile.DoWork
 
        TmpTable = DirectCast(e.Argument, DataTable)
        Dim o_fncImportDataTable_Processer As New clsDataAccess_ImportDataFile
        TmpTable = o_fncImportDataTable_Processer.fncImportDataTable_Processer(sFile)
        e.Result = TmpTable
        'For Value As Integer = 0 To 100
        'If bgw_ImportDataFile.CancellationPending Then
        'Exit For
        'End If
        'bgw_ImportDataFile.ReportProgress(Value, ListText)
        'Threading.Thread.Sleep(100)
        'Next Value
 
    End Sub
 
    Private Sub bgw_ImportDataFile_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bgw_ImportDataFile.ProgressChanged
 
        StatusStrip_ProgressBar.Value = e.ProgressPercentage
 
    End Sub
 
    Private Sub Button_Cancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_Cancel.Click
 
        bgw_ImportDataFile.CancelAsync()
 
    End Sub
 
    Private Sub bgw_ImportDataFile_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgw_ImportDataFile.RunWorkerCompleted
 
        Me.DataGridView1.DataSource = e.Result
        Me.StatusStrip_ProgressLabel.Visible = False
        Me.StatusStrip_ProgressBar.Visible = False
        Me.StatusStrip_ProgressBar.Value = 0
 
    End Sub
 
End Class
 
Public Class clsDataAccess_ImportDataFile
 
    Dim _RowCount As Integer
    Dim _ColCount As Integer
 
    Public Property p_RowCount() As Integer
        Get
            Return _RowCount
        End Get
        Set(ByVal value As Integer)
            _RowCount = value
        End Set
    End Property
 
    Public Property p_ColCount() As Integer
        Get
            Return _ColCount
        End Get
        Set(ByVal value As Integer)
            _ColCount = value
        End Set
    End Property
 
 
    Public Function fncImportDataTable_Processer(ByVal FileName As String) As DataTable
 
        ' Initialize the return values
        Dim list As New List(Of String())
        Dim table As DataTable = Nothing
        Using parser As New TextFieldParser(FileName)
            ' Setup the comma-delimited file parser.
            parser.TextFieldType = FieldType.Delimited
            subImportDataTable_Deliniator(parser)
            parser.HasFieldsEnclosedInQuotes = False
            While Not parser.EndOfData
                Try
                    ' Read the comma-delimited text as fields into a string array.
                    Dim input As String() = parser.ReadFields()
                    If table Is Nothing Then
                        table = Me.fncImportDataTable_Headings(Path.GetFileName(FileName), input)
                    End If
                    If input(0).Trim <> "" And input(0) <> "******" Then
                        Me.fncImportDataTable_DataRow(table, input)
                        'Form2.StatusStrip_ProgressBar.PerformStep()
                    Else
                        'Else send to Error table
                    End If
                Catch ex As MalformedLineException
                    ' Ignore invalid lines.
                End Try
            End While
        End Using
        Return table
 
    End Function
 
    Private Function fncImportDataTable_Headings(ByVal name As String, ByVal input As String()) As DataTable
 
        Dim table As New DataTable(name)
        For index As Integer = 1 To input.Length
            table.Columns.Add("Field" & index)
        Next index
        Return table
 
    End Function
 
    Private Sub fncImportDataTable_DataRow(ByVal table As DataTable, ByVal input As String())
 
        Dim row As DataRow = table.NewRow()
        For index As Integer = 0 To table.Columns.Count - 1
            If index < input.Length Then
                row(index) = input(index)
            End If
        Next index
        table.Rows.Add(row)
 
    End Sub
 
    Public Function fncImportDataTable_RowCount(ByVal Filename As String) As Integer
 
        ' Initialize the return values
        Dim list As New List(Of String())
        Dim table As DataTable = Nothing
        Dim iRows As Integer = 0
        Dim iCols As Integer = 0
        Using parser As New TextFieldParser(Filename)
            ' Setup the comma-delimited file parser.
            parser.TextFieldType = FieldType.Delimited
            subImportDataTable_Deliniator(parser)
            parser.HasFieldsEnclosedInQuotes = False
            While Not parser.EndOfData
                Try
                    ' Read the comma-delimited text as fields into a string array.
                    Dim input As String() = parser.ReadFields()
                    iRows = iRows + 1
                    If iRows = 1 Then
                        iCols = parser.ReadFields.Count
                    End If
                Catch ex As MalformedLineException
                    ' Ignore invalid lines.
                End Try
            End While
        End Using
        p_RowCount = iRows
        p_ColCount = iCols
 
    End Function
 
    Private Sub subImportDataTable_Deliniator(ByRef parser)
 
        Select Case Form2.ComboBox_FileDeliniator.Text
            Case "Coma"
                parser.Delimiters = New String() {","}
            Case "Semi-Colin"
                parser.Delimiters = New String() {";"}
        End Select
    End Sub
    
End Class

Open in new window

0
Comment
Question by:Peter Allen
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
7 Comments
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 22647058
The problem is right here:

    Private Sub Button_Preview_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_Preview.Click
 
        ...
        Dim bgw_ImportDataFile = New System.ComponentModel.BackgroundWorker

By placing "Dim" in front of "bgw_ImportDataFile" you have created a LOCAL variable in Button_Preview_Click().  This local variable is actually HIDING the previously declared variable you had placed at the Form level.  The Form level BackgroundWorker variable never actually gets used!  This is why it never traps any of the events...because it doesn't point to anything....

Removing the "Dim" should fix the problem:

        bgw_ImportDataFile = New System.ComponentModel.BackgroundWorker

0
 

Author Comment

by:Peter Allen
ID: 22650734
Yes.  Darn that one word!  Thank you for that.  I ran the code and I was able to get further.  Now I am looking at the Delimiters procedure.  I had referenced a control on the form to determine what the Delimiter was.  This was not getting passed to the BackgroundWorker.  So I added a new private string called SDelimiter which does pass the text to determine the proper deliniation character.

In the following sub-routine...

    Private Sub bgw_ImportDataFile_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgw_ImportDataFile.RunWorkerCompleted

        Me.DataGridView1.DataSource = TmpTable
        Me.StatusStrip_ProgressLabel.Visible = False
        Me.StatusStrip_ProgressBar.Visible = False
        Me.StatusStrip_ProgressBar.Value = 0

    End Sub

The DataGridView1.DataSource was changed to accept the correct DataTable instead of e.Result.  My goofup.  The process works very well, Thank you for the solution.  I do have those two additional questions for you, though.

What about how to report the progress of fncImportDataTable_Processer?

I know that I have to call ProgressChanged, but am unclear as to how to report a percentage back to the UI to a ProgressBar.

And what about the ability to set up a BackgroundWorker in such a way as to be able to call it to perform these actions without writing it more then one time.  Let's say in a class.

0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 22651456
"What about how to report the progress of fncImportDataTable_Processer?"

Well...since you have put the "meat" into a separate class you would have to do either one of two things:

(a) Pass the BackgroundWorker into the Class so that it can call the ReportProgess() method directly.
(b) Make the clsDataAccess_ImportDataFile Class Raise its own Progress events that the BackgroundWorker subscribes to.  Then it will relay that Progress by raising its own Progress event in turn.

To get the actual progress you need to base your TextFieldParser on a Stream instead of passing it a FileName directly.  This way you can query the underlying stream to determine how far along it is compared to the total length of the file.  This approach isn't perfect, though, because .Net Streams actually read ahead and buffer the data for you...so it may say 100% a little before it is actually done.

Here is a small snippet that demonstrates the concept:
    Private Sub Button1_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim fs As New FileStream("c:\someFile.txt", FileMode.Open)
        Dim tfp As New TextFieldParser(fs)
        tfp.Delimiters = New String() {","}
        Dim values() As String
        Dim percent As Decimal
        While Not tfp.EndOfData
            values = tfp.ReadFields
            percent = fs.Position / fs.Length * 100
 
            Label1.Text = percent.ToString("0") & "%"
            Application.DoEvents()
 
        End While
        tfp.Close()
 
        MessageBox.Show("Done")
    End Sub

Open in new window

0
NFR key for Veeam Backup for Microsoft Office 365

Veeam is happy to provide a free NFR license (for 1 year, up to 10 users). This license allows for the non‑production use of Veeam Backup for Microsoft Office 365 in your home lab without any feature limitations.

 

Author Comment

by:Peter Allen
ID: 22653306
I was looking at the code ip point B, but I think Point A is a better solution.  I am stuck, though on how to pass the function into the class.   ...

Thank you so much for the assistance.
0
 
LVL 86

Accepted Solution

by:
Mike Tomlinson earned 2000 total points
ID: 22653460
Ok...first, in your Class, add a variable to hold the BackgroundWorker reference.  Then modify the Constructor to accept the external parameter and assign it to the internal variable:

    Public Class clsDataAccess_ImportDataFile

        ... rest of your already existing class ...

        Private bgw As System.ComponentModel.BackgroundWorker = Nothing

        Public Sub New(ByVal bgw As System.ComponentModel.BackgroundWorker)
            Me.bgw = bgw
        End Sub

        ... rest of your already existing class ...

    End Class

Then, in the "DoWork" method you would pass in the BackgroundWorker control when you create your class:

    Private Sub bgw_ImportDataFile_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgw_ImportDataFile.DoWork
        TmpTable = DirectCast(e.Argument, DataTable)
        Dim o_fncImportDataTable_Processer As New clsDataAccess_ImportDataFile(Me.bgw_ImportDataFile)

Finally, in your class you use your reference to report progress:

    Public Class clsDataAccess_ImportDataFile

        ... rest of your already existing class ...

        Private bgw As System.ComponentModel.BackgroundWorker = Nothing

        Public Sub New(ByVal bgw As System.ComponentModel.BackgroundWorker)
            Me.bgw = bgw
        End Sub

        Public Function fncImportDataTable_Processer(ByVal FileName As String) As DataTable

            ... rest of your already existing function ...

            bgw.ReportProgress(someValue / someTotal * 100)

            ... rest of your already existing function ...

        End Function

        ... rest of your already existing class ...

    End Class
0
 

Author Comment

by:Peter Allen
ID: 22653833
I will get back with you on thos becaise pne pf the values I need is not getting inserted.  I will let you know tomorrow.  Thank you so very much!
0
 

Author Closing Comment

by:Peter Allen
ID: 31503229
Idle Mind,

I accept your solution   THANK YOU!!  The solution works perfectly.  For the percentage I calculated the row count in the text file first before the actual import.  The ProgressBar updates as it should now.  I just had to add 1 to the row count to preventing an unhandled exception.

Thanks again!
0

Featured Post

Prepare for your VMware VCP6-DCV exam.

Josh Coen and Jason Langer have prepared the latest edition of VCP study guide. Both authors have been working in the IT field for more than a decade, and both hold VMware certifications. This 163-page guide covers all 10 of the exam blueprint sections.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Dependencies in Software Design In software development, the idea of dependencies (http://en.wikipedia.org/wiki/Coupling_%28computer_programming%29) is an issue of some importance. This article seeks to explain what dependencies are and where they …
"Disruption" is the most feared word for C-level executives these days. They agonize over their industry being disturbed by another player - most likely by startups.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question