Browse DevX
Sign up for e-mail newsletters from DevX


Build Better UIs with the Asynchronous Support in .NET 2.0  : Page 6

Good user interfaces let users keep working as seamlessly as possible while an application performs long background processing tasks. While .NET 1.0 certainly simplified the process of launching and managing multiple threads in Windows applications, you had to write much of the infrastructure yourself. In contrast, .NET 2.0 adds direct framework support for asynchronously fetching data and performing background tasks.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Searching the File Set
Finally, the code makes recursive calls to a method called SearchDirectoryRecursive to find the files that match and display the same.

' Process all the files in the current directory ' and get the count of files completed... filesdone = filesdone + _ directoryInfo.GetFiles().Length 'report progress as of now... worker.ReportProgress(CInt(( _ filesdone / totalfiles) * 100)) For Each file As FileInfo In directoryInfo.GetFiles() If file.Name.ToLower.IndexOf( _ search.ToLower) > 0 Then If Not worker.CancellationPending Then callback.Invoke(file) Threading.Thread.Sleep(10) Else e.Cancel = True Exit Sub End If End If Next

This method performs three important tasks. The first two lines retrieve the number of files to be searched in the current directory and call the interface to set the completed search percentage via the ReportProgress method. Note that it does this before actually processing the files, because doing such processing inside a For loop becomes complex. It's important not to over-inflate the importance of the progress complete feature, because it just adds overhead to the intended search functionality.

The code iterates through the files, comparing each file name against the search term that the user entered. When Indexof returns a value greater than zero the file is a match, and you need to display it in the search results. In each iteration, the loop checks to see if the client has cancelled the search task by checking the BackgroundWorker.CancellationPending property. If the property value is True, the client has requested cancellation of background task, so you need to set the eventArgs.Cancel property to True, which will cancel the task. If the user hasn't cancelled, then the code continues to the third important task: invoking the delegate using callback.Invoke (file), which passes the current file name to the method that displays search results in the ListView.

The DoWork event can complete in any of three ways:
  • The DoWork event code exits normally
  • An unhandled exception occurs
  • The user requests cancellation
No matter which way it completes, the event raises the RunWorkerCompleted event, where you can respond appropriately. For example, you can use the RunWorkerEventArgs.Cancelled property to check the user cancelled the operation, and if so return message confirming that the search was stopped. For errors, check the RunWorkerCancelEventArgs.Error property, which provides the exception details. The DoWork event also gives you access to the RunWorkerEventArgs.Result property, which can be any object that you need to return to the client.

Private Sub searchFolders_RunWorkerCompleted( _ ByVal sender As Object, _ ByVal e As System.ComponentModel. _ RunWorkerCompletedEventArgs) _ Handles searchFolders.RunWorkerCompleted Dim obj As System.ComponentModel. _ BackgroundWorker = CType(sender, _ System.ComponentModel.BackgroundWorker) If Not e.Error Is Nothing Then callComplete(e.Error.Message) ElseIf e.Cancelled Then callComplete("Stopped the search.") Else callComplete(e.Result) End If End Sub

The last important event is ProgressChanged. The method accepts a ProgressChangedEventArgs argument.

Private Sub searchFolders_ProgressChanged( _ ByVal sender As Object, ByVal e As _ System.ComponentModel.ProgressChangedEventArgs) _ Handles searchFolders.ProgressChanged progressChange = e.ProgressPercentage End Sub

The ProgressChangedEventArgs.ProgressPercentage property returns the percentage completed value (set in the DoWork event by calling the ReportProgress method). You can get access to the state via the ProgressChangedEventArgs.UserState property, which is an overloaded parameter of ReportProgress. This event fires each time the progress changes; therefore you should use a private variable to hold the current status. You inform the caller on the progress completion via a public Progress property.

So far, you've created a rich component that uses BackgroundWorker to search local fixed drives for matching file names using the BackGroundWorker class. The component can report search progress and lets users cancel the search task. It invokes delegate callbacks to registered methods passing information about matching files as FileInfo instances. Finally, it handles error messages.

Now take a look at the UI implementation.

The initial form load event creates an instance of the ContentSearcher.

Private objContentsearcher As ContentSearcher objContentsearcher = New ContentSearcher( _ AddressOf FileInformation, AddressOf ProcessComplete)

The first delegate references a method to display matching files, and the second report on the final search status—whether the user cancelled, an error occurred, or the process completed successfully.

You invoke the search by calling the ContentSearcher.SearchContent method.

Private Sub SearchContent(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles btnSearch.Click If Not isWorkerStarted Then Dim a As Integer = System.Threading.Thread. _ CurrentThread.ManagedThreadId Dim getWorker As _ System.ComponentModel.BackgroundWorker = _ objContentsearcher.Worker lstResult.Clear() lblProgress.Text = _ "Scanning started...please wait" pgBar.Value = 0 pgBar.Visible = True isWorkerStarted = True getWorker.RunWorkerAsync(txtSearch.Text) End If End Sub

The Boolean variable isWorkerStarted ensures that users can't perform more than one search at a time. Although you must write it each time, you should consider this technique as a standard feature; you need to implement this or a similar mechanism on the UI side whenever users or events launch asynchronous calls. The method initializes some control values and calls the ContentSearcher.RunWorkerAsync method, passing the search text as a parameter, for this simple example. Note that you could just as easily pass any other type of object, for example, an XML document that contained user preferences and additional search restrictions (e.g. search all files between 10/7/2004 and 10/8/2004) When called, the BackGroundWorker executes the DoWork event on a worker thread and the UI remains responsive, so you can display messages and search progress.

Thanks for your registration, follow us on our social networks to keep up-to-date