Object Variable or With Block Variable not Set Error Access Vba - vba

I tried reading other posted questions about this error but I am still having trouble understanding it.
I was having errors after renaming variables and posted this question, I use option explicit and debug my code and it fixed my naming problems, But I still am having one problem in that 2 out of my 21 comboboxes in the operations row are still getting this same error. How can they get an error when they are using the same functions as the other 19. I checked their names and they are correct how could this be??
I have a form that I have build that is set up to look and function similar to and excel spreadsheet. This is the layout, Column Names first and then Control type
Operation Time/Min DecTime LaborRate Cost MarkUp Total
ComboBox TextBox Label Label Label TextBox Label
So all together I have 7 columns with 21 rows (not including column names) in which I can input data. Any where there is a label there is no user input its value is simply derived from the other fields.
I used the following naming convention for my form controls cb_op1, cb_op2, cb_op3 ... I just increment it by one to mimic the row number (control name plus row number). Operation = cb_op, Time = tb_time, DecTime = tb_Dectime, LaborRate = tb_LbrRate, Cost = tb_Cost, MarkUp = tb_MU, Total = tb_Total.
I wrote functions for the AfterUpdate events for my controls. This first function gets the value of the control based on its row number and then allows me to assign it to a variable this fuction is used for multiple controls update events.
Private Function GetControlValue(name As String) As Object
Dim obj As Object
For Each obj In Me.Controls
If obj.name = name Then
Set GetControlValue = obj
Exit Function
End If
Next
End Function
The next function is used to actually update the other field in the row based on the after update event. I use the GetControlValue Function listed above in this function. This is just for the operations column.
Private Function OperationsUpdate(RowNumber As Integer)
Dim Operations As Object
Dim Time As Object
Dim DecTime As Object
Dim LaborRate As Object
Set Operations = GetControlValue("cb_op" & RowNumber)
Set Time = GetControlValue("tb_time" & RowNumber)
Set DecTime = GetControlValue("tb_DecTime" & RowNumber)
Set LaborRate = GetControlValue("tb_LbrRate" & RowNumber)
Set Cost = GetControlValue("tb_Cost" & RowNumber)
If IsNull(Time.Value) Then
If IsNull(Operation.Value) Then
Cost.Value = ""
LaborRate.Value = ""
Else
LaborRate.Value = DLookup("LaborRate", "OperationsType", "[operationsID] = " & Operation.Value)
End If
Else
If IsNull(Operation.Value) Then
Cost.Value = ""
LaborRate.Value = ""
Else
LaborRate.Value = DLookup("LaborRate", "OperationsType", "[operationsID] = " & Operation.Value)
Cost.Value = DecTime.Value * LaborRate.Value
End If
End If
FormObjectsUpdate2 (RowNumber)
Set Operations = Nothing
Set Time = Nothing
Set DecTime = Nothing
Set LaborRate = Nothing
Set Cost = Nothing
End Function
Finally this is how my After update events look per control. I post just the first three not all 21 to save space.
Private Sub cb_op1_AfterUpdate()
OperationsUpdate (1)
End Sub
Private Sub cb_op2_AfterUpdate()
OperationsUpdate (2)
End Sub
Private Sub cb_op3_AfterUpdate()
OperationsUpdate (3)
End Sub
I just posted the function and the code for the first column operations all the other after update events are now having the same issues too.

Related

Highlight DataGridViewRows based on value comparison with other rows

I have a Part class with the fields list in the code below. I have a DataGridView control, which I am filtering with the Advanced DGV (ADGV) DLL from NUGET. I must include the ADGV in my winform. I currently have a DataGridView, a search box on the form, and a button to run the following function. I need to go through all of the visible rows, collect a unique list of part numbers with their most recent revisions, and then color the rows in DataGridView which are out of date by checking the part number and rev on each row against the mostuptodate list. For 45,000 entries displayed in DataGridView, this take ~17 secs. For ~50 entries, it take ~1.2 seconds. This is extremely inefficient, but I can't see a way to cut the time down.
Sub highlightOutdatedParts()
'Purpose: use the results in the datagridview control, find the most recent revision of each part, and
' highlight all outdated parts relative to their respective most recent revisions
'SORT BY PART NUMBER AND THEN BY REV
If resultsGrid.ColumnCount = 0 Or resultsGrid.RowCount = 0 Then Exit Sub
Dim stopwatch As New Stopwatch
stopwatch.Start()
resultsGrid.Sort(resultsGrid.Columns("PartNumber"), ListSortDirection.Ascending)
Dim iBag As New ConcurrentBag(Of Part)
Dim sortedList As Generic.List(Of Part)
For Each row As DataGridViewRow In resultsGrid.Rows
If row.Visible = True Then
Dim iPart As New Part()
Try
iPart.Row = row.Cells(0).Value
iPart.Workbook = CStr(row.Cells(1).Value)
iPart.Worksheet = CStr(row.Cells(2).Value)
iPart.Product = CStr(row.Cells(3).Value)
iPart.PartNumber = CStr(row.Cells(4).Value)
iPart.ItemNo = CStr(row.Cells(5).Value)
iPart.Rev = CStr(row.Cells(6).Value)
iPart.Description = CStr(row.Cells(7).Value)
iPart.Units = CStr(row.Cells(8).Value)
iPart.Type = CStr(row.Cells(9).Value)
iPart.PurchCtgy = CStr(row.Cells(10).Value)
iPart.Qty = CDbl(row.Cells(11).Value)
iPart.TtlPerProd = CDbl(row.Cells(12).Value)
iPart.Hierarchy = CStr(row.Cells(13).Value)
iBag.Add(iPart)
Catch ice As InvalidCastException
Catch nre As NullReferenceException
End Try
End If
Next
sortedList = (From c In iBag Order By c.PartNumber, c.Rev).ToList() ' sort and convert to list
Dim mostUTDRevList As New Generic.List(Of Part) ' list of most up to date parts, by Rev letter
For sl As Integer = sortedList.Count - 1 To 0 Step -1 'start at end of list and work to beginning
Dim query = From entry In mostUTDRevList ' check if part number already exists in most up to date list
Where entry.PartNumber = sortedList(sl).PartNumber
Select entry
If query.Count = 0 Then ' if this part does not already exist in the list, add.
mostUTDRevList.Add(sortedList(sl))
End If
Next
'HIGHLIGHT DATAGRIDVIEW ROWS WHERE PART NUMBERS ARE OUT OF DATE
For Each row As DataGridViewRow In resultsGrid.Rows
' if that part with that Rev does not exist in the list, it must be out of date
Try
Dim rowPN As String = CStr(row.Cells(4).Value).ToUpper ' get part number
Dim rowR As String = CStr(row.Cells(6).Value).ToUpper ' get Rev
Dim query = From entry In mostUTDRevList ' check if that part number with that Rev is in the list.
Where entry.PartNumber.ToUpper.Equals(rowPN) AndAlso
entry.Rev.ToUpper.Equals(rowR)
Select entry
If query.Count = 0 Then ' if the part is out of date highlight its' row
row.DefaultCellStyle.BackColor = Color.Chocolate
End If
Catch ex As NullReferenceException
Catch ice As InvalidCastException
End Try
Next
resultsGrid.Select()
stopwatch.Stop()
If Not BackgroundWorker1.IsBusy() Then timertextbox.Text = stopwatch.Elapsed.TotalSeconds.ToString & " secs"
MessageBox.Show("Highlighting completed successfully.")
End Sub
It is almost always faster to work with the data than the control. The control is simply the means to present a view of the data (in a grid) to the users. Working with the data from there requires too much converting to be effieicent. Then, use the DGV events to highlight the rows
Its hard to tell all the details of what you are doing, but it looks like you are comparing the data to itself (as opposed to some concrete table where the lastest revision codes are defined). Nor is it clear why the datasources are collections, ConcurrentBags etc. The key would be to use collections optimized for the job.
To demonstrate, I have a table with 75,000 rows; the product codes are randomly selected from a pool of 25,000 and a revision code is a random integer (1-9). After the DGV datasource is built (a DataTable) a LookUp is created from the ProductCode-Revision pair. This is done once and once only:
' form level declaration
Private PRCodes As ILookup(Of String, Int32)
' go thru table
' group by the product code
' create an anon Name-Value object for each,
' storing the code and highest rev number
' convert result to a LookUp
PRCodes = dtSample.AsEnumerable.
GroupBy(Function(g) g.Item("ProductCode"),
Function(key, values) New With {.Name = key.ToString(), .Value = values.
Max(Of Int32)(Function(j) j.Field(Of Int32)("RevCode"))
}).
ToLookup(Of String, Int32)(Function(k) k.Name, Function(v) v.Value)
Elapsed time via stopwatch: 81 milliseconds to create the collection of 23731 items. The code uses an anonymous type to store a Max Revision code for each product code. A concrete class could also be used. If you're worried about mixed casing, use .ToLowerInvariant() when creating the LookUp (not ToUpper -- see What's Wrong With Turkey?) and again later when looking up the max rev.
Then rather than looping thru the DGV rows use the RowPrePaint event:
If e.RowIndex = -1 Then Return
If dgv1.Rows(e.RowIndex).IsNewRow Then Return
' .ToLowerInvariant() if the casing can vary row to row
Dim pc = dgv1.Rows(e.RowIndex).Cells("ProductCode").Value.ToString()
Dim rv = Convert.ToInt32(dgv1.Rows(e.RowIndex).Cells("RevCode").Value)
Dim item = PRCodes(pc)(0)
If item > rv Then
dgv1.Rows(e.RowIndex).DefaultCellStyle.BackColor = Color.MistyRose
End If
Notes
It takes some time to create the DataSource, but 75,000 rows is a lot to throw at a user
The time to create the LookUp is minimal - barely measurable
There is no noticeable wait in displaying them because a) the LookUp is made for this sort of thing, b) rows are done as needed when they are displayed. Row # 19,999 may never be processed if the user never scrolls that far.
This is all geared to just color a row. If you needed to save the Current/NotCurrent state for each row, add a Boolean column to the DataTable and loop on that. The column can be invisible if to hide it from the user.
The random data results in 47,000 out of date RevCodes. Processing 75k rows in the DataTable to set the flag takes 591 milliseconds. You would want to do this before you set the DataTable as the DataSource to prevent changes to the data resulting in various events in the control.
In general, the time to harvest the max RevCode flag and even tag the out of date rows is a trivial increment to creating the datasource.
The Result:
The data view is sorted by ProductCode so that the coloring of lower RevCodes is apparent.
We surely cant grok all the details and constraints of the system from a small snippet - even the data types and original datasource are a guess for us. However, this should provide some help with better look-up methods, and the concept of working with the data rather than the user's view.
One thing is the revision code - yours is treating them as a string. If this is alphanumeric, it may well not compare correctly - "9" sorts/compares higher than "834" or "1JW".
See also:
Lookup(Of TKey, TElement) Class
Anonymous Types
The solution was spurred in part by #Plutonix.
Sub highlightOutdatedParts()
If resultsGrid.ColumnCount = 0 Or resultsGrid.RowCount = 0 Then Exit Sub
Dim stopwatch As New Stopwatch
stopwatch.Start()
resultsGrid.DataSource.DefaultView.Sort = "PartNumber ASC, Rev DESC"
resultsGrid.Update()
'HIGHLIGHT DATAGRIDVIEW ROWS WHERE PART NUMBERS ARE OUT OF DATE
Dim irow As Long = 0
Do While irow <= resultsGrid.RowCount - 2
' if that part with that Rev does not exist in the list, it must be out of date
Dim utdPN As String = resultsGrid.Rows(irow).Cells(4).Value.ToString().ToUpper()
Dim utdRev As String = resultsGrid.Rows(irow).Cells(6).Value.ToString().ToUpper()
Dim iirow As Long = irow + 1
'If iirow > resultsGrid.RowCount - 1 Then Exit Do
Dim activePN As String = Nothing
Dim activeRev As String = Nothing
Try
activePN = resultsGrid.Rows(iirow).Cells(4).Value.ToString().ToUpper()
activeRev = resultsGrid.Rows(iirow).Cells(6).Value.ToString().ToUpper()
Catch ex As NullReferenceException
End Try
Do While activePN = utdPN
If iirow > resultsGrid.RowCount - 1 Then Exit Do
If activeRev <> utdRev Then
resultsGrid.Rows(iirow).DefaultCellStyle.BackColor = Color.Chocolate
End If
iirow += 1
Try
activePN = resultsGrid.Rows(iirow).Cells(4).Value.ToString().ToUpper()
activeRev = resultsGrid.Rows(iirow).Cells(6).Value.ToString().ToUpper()
Catch nre As NullReferenceException
Catch aoore As ArgumentOutOfRangeException
End Try
Loop
irow = iirow
Loop
resultsGrid.Select()
stopwatch.Stop()
If Not BackgroundWorker1.IsBusy() Then
timertextbox.Text = stopwatch.Elapsed.TotalSeconds.ToString & " secs"
resultcounttextbox.Text = resultsGrid.RowCount - 1 & " results"
End If
MessageBox.Show("Highlighting completed successfully.")
End Sub

Setting CheckBoxes from another userform in VBA

I have a userform which contains a number of checkboxes from 1 to 100. I have written some very simple code so that when you submit the form it creates a binary string that represents the state of those 100 checkboxes, where 0 is false and 1 is true. The code to do this is here:
Private Sub BusRulesSubmit_Click()
Dim myBinaryString As String
Dim nm As String
Dim c As Control
For busRuleIdx = 1 To 100
nm = "CheckBox" & busRuleIdx
Set c = Controls(nm)
If c.Value = True Then
myBinaryString = myBinaryString & "1"
Else
myBinaryString = myBinaryString & "0"
End If
Next
msgBox myBinaryString
End Sub
I now want to open this Userform from another form, where I have a similar binary string, and use this string to set the values of the checkboxes to true or false as appropariate. However I am having issues when setting my control. The code is here:
Private Sub populateBusRules()
Dim c As Control
Dim myBRBinary As String
myBRBinary = "000000000011100000000000000000000000000000000000000000000000000000000010000000000000000000000000000"
For busRuleIdx = 1 To 100
nm = "BusinessRules.CheckBox" & busRuleIdx
Set c = Controls(nm)
If Mid(myBRBinary, buRuleIdx - 1, 1) = 1 Then
c.Value = True
Else
c.Value = False
End If
Next
End Sub
When I run the above, I get a runtime error "Could not find the specified object" and when going to debug it highlights this problem where the code states "Set c = Controls(nm)" - and I can see that it is failing in the first round of the loop i.e. where nm = "BusinessRules.CheckBox1"
Interestingly if I run the code "Set c = Controls(BusinessRules.CheckBox1)" I get no such issue.
Any help would be much appreciated.
Thanks,
Paul.
I think the BusinessRules is giving you the issue. In the Controls collection, there is no Control named "BusinessRules.CheckBox1", only one named "CheckBox1" within the BusinessRules.Controls collection. Assuming there aren't other issues mentioned in the comments above (like the form being closed before this is called), then this should fix your issue

Base data sheet form will not display calculated Field

I have a Data Sheet form which has a calculated field column. However the field will not display even though it has the correct value. The field in question is "numRisk":
Sub Calculate_Risk (Form As Object)
Dim OrderPrice, IfDonePrice, TotBrSymComm, BrComm, Risk As Double
Dim Symbol As String
Dim IntRateMult, noContracts As Integer
If MinTick = 0 OR Rate = 0 Then
Exit Sub
End If
Symbol = RTrim(Form.getByName("txtSymbol").CurrentValue)
If Symbol = "" Then
Exit Sub
End If
OrderPrice = Form.getByName("fmtOrder_Price").CurrentValue
IfDonePrice = Form.getByName("fmtIf_Done_Price").CurrentValue
noContracts = Form.getByName("fmtNo_Contracts").CurrentValue
If NOT USIntRates Then
Risk = ABS(OrderPrice - IfDonePrice) / MinTick
Else
Risk = ABS(OrderPrice\1 - IfDonePrice\1) * MinTick
IntRateMult = IIf(Symbol = "FV" OR Symbol = "TU",400, 200)
Risk = ABS(Risk - IntRateMult * ABS(OrderPrice - OrderPrice\1
IfDonePrice + IfDonePrice\1))
End If
Risk = Risk * MinTickVal / Rate
TotBrSymComm = BrSymComm + BrSymCommAud
BrComm = IIf(TotBrSymComm = 0, BrCommission, BrSymCommAud + BrSymComm/Rate)
Risk = noContracts*(Risk + BrComm * 2)
Form.getByName("numRisk").Value = Risk
End Sub
The subroutine is called from the following routine which is triggered when the form is loaded:
Sub FromListForm(Event as Object)
Dim Form As Object
Dim TodaysDate As New com.sun.star.util.Date
Dim CurrDate As Date
Form=Event.Source.getByName("MainForm_Grid")
Form.RowSet.first()
Do Until Form.RowSet.isAfterLast()
Get_Contract(Form)
Get_Broker_Comm(Form)
Calculate_Risk(Form)
If isEmpty(Form.getByName("OrderDate").Date) Then
CurrDate = Date()
TodaysDate.Day = Day(CurrDate)
TodaysDate.Month = Month(CurrDate)
TodaysDate.Year = Year(CurrDate)
Form.getByName("OrderDate").CurrentValue = TodaysDate
End If
Form.RowSet.next()
Loop
Form.RowSet.last()
End Sub
Also is there a more efficient method to cycle through the rows? As this seems so slow I can see the row pointer moving down the table as each row is processed.
If I understand correctly, you're trying to enter individual values into each cell in a column of a tablegrid control? I don't believe that's possible.
Inside a tablegrid control, all values have to come from the underlying query. I recommend writing a query to do these calculations, and using that query as the basis for the form - that would solve both the problem of displaying the calculated result as well as improving the load speed of the form (since database logic in determining query results is almost always more efficient than a macro going row-by-row).
Alternately, you could have the calculated field be standalone, showing only the calculated result for the currently selected row of the tablegrid control. In this scenario, the "form loaded" event would only do the calculation for the first row, and the calculating macro would be triggered each time the row selection changed.

Set parent Control to another control

I need to set a parent Control to another control using the VBA code.
Actually i am looping to create differents controls dynamically and i want now to link them by child-parent.
Do someone has an idea ?
Here is the function where i create a new control and i set some values. And the last assignment is where i want to set the parent
Public Function apply_change(ihm_f, oNode, iMyName$, project$)
Dim new_elem
Dim oSubNodes As IXMLDOMNode
If oNode.Attributes.getNamedItem("Type").Text <> "Page" Then
If (oNode.Attributes.getNamedItem("Type").Text = "RefEdit") Then
Set new_elem = ihm_f.Designer.Controls.Add("RefEdit.Ctrl", oNode.nodeName, True)
Else
Set new_elem = ihm_f.Designer.Controls.Add("Forms." & oNode.Attributes.getNamedItem("Type").Text & ".1", oNode.nodeName, True)
End If
With new_elem
On Error Resume Next
.Width = oNode.Attributes.getNamedItem("Width").Text
.Top = oNode.Attributes.getNamedItem("Top").Text
.Left = oNode.Attributes.getNamedItem("Left").Text
.Height = oNode.Attributes.getNamedItem("Height").Text
Set .Parent = get_parent(oNode.ParentNode.nodeName, oNode, ihm_f)
End With
If oNode.Attributes.getNamedItem("Type").Text = "MultiPage" Then
Call new_elem.Pages.Remove(0)
Call new_elem.Pages.Remove(0)
For Each oSubNodes In oNode.ChildNodes()
Call new_elem.Pages.Add(oSubNodes.BaseName, oSubNodes.Attributes.getNamedItem("Caption").Text, oSubNodes.Attributes.getNamedItem("Index").Text)
Next oSubNodes
End If
End If
Set apply_change = ihm_f
End Function
The getparent function return a Controle which can be anything .. like textbox or combo box etc..
You provide so little information in your question that it is difficult to guess your objective.
However, I am guessing that you want to record that Control Xxxxx is the parent of control Yyyyy where the definition of “parent” has nothing to do with Excel’s definition of parent. I am further guessing you do not know how to access controls by number.
The macro below lists the name, type and top position of every control on a form by its index number within the collection Controls. Any property of a control is accessible in this way. If control Xxxxx is the parent of control Yyyyy, you can scan the collection to find their index numbers when the form loads and record this information for use when required.
Private Sub UserForm_Initialize()
Dim InxCtrl As Long
Dim LenNameMax As Long
Dim LenTypeMax As Long
LenNameMax = 0
For InxCtrl = 0 To Controls.Count - 1
If LenNameMax < Len(Controls(InxCtrl).Name) Then
LenNameMax = Len(Controls(InxCtrl).Name)
End If
If LenTypeMax < Len(TypeName(Controls(InxCtrl))) Then
LenTypeMax = Len(TypeName(Controls(InxCtrl)))
End If
Next
Debug.Print PadR("Name", LenNameMax) & "|" & PadR("Type", LenTypeMax) & "| Top"
For InxCtrl = 0 To Controls.Count - 1
Debug.Print PadR(Controls(InxCtrl).Name, LenNameMax) & "|" & _
PadR(TypeName(Controls(InxCtrl)), LenTypeMax) & "|" & _
PadL(Format(Controls(InxCtrl).Top, "#,###.00"), 8)
Next
End Sub

Unexplained Type Mismatch error at about every 10,000 iterations in Excel VBA

I have a VBA macro that uses Microsoft MapPoint to calculate the distance between two locations for each record in my spreadsheet. I have about 120,000 records to process. The program runs smoothly for about 10,000 iterations then returns a Type Mismatch error where I define the MapPoint locations in my error handler. At which point, I select 'Debug' and then resume execution without editing any code, and it will run successfully for another 10,000 or so records before the same thing happens again.
I've checked my data, and I can't see why there would be a type mismatch, or for that matter why the code would choke on a record one time, and then, without resetting anything, handle the same record upon resuming. Any idea why this would happen?
For reference,
- column M contains locations of the form "X County, ST"
- column AN contains a separate location as ZIP
- column G contains the same location data as AN but in the form "X County, ST"
Sub distance_from_res()
Dim oApp As MapPoint.Application
Dim k As Long
Dim count As Long
Dim errors As Long
k = 0
count = Sheets("i1_20041").Range("A2", Sheets("i1_20041").Range("A2").End(xlDown)).count
errors = 0
Set oApp = CreateObject("MapPoint.Application.NA.11")
oApp.Visible = False
Set objMap = oApp.NewMap
Dim objRes As MapPoint.Location
Dim objFish As MapPoint.Location
'Error executes code at 'LocError' and then returns to point of error.
On Error GoTo LocError
Do While k < count
If Sheets("i1_20041").Range("M2").Offset(k, 0) <> "" Then
'Sets MapPoint locations as [County],[State] from Excel sheet columns "INT_CNTY_ST" and "ZIP".
Set objRes = objMap.FindResults(Sheets("i1_20041").Range("AN2").Offset(k, 0)).Item(1)
Set objFish = objMap.FindResults(Sheets("i1_20041").Range("M2").Offset(k, 0)).Item(1)
'Calculates distance between two locations and prints it in appropriate cell in Column AO.
Sheets("i1_20041").Range("AO2").Offset(k, 0) = objRes.DistanceTo(objFish)
Else
errors = errors + 1
End If
k = k + 1
Loop
'Displays appropriate message at termination of program.
If errors = 0 Then
MsgBox ("All distance calculations were successful!")
Else
MsgBox ("Complete! Distance could not be calculated for " & errors & " of " & count & " records.")
End If
Exit Sub
LocError:
If Sheets("i1_20041").Range("G2").Offset(k, 0) = "" Then
errors = errors + 1
Else
'THIS IS WHERE THE ERROR OCCURS!
Set objRes = objMap.FindResults(Sheets("i1_20041").Range("G2").Offset(k, 0)).Item(1)
Set objFish = objMap.FindResults(Sheets("i1_20041").Range("M2").Offset(k, 0)).Item(1)
'Calculates distance between two locations and prints it in appropriate cell in Column AO.
Sheets("i1_20041").Range("AO2").Offset(k, 0) = objRes.DistanceTo(objFish)
End If
k = k + 1
Resume
End Sub
UPDATE:
I incorporated most of the suggestions from #winwaed and #Mike D, and my code is now more accurate and doesn't choke on errors. However, the old problem reared its head in a new form. Now, after around 10,000 iterations, the code continues but prints the distance of the ~10,000th record for every record afterwards. I can restart the code at the trouble point, and it will find the distances normally for those records. Why would this happen? I've posted my updated code below.
Sub distance_from_res()
Dim oApp As MapPoint.Application
Dim k As Long
Dim rc As Long
Dim errors As Long
Dim dist As Double
Dim zipRes As Range
Dim coRes As Range
Dim coInt As Range
Dim distR As Range
Set zipRes = Sheets("Sheet1").Range("C2")
Set coRes = Sheets("Sheet1").Range("B2")
Set coInt = Sheets("Sheet1").Range("E2")
Set distR = Sheets("Sheet1").Range("G2")
k = 0
rc = Sheets("Sheet1").Range("F2", Sheets("Sheet1").Range("F2").End(xlDown)).Count
errors = 0
'Start MapPoint application.
Set oApp = CreateObject("MapPoint.Application.NA.11")
oApp.Visible = False
Set objMap = oApp.NewMap
Dim objResultsRes As MapPoint.FindResults
Dim objResultsInt As MapPoint.FindResults
Dim objRes As MapPoint.Location
Dim objInt As MapPoint.Location
Do While k < rc
'Check results for Res Zip Code. If good, set first result to objRes. If not, check results for Res County,ST. If good, set first result to objRes. Else, set objRes to Nothing.
Set objResultsRes = objMap.FindResults(zipRes.Offset(k, 0))
If objResultsRes.ResultsQuality = geoFirstResultGood Then
Set objRes = objResultsRes.Item(1)
Else
Set objResultsRes = Nothing
Set objResultsRes = objMap.FindResults(coRes.Offset(k, 0))
If objResultsRes.ResultsQuality = geoFirstResultGood Then
Set objRes = objResultsRes.Item(1)
Else
If objResultsRes.ResultsQuality = geoAmbiguousResults Then
Set objRes = objResultsRes.Item(1)
Else
Set objRes = Nothing
End If
End If
End If
Set objResultsInt = objMap.FindResults(coInt.Offset(k, 0))
If objResultsInt.ResultsQuality = geoFirstResultGood Then
Set objInt = objResultsInt.Item(1)
Else
If objResultsInt.ResultsQuality = geoAmbiguousResults Then
Set objInt = objResultsInt.Item(1)
Else
Set objInt = Nothing
End If
End If
On Error GoTo ErrDist
distR.Offset(k, 0) = objRes.DistanceTo(objInt)
k = k + 1
Loop
Exit Sub
ErrDist:
errors = errors + 1
Resume Next
End Sub
You are constructing a somewhat complex range object (Range -> Offset -> Item). DIM temporary range objects and do it in steps so you can see where exactly the problem occurs
tmpR1 = Sheets("i1_20041").Range("G2")
tmpR2 = tmpR1.Offset(k,0)
then examine the .Count property of the .FindResult before you try accessing Item(1) .... maybe this item doesn't exist ?!?
Debug.Print objMap.FindResult(tmpR2).Count
Hint:
looking at your code, I observe that you use a variable "count". This variable name overlaps with the "Count" property in your second line of code - that's why the "Count" keyword at the end of the statement is printed all lowercase. It's not got anything to do with the errors (we pretend ;-) ), but bad style anyway.
MikeD is right with your dangerous FindResults() calls. However, there is a better way to check the results. The "FindResults collection" isn't a pure collection but includes an extra properties called "ResultsQuality". Docs are here:
http://msdn.microsoft.com/en-us/library/aa493061.aspx
Resultsquality returns a GeoFindResultsQuality enumeration. You want to check for the values geoAllResultsGood and geFirstResultGood. All other results should give an error of some result. Note that your existing code would work find with (for example) Ambiguous Results, even though it is unlikely the first result is the correct one. Also it might match on State or Zipcode (because that is the best it can find) whcih give you an erroneous result. Using ResultsQuality, you can detect this.
I would still check the value of Count as an additional check.
Note that your code is calculating straight line (Great Circle) distances. As such the bottleneck will be the geocoding (FindResults). If you are using the same locations a lot, then a caching mechanism could greatly speed things up.
If you want to calculate driving distances, then there are a number of products on the market for this (yes I wrote two of them!).