Welcome to Atlanta .NET Regular Guys Sign in | Join | Help

Adventures in Delegates

My previous post talks about adding an animated gif to a Winform app to act as a progress indicator. Well, I got that working and found that when my single threaded app makes the animated gif visible, then starts to do a bunch of work - the animated gif isn't animated. It pauses and waits for the work to be done.

So now I figured I needed to offload the bulk of this work to another thread. Fortunately, VB.Net and the delegates makes this a pretty easy task. Let's take a look:

I've got a form class which reacts to a button push from the user. A function on the form gets called to generate a list of valid storage bins and bind this list to a combo box:

Public Sub PopulateBinsList()
' calculate capacity
Dim BinCapacity As Integer
If (IncludeRecallsCheckBox.Checked) Then
BinCapacity = EnteredItem.WarehouseRecords(0).onHand + _
EnteredItem.WarehouseRecords(0).onOrder + _
EnteredItem.InRecall
Else
BinCapacity = EnteredItem.WarehouseRecords(0).onHand + _
EnteredItem.WarehouseRecords(0).onOrder
End If
' get valid bins
Dim saveCursor As Cursor = Windows.Forms.Cursor.Current
Windows.Forms.Cursor.Current = Cursors.WaitCursor
Dim ValidBins As BinsCollection = New BinsCollection
cboAssignBin.DataSource = ValidBins.GetValidBins(EnteredItem.AlphaCfg, _
WarehouseNumber, _
BinCapacity)
cboAssignBin.DisplayMember =
"BinNumber"
' this takes a while
UpdateBinInfoLabels(
DirectCast(cboAssignBin.SelectedItem, Bin))
Windows.Forms.Cursor.Current = saveCursor

End
Sub

The offensive line is the part where I call ValidBins.GetValidBins - it simply takes FOREVER! This is an ideal candidate for moving off to another thread. First I changed the code in my forms class to create a new object which would start the process of building a list of valid storage bins. I also needed a new function which would execute once the work had been done. This is the code I ended up with:

Public Sub PopulateBinsList()
' calculate capacity
Dim BinCapacity As Integer
If (IncludeRecallsCheckBox.Checked) Then
BinCapacity = EnteredItem.WarehouseRecords(0).onHand + _
EnteredItem.WarehouseRecords(0).onOrder + _
EnteredItem.InRecall
Else
BinCapacity = EnteredItem.WarehouseRecords(0).onHand + _
EnteredItem.WarehouseRecords(0).onOrder
End If
' get valid bins
Dim saveCursor As Cursor = Windows.Forms.Cursor.Current
Windows.Forms.Cursor.Current = Cursors.WaitCursor
Dim BinFiller As New BinListFiller(WarehouseNumber, _
EnteredItem, _
BinCapacity, _
AddressOf DatabindBinsList)
BinFiller.BuildValidBinsCollection()
End Sub

Public Sub DatabindBinsList(ByRef validBins As BinsCollection)
cboAssignBin.DataSource = validBins
cboAssignBin.DisplayMember =
"BinNumber"
UpdateBinInfoLabels(
DirectCast(cboAssignBin.SelectedItem, Bin))
Windows.Forms.Cursor.Current = Cursors.Default
End Sub

Now I need to take my new DatabindBinsList and create a delegate for it. A delegate is essentially a function pointer. It allows me to create a function "signature" that I can pass around my application and use. Delegates are defined outside the class (up where you define the IMPORTS):

Public Delegate Sub FillBinsCallback(ByRef validBins As BinsCollection)

Notice how the delegate FillBinsCallback takes the same parameters and has the same returns as DatabindBinsList (the name of the parameter isn't important, the type and ByRef/ByVal is). This means that I can point my point my delegate to this function. This will become a little clearer as I go on. Now that I've changed my form class to expect another object to asynchronously do the work and call the DatabindBinsList subroutine when complete, I need to build that class:


Public
Class BinListFiller

Private _warehouseNumber As Integer
Private _reqCapacity As Integer
Private _binsList As BinsCollection
Private _targetItem As Item
Private _callbackMethod As FillBinsCallback

''' <summary>
''' Creates a new instance of BinListFiller
''' </summary>
''' <param name="warehouseNumber"></param>
''' <param name="targetItem"></param>
''' <param name="requiredCapacity"></param>
''' <param name="callbackMethod"></param>
Public Sub New(ByVal warehouseNumber As Integer, _
ByVal targetItem As Item, _
ByVal requiredCapacity As Integer, _
ByVal callbackMethod As FillBinsCallback)
MyBase.New()
_warehouseNumber = warehouseNumber
_reqCapacity = requiredCapacity
_targetItem = targetItem
_callbackMethod = callbackMethod
End Sub

Public Sub BuildValidBinsCollection()
Dim ValidBins As BinsCollection = New BinsCollection
ValidBins = ValidBins.GetValidBins(_targetItem.AlphaCfg, _
_warehouseNumber, _
_reqCapacity)
_callbackMethod(ValidBins)
End Sub
End
Class

Notice how my new worker class takes as a parameter the callback method, which is a delegate. This means, when I initialize the object, I have to give it the address of a method that matches my function pointer (delegate). You can see this delegate get used in the BuildValidBinsCollection method - it calls the _callbackMethod and passes in the BinsCollection it built up. Because my _callbackMethod was passed the AddressOf DatabindBinsList, this is the function which gets executed.

The net effect of this work - NOTHING! I've not done anything here to make the work asynchronous. All the work is still being done in a single thread. Instead what I've done is the sort of thing you might do for an object factory or something - allowing Object A cause Object B to do some work and notify Object A when it's done without Object B actually knowing anything about Object A.

Look for the next post to reveal how I got this to the correct way - using the delegate's built in BeginInvoke functionality (actually easier than what I tried to do here)

-- Matt Ranlett

Published 13-01-2006 12:23 by Matt Ranlett
Filed Under:

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Atlanta .NET Regular Guys said:


Ok, so let's see where we are. In Post1 I talked about getting an animated gif on the screen to indicate...
January 13, 2006 10:59 AM
 

Atlanta .NET Regular Guys said:


Ok, so let's see where we are. In Post1 I talked about getting an animated gif on the screen to indicate...
January 13, 2006 11:00 AM

What do you think?

(required) 
(optional)
(required) 

About Matt Ranlett

One of the two original Atlanta .NET Regular Guys, Matt fills his free time by helping to run several Atlanta area user groups, the Atlanta Code Camps, and works as one of the two INETA co-Vice Presidents of Technology
SkinName:iroha_Blog2
Powered by Community Server, by Telligent Systems