Use BackgroungWorker in VB.NET to build a list of Detached Databases on System

Experts,

I want to use BackgroundWorker to perform a recursive search that builds a table of Detached MSSQL tables.  The following is code that currently does this in the Foreground.  If I could move it to the Background I thought i would be able to get the operation dome more quickly.

This listing is the Button Click event:
    Private Sub Button_DatabaseSearch_Click(sender As Object, e As EventArgs) _
            Handles Button_DatabaseSearch.Click

        Dim myArguments As bgwArguments = New bgwArguments()
        Dim sDrive As String = Me.ComboBox_DriveList.Text & "\"
        Dim sFilter As String = "*.MDF"
        Dim bgw_DatabaseSearch As New ComponentModel.BackgroundWorker
        Me.Button_DatabaseSearch.Enabled = False
        Me.Button_DatabaseClear.Visible = False
        Me.Button_DatabaseClear.Enabled = False
        Me.Button_DatabaseCancel.Visible = True
        Me.Button_DatabaseCancel.Enabled = True
        Me.Cursor = Cursors.WaitCursor
        Me.DataGridView_ListDetachedDatabases.ColumnCount = 3
        Me.DataGridView_ListDetachedDatabases.Columns(0).Width = 100
        Me.DataGridView_ListDetachedDatabases.Columns(0).ReadOnly = True
        Me.DataGridView_ListDetachedDatabases.Columns(1).Width = 160
        Me.DataGridView_ListDetachedDatabases.Columns(1).ReadOnly = True
        Me.DataGridView_ListDetachedDatabases.Columns(2).Width = 300
        Me.DataGridView_ListDetachedDatabases.Columns(2).ReadOnly = True
        myArguments.sDrive = sDrive
        myArguments.sFilter = sFilter
        myArguments.dsDataSet = clsDataGridViewDataSource.dsDataGridViewDataSource
        bgw_DatabaseSearch.WorkerReportsProgress = True
        bgw_DatabaseSearch.WorkerSupportsCancellation = True
        clsDataGridViewDataSource.oclsDataSourceConnectionUtilities.subLocateDataSourceFiles("Initialize", sDrive, clsDataGridViewDataSource.dsDataGridViewDataSource)
        clsDataGridViewDataSource.oclsDataSourceConnectionUtilities.subLocateDataSourceFiles(sDrive, sFilter, clsDataGridViewDataSource.dsDataGridViewDataSource)
        Me.DataGridView_ListDetachedDatabases.DataSource = clsDataGridViewDataSource.dsDataGridViewDataSource.Tables(0)
        Me.TextBox_RecordCount.Text = clsDataGridViewDataSource.dsDataGridViewDataSource.Tables(0).Rows.Count.ToString
        Me.Cursor = Cursors.Default
        Me.Button_DatabaseCancel.Visible = False
        Me.Button_DatabaseCancel.Enabled = False
        Me.Button_DatabaseClear.Visible = True
        Me.Button_DatabaseClear.Enabled = True

    End Sub

Open in new window


the oclsDataSourceConnectuinUtilities class is:
Public Class clsDataGridViewDataSource_ConnectionMethod


    '****************************************************************************************************
    '*  DATABASE DATASOURCE FILE(S)                                                                     *
    '****************************************************************************************************

    '01.00.000  09/07/2012  11/03/2015  Initial release of this Function
    Public Sub subLocateDataSourceFiles _
           (
            ByVal sDrive As String,
            ByVal sFilter As String,
            ByVal dsDataSourceLink As DataSet
            )

        Dim o_dInfo As DirectoryInfo
        Dim o_sInfo As DirectoryInfo
        Dim o_fInfo As FileInfo
        Select Case sDrive
            Case "Initialize"
                Call subDataTable_Initialize(dsDataSourceLink)
            Case Else
                Try
                    o_dInfo = New DirectoryInfo(sDrive)
                    For Each o_fInfo In o_dInfo.GetFiles(sFilter)
                        Call subDataTable_RowAdd(o_fInfo, dsDataSourceLink)
                    Next o_fInfo
                    For Each o_sInfo In o_dInfo.GetDirectories
                        subLocateDataSourceFiles(o_sInfo.FullName, "*.MDF", dsDataSourceLink)
                    Next o_sInfo
                Catch ex As Exception
                End Try
        End Select

    End Sub

    '****************************************************************************************************
    '*  INITIALIZE DATATABLE                                                                            *
    '****************************************************************************************************

    '01.00.000  09/07/2012  11/03/2015  Initial release of this Subroutine
    Private Sub subDataTable_Initialize(ByRef dsDataSourceLink As DataSet)

        Dim dtDataSourceLink As DataTable = New DataTable
        Dim oCol0 As DataColumn = New DataColumn
        Dim oCol1 As DataColumn = New DataColumn
        Dim oCol2 As DataColumn = New DataColumn
        dtDataSourceLink.Columns.Add(oCol0)
        dtDataSourceLink.Columns.Add(oCol1)
        dtDataSourceLink.Columns.Add(oCol2)
        'Name columns
        dtDataSourceLink.Columns(0).ColumnName = "No."
        dtDataSourceLink.Columns(1).ColumnName = "DatabaseName"
        dtDataSourceLink.Columns(2).ColumnName = "Folder"
        If dsDataSourceLink.Tables.Count > 0 Then
            dsDataSourceLink.Clear()
        End If
        dsDataSourceLink.Tables.Add(dtDataSourceLink)

    End Sub

    '****************************************************************************************************
    '*  ADD NEW ROW TO DATATABLE                                                                        *
    '****************************************************************************************************

    '01.00.000  09/07/2012  11/03/2015  Initial release of this Subroutine
    Private Sub subDataTable_RowAdd _
            (
             ByVal o_fInfo As FileInfo,
             ByRef dsDataSourceLink As DataSet
             )

        Dim oRow As DataRow
        Dim dtTable As DataTable = New DataTable
        oRow = dsDataSourceLink.Tables(0).NewRow
        oRow.Item("No.") = dsDataSourceLink.Tables(0).Rows.Count + 1
        oRow.Item("DatabaseName") = o_fInfo.Name
        oRow.Item("Folder") = o_fInfo.DirectoryName
        dsDataSourceLink.Tables(0).Rows.Add(oRow)

    End Sub


End Class

Open in new window


I tried to impliment the BackgroungWorker (bgw_DataBaseSearch), but when I executed the DoWork subroutine from the Form It did not even start.

For the DoWork code block I moved the lines:
        clsDataGridViewDataSource.oclsDataSourceConnectionUtilities.subLocateDataSourceFiles("Initialize", sDrive, clsDataGridViewDataSource.dsDataGridViewDataSource)
        clsDataGridViewDataSource.oclsDataSourceConnectionUtilities.subLocateDataSourceFiles(sDrive, sFilter, clsDataGridViewDataSource.dsDataGridViewDataSource)
        Me.DataGridView_ListDetachedDatabases.DataSource = clsDataGridViewDataSource.dsDataGridViewDataSource.Tables(0)
        Me.TextBox_RecordCount.Text = clsDataGridViewDataSource.dsDataGridViewDataSource.Tables(0).Rows.Count.ToString

Open in new window


The sDrive and sFilter were defined in the DoWork sub as it was in its own thread, but I still didn't get it to work.

Please advise.  Thank You!
Peter AllenAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

it_saigeDeveloperCommented:
Threads (or background workers) are not used to *speed things up* unless less you are chunking the data across multiple threads (or background workers).  One form of this chunking is known as parallelism.  The primary reason for threading is user interface responsiveness whereas the form/dialog that start the process will remain responsive while the long running process runs in the background.

Another thing that we have to look at here are the controls which you have on your main form.  These cannot be directly accessed from a thread that did not create them; this is known as a CrossThreadException.  So this means that they cannot be directly called from your background worker *while delegates can be used to invoke a modification to the controls, the simple solution will probably be to use the background workers ProgressChanged event*.

-saige-
0
it_saigeDeveloperCommented:
That being stated, here is an example:

Form1.vb -
Imports System.ComponentModel
Imports System.Reflection

Public Class Form1
	ReadOnly people As New SortableBindingList(Of Person)

	Private Sub OnLoad(sender As Object, e As EventArgs) Handles MyBase.Load
		btnCancel.Enabled = False
		BindingSource1.DataSource = people
		DataGridView1.DataSource = BindingSource1
	End Sub

	Private Sub OnClick(sender As Object, e As EventArgs) Handles btnCancel.Click, btnStart.Click
		If TypeOf sender Is Button Then
			Dim btn As Button = DirectCast(sender, Button)
			If btn.Equals(btnStart) Then
				people.Clear()
				btn.Enabled = False
				BackgroundWorker1.RunWorkerAsync()
				btnCancel.Enabled = True
			ElseIf btn.Equals(btnCancel) Then
				btn.Enabled = False
				BackgroundWorker1.CancelAsync()
			End If
		End If
	End Sub

	Private Sub OnDoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
		If TypeOf sender Is BackgroundWorker Then
			Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)
			For i As Integer = 1 To 100 Step 1
				If Not worker.CancellationPending Then
					worker.ReportProgress(i, New PersonUserState() With {.Person = New Person() With {.ID = i, .Name = String.Format("Person{0}", i), .Birthdate = DateTime.Now.AddYears(-i), .IsWorking = i Mod 2 = 0}})
					' For the sake of making this a long running process 
					' we will sleep for 10 seconds before sending the next person
					Threading.Thread.Sleep(1000)
				Else
					Return
				End If
			Next
		End If
	End Sub

	Private Sub OnProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
		lbProgress.Text = e.ProgressPercentage.ToString() & "%"
		If Not e.UserState Is Nothing AndAlso TypeOf e.UserState Is PersonUserState Then
			Dim state As PersonUserState = TryCast(e.UserState, PersonUserState)
			If Not state Is Nothing Then
				people.ResetBindings()
				people.Add(state.Person)
			End If
		End If
	End Sub

	Private Sub OnWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
		If e.Cancelled = True Then
			lbProgress.Text = "Canceled!"
		ElseIf e.Error IsNot Nothing Then
			lbProgress.Text = "Error: " & e.Error.Message
		Else
			lbProgress.Text = "Done!"
		End If
		btnStart.Enabled = True
	End Sub

	Private Sub OnAddingNew(sender As Object, e As AddingNewEventArgs) Handles BindingSource1.AddingNew
		If DataGridView1.Rows.Count = BindingSource1.Count Then
			BindingSource1.RemoveAt(BindingSource1.Count - 1)
		End If
	End Sub
End Class

Class Person
	Public Property ID() As Integer
	Public Property Name() As String
	Public Property Birthdate() As DateTime
	Public Property IsWorking() As Boolean
End Class

Class PersonUserState
	Public Property Person() As Person
End Class

Class SortableBindingList(Of T)
	Inherits BindingList(Of T)
	Private Property IsSorted As Boolean
	Private Property SortDirection As ListSortDirection
	Private Property SortProperty As PropertyDescriptor

	Protected Overrides ReadOnly Property SupportsSortingCore() As Boolean
		Get
			Return True
		End Get
	End Property

	Protected Overrides ReadOnly Property SortDirectionCore() As ListSortDirection
		Get
			Return _SortDirection
		End Get
	End Property

	Protected Overrides ReadOnly Property SortPropertyCore() As PropertyDescriptor
		Get
			Return _SortProperty
		End Get
	End Property

	Protected Overrides Sub ApplySortCore(ByVal [property] As PropertyDescriptor, ByVal direction As ListSortDirection)
		Dim items As List(Of T) = TryCast(Me.Items, List(Of T))
		If items Is Nothing Then
			IsSorted = False
		Else
			Dim comparer As New PropertyCompare(Of T)([property].Name, direction)
			items.Sort(comparer)
			IsSorted = True
			SortDirection = direction
			SortProperty = [property]
		End If
		OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
	End Sub

	Protected Overrides ReadOnly Property IsSortedCore() As Boolean
		Get
			Return _IsSorted
		End Get
	End Property

	Protected Overrides Sub RemoveSortCore()
		_IsSorted = False
	End Sub

	Sub New(ByVal list As ICollection(Of T))
		MyBase.New(CType(list, IList(Of T)))
	End Sub

	Sub New()
		MyBase.New()
	End Sub

	Private Class PropertyCompare(Of T)
		Implements IComparer(Of T)

		Private Property PropertyInfo() As PropertyInfo
		Private Property SortDirection() As ListSortDirection
		Friend Sub New(ByVal [property] As String, ByVal direction As ListSortDirection)
			PropertyInfo = GetType(T).GetProperty([property])
			SortDirection = direction
		End Sub

		Friend Function Compare(ByVal x As T, ByVal y As T) As Integer Implements IComparer(Of T).Compare
			Return If(SortDirection = ListSortDirection.Ascending,
			  Comparer.[Default].Compare(PropertyInfo.GetValue(x, Nothing), PropertyInfo.GetValue(y, Nothing)),
			  Comparer.[Default].Compare(PropertyInfo.GetValue(y, Nothing), PropertyInfo.GetValue(x, Nothing)))
		End Function
	End Class
End Class

Open in new window


Form1.Designer.vb -
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
    Inherits System.Windows.Forms.Form

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
		Me.components = New System.ComponentModel.Container()
		Me.DataGridView1 = New System.Windows.Forms.DataGridView()
		Me.btnStart = New System.Windows.Forms.Button()
		Me.btnCancel = New System.Windows.Forms.Button()
		Me.BindingSource1 = New System.Windows.Forms.BindingSource(Me.components)
		Me.BackgroundWorker1 = New System.ComponentModel.BackgroundWorker()
		Me.lbProgress = New System.Windows.Forms.Label()
		CType(Me.DataGridView1, System.ComponentModel.ISupportInitialize).BeginInit()
		CType(Me.BindingSource1, System.ComponentModel.ISupportInitialize).BeginInit()
		Me.SuspendLayout()
		'
		'DataGridView1
		'
		Me.DataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
		Me.DataGridView1.Dock = System.Windows.Forms.DockStyle.Top
		Me.DataGridView1.Location = New System.Drawing.Point(0, 0)
		Me.DataGridView1.Name = "DataGridView1"
		Me.DataGridView1.Size = New System.Drawing.Size(575, 260)
		Me.DataGridView1.TabIndex = 0
		'
		'btnStart
		'
		Me.btnStart.Location = New System.Drawing.Point(407, 266)
		Me.btnStart.Name = "btnStart"
		Me.btnStart.Size = New System.Drawing.Size(75, 23)
		Me.btnStart.TabIndex = 1
		Me.btnStart.Text = "Start"
		Me.btnStart.UseVisualStyleBackColor = True
		'
		'btnCancel
		'
		Me.btnCancel.Location = New System.Drawing.Point(488, 266)
		Me.btnCancel.Name = "btnCancel"
		Me.btnCancel.Size = New System.Drawing.Size(75, 23)
		Me.btnCancel.TabIndex = 2
		Me.btnCancel.Text = "Cancel"
		Me.btnCancel.UseVisualStyleBackColor = True
		'
		'BindingSource1
		'
		'
		'BackgroundWorker1
		'
		Me.BackgroundWorker1.WorkerReportsProgress = True
		Me.BackgroundWorker1.WorkerSupportsCancellation = True
		'
		'lbProgress
		'
		Me.lbProgress.AutoSize = True
		Me.lbProgress.Location = New System.Drawing.Point(13, 276)
		Me.lbProgress.Name = "lbProgress"
		Me.lbProgress.Size = New System.Drawing.Size(56, 13)
		Me.lbProgress.TabIndex = 3
		Me.lbProgress.Text = "lbProgress"
		'
		'Form1
		'
		Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
		Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
		Me.ClientSize = New System.Drawing.Size(575, 301)
		Me.Controls.Add(Me.lbProgress)
		Me.Controls.Add(Me.btnCancel)
		Me.Controls.Add(Me.btnStart)
		Me.Controls.Add(Me.DataGridView1)
		Me.Name = "Form1"
		Me.Text = "Form1"
		CType(Me.DataGridView1, System.ComponentModel.ISupportInitialize).EndInit()
		CType(Me.BindingSource1, System.ComponentModel.ISupportInitialize).EndInit()
		Me.ResumeLayout(False)
		Me.PerformLayout()

	End Sub
	Friend WithEvents DataGridView1 As System.Windows.Forms.DataGridView
	Friend WithEvents btnStart As System.Windows.Forms.Button
	Friend WithEvents btnCancel As System.Windows.Forms.Button
	Friend WithEvents BindingSource1 As System.Windows.Forms.BindingSource
	Friend WithEvents BackgroundWorker1 As System.ComponentModel.BackgroundWorker
	Friend WithEvents lbProgress As System.Windows.Forms.Label

End Class

Open in new window

Which produces the following output -Initial loadAfter clicking start, we start filling the datagridIf we hit cancel (remember UI is responsive) the process stops.-saige-
0
Peter AllenAuthor Commented:
it_saige - Great example!

I have a couple of questions for you directly related to the example.

If the UI Form and BackgroungWorker are on different threats I can pass a class of Properties to the BackgroungWorker when it starts.  Correct?  (I think so) through the "RunWorkerAsync sub as a parameters.

Once the new thread starts can it access classes defined from the UI form or do these also need to be re-declared in the thread or (dimmed/cast) in the thread?

In the code I sent the BackgroundWorker was to create a table recursively searching for all *.MDF SSQL servers that were "Detached" from SQL Server and populate a dataset.  If the classes which were defined in the class that calls the UI itself then they are not accessible to the BackgroundWorker and must be passed.  If this is correct do I just define those instances in the BackgroundWorker?
0
Cloud Class® Course: Python 3 Fundamentals

This course will teach participants about installing and configuring Python, syntax, importing, statements, types, strings, booleans, files, lists, tuples, comprehensions, functions, and classes.

Peter AllenAuthor Commented:
I am going to use your example now to see...
0
it_saigeDeveloperCommented:
Yes, you can definitely pass a class of Properties to the BackgroundWorker through the overloaded RunWorkerAsync method that accepts an object as an argument (if you need to supply more than one argument you can either supply an array of objects or a specialized class that contains the objects you want to pass).

So in the test case above:
Imports System.ComponentModel
Imports System.Reflection

Public Class Form1
	ReadOnly people As New SortableBindingList(Of Person)

	Private Sub OnLoad(sender As Object, e As EventArgs) Handles MyBase.Load
		btnCancel.Enabled = False
		BindingSource1.DataSource = people
		DataGridView1.DataSource = BindingSource1
	End Sub

	Private Sub OnClick(sender As Object, e As EventArgs) Handles btnCancel.Click, btnStart.Click
		If TypeOf sender Is Button Then
			Dim btn As Button = DirectCast(sender, Button)
			If btn.Equals(btnStart) Then
				people.Clear()
				btn.Enabled = False
				BackgroundWorker1.RunWorkerAsync(New List(Of Person)(From i In Enumerable.Range(1, 100) Select New Person() With {.ID = i, .Name = String.Format("Person{0}", i), .Birthdate = DateTime.Now.AddYears(-i), .IsWorking = i Mod 2 = 0}))
				btnCancel.Enabled = True
			ElseIf btn.Equals(btnCancel) Then
				btn.Enabled = False
				BackgroundWorker1.CancelAsync()
			End If
		End If
	End Sub

	Private Sub OnDoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
		If TypeOf sender Is BackgroundWorker Then
			Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)
			If e.Argument IsNot Nothing AndAlso TypeOf e.Argument Is IEnumerable(Of Person) Then
				Dim people As IEnumerable(Of Person) = DirectCast(e.Argument, IEnumerable(Of Person))
				Dim counter As Integer = 1
				For Each [person] As Person In people
					If Not worker.CancellationPending Then
						worker.ReportProgress(counter / people.Count * 100.0F, New PersonUserState() With {.Person = person})
						counter = counter + 1
						Threading.Thread.Sleep(1000)
					Else
						e.Cancel = True
						Return
					End If
				Next
			End If
		End If
	End Sub

	Private Sub OnProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
		lbProgress.Text = e.ProgressPercentage.ToString() & "%"
		If Not e.UserState Is Nothing AndAlso TypeOf e.UserState Is PersonUserState Then
			Dim state As PersonUserState = TryCast(e.UserState, PersonUserState)
			If Not state Is Nothing Then
				people.ResetBindings()
				people.Add(state.Person)
			End If
		End If
	End Sub

	Private Sub OnWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
		If e.Cancelled = True Then
			lbProgress.Text = "Canceled!"
		ElseIf e.Error IsNot Nothing Then
			lbProgress.Text = "Error: " & e.Error.Message
		Else
			lbProgress.Text = "Done!"
		End If
		btnStart.Enabled = True
	End Sub

	Private Sub OnAddingNew(sender As Object, e As AddingNewEventArgs) Handles BindingSource1.AddingNew
		If DataGridView1.Rows.Count = BindingSource1.Count Then
			BindingSource1.RemoveAt(BindingSource1.Count - 1)
		End If
	End Sub
End Class

Class Person
	Public Property ID() As Integer
	Public Property Name() As String
	Public Property Birthdate() As DateTime
	Public Property IsWorking() As Boolean
End Class

Class PersonUserState
	Public Property Person() As Person
End Class

Class SortableBindingList(Of T)
	Inherits BindingList(Of T)
	Private Property IsSorted As Boolean
	Private Property SortDirection As ListSortDirection
	Private Property SortProperty As PropertyDescriptor

	Protected Overrides ReadOnly Property SupportsSortingCore() As Boolean
		Get
			Return True
		End Get
	End Property

	Protected Overrides ReadOnly Property SortDirectionCore() As ListSortDirection
		Get
			Return _SortDirection
		End Get
	End Property

	Protected Overrides ReadOnly Property SortPropertyCore() As PropertyDescriptor
		Get
			Return _SortProperty
		End Get
	End Property

	Protected Overrides Sub ApplySortCore(ByVal [property] As PropertyDescriptor, ByVal direction As ListSortDirection)
		Dim items As List(Of T) = TryCast(Me.Items, List(Of T))
		If items Is Nothing Then
			IsSorted = False
		Else
			Dim comparer As New PropertyCompare(Of T)([property].Name, direction)
			items.Sort(comparer)
			IsSorted = True
			SortDirection = direction
			SortProperty = [property]
		End If
		OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
	End Sub

	Protected Overrides ReadOnly Property IsSortedCore() As Boolean
		Get
			Return _IsSorted
		End Get
	End Property

	Protected Overrides Sub RemoveSortCore()
		_IsSorted = False
	End Sub

	Sub New(ByVal list As ICollection(Of T))
		MyBase.New(CType(list, IList(Of T)))
	End Sub

	Sub New()
		MyBase.New()
	End Sub

	Private Class PropertyCompare(Of T)
		Implements IComparer(Of T)

		Private Property PropertyInfo() As PropertyInfo
		Private Property SortDirection() As ListSortDirection
		Friend Sub New(ByVal [property] As String, ByVal direction As ListSortDirection)
			PropertyInfo = GetType(T).GetProperty([property])
			SortDirection = direction
		End Sub

		Friend Function Compare(ByVal x As T, ByVal y As T) As Integer Implements IComparer(Of T).Compare
			Return If(SortDirection = ListSortDirection.Ascending,
			  Comparer.[Default].Compare(PropertyInfo.GetValue(x, Nothing), PropertyInfo.GetValue(y, Nothing)),
			  Comparer.[Default].Compare(PropertyInfo.GetValue(y, Nothing), PropertyInfo.GetValue(x, Nothing)))
		End Function
	End Class
End Class

Open in new window


Would produce the same output as the original.

-saige-
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Peter AllenAuthor Commented:
Great answer.  I looked more closely and did finally understand.  Thank you.  I also noted that after the 'RunWorkerAsync()' that I had some code which I moved to the 'RunWorkerCompleted' sub.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic.NET

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.