Results 1 to 10 of 10

Thread: Weird Excel VBA "Find" and "Sort" issue.

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Jul 2001
    Location
    London UK
    Posts
    671

    Weird Excel VBA "Find" and "Sort" issue.

    I have a strange problem with automating Excel from VB. When using the Find function to cycle through nonblank
    cells in a large range it takes less than a second to run if I haven't used the sort function previously but more
    than 5 minutes if I have!

    (If anyone wants to check this code out just paste the following code into a standard VB module then run it with the sort line commented and uncommented and see!)

    Has anyone any explanation/solution/work around for this behaviour?

    VB Code:
    1. 'Paste this code into a standard vbmodule.
    2. Option Explicit
    3.  
    4. Private Sub Main()
    5.     If Dir("C:\Test.xls") = "" Then
    6.     Open "C:\Test.xls" For Output As #1
    7.     Close #1
    8.     End If
    9. Dim collCells As Object, collComments As Object, objComment As Object
    10. Dim i As Long, objXLApplication As Object, lngNumberOfCellsToFind As Long
    11. Const xlAscending = 1
    12. Const xlYes = 1
    13. Const xlTopToBottom = 1
    14. Set objXLApplication = CreateObject("Excel.Application")
    15. objXLApplication.Visible = False
    16. objXLApplication.Workbooks.Open ("C:\Test.xls")
    17. objXLApplication.ActiveSheet.Cells.ClearContents
    18.  
    19.  
    20. Set collComments = objXLApplication.ActiveSheet.Comments
    21.     For Each objComment In collComments
    22.     objComment.Delete
    23.     Next
    24. Set collComments = Nothing
    25.  
    26.  
    27.  
    28. Set collCells = objXLApplication.Range("A2:U52").Cells
    29.     For i = 1 To collCells.Count
    30.     collCells(i).Value = Int((100 * Rnd) + 1)
    31.     Next
    32. Set collCells = Nothing
    33.  
    34. objXLApplication.Range("B53").Value = "First dodgy cell"
    35. objXLApplication.Range("IV65536").Value = "Second dodgy cell"
    36. lngNumberOfCellsToFind = 2
    37.  
    38. 'THIS IS THE PROBLEM LINE BELOW!!!!!
    39. 'objXLApplication.ActiveSheet.Columns("A:U").Sort objXLApplication.Range("N2"), xlAscending, objXLApplication.Range("S2"), , xlAscending, , , xlYes, 1, False, xlTopToBottom
    40.  
    41. CommentNonBlankCells objXLApplication.Range("A53:IV65536"), lngNumberOfCellsToFind
    42. objXLApplication.DisplayAlerts = False
    43. objXLApplication.ActiveWorkbook.SaveAs FileName:="C:\Test.xls", FileFormat:=-4143, _
    44.         Password:="", WriteResPassword:="", ReadOnlyRecommended:=False, _
    45.         CreateBackup:=False
    46. objXLApplication.ActiveWorkbook.Close True
    47. objXLApplication.Quit
    48. Set objXLApplication = Nothing
    49. End Sub
    50. Private Sub CommentNonBlankCells(ByRef PassedRange As Object, ByRef lngNumberOfCellsToFind As Long)
    51. Dim objFoundCell As Object, strFirstAddressInRange As String, blnExitDo As Boolean
    52. Const xlFormulas = -4123
    53. Const xlPart = 2
    54. Const xlByRows = 1
    55. Const xlNext = 1
    56. Set objFoundCell = PassedRange.Find("*", , xlFormulas, xlPart, xlByRows, xlNext, False)
    57.     If TypeName(objFoundCell) = "Nothing" Then Exit Sub
    58. lngNumberOfCellsToFind = lngNumberOfCellsToFind - 1
    59. strFirstAddressInRange = objFoundCell.Address
    60. objFoundCell.AddComment ("Data outside expected area")
    61.     Do Until blnExitDo Or (lngNumberOfCellsToFind = 0)
    62.     DoEvents
    63.     Debug.Print Now()
    64.     'THIS IS THE LINE THAT CAN TAKE TOO LONG BELOW!!!!!
    65.     Set objFoundCell = PassedRange.FindNext(objFoundCell)
    66.     Debug.Print Now()
    67.         If objFoundCell.Address = strFirstAddressInRange Then
    68.         blnExitDo = True
    69.         Else
    70.         objFoundCell.AddComment ("Data outside expected area")
    71.         lngNumberOfCellsToFind = lngNumberOfCellsToFind - 1
    72.         End If
    73.     Loop
    74. Set objFoundCell = Nothing
    75. End Sub
    Last edited by MartinSmith; Mar 15th, 2003 at 08:55 AM.

  2. #2

    Thread Starter
    Fanatic Member
    Join Date
    Jul 2001
    Location
    London UK
    Posts
    671
    Bump!

  3. #3
    Evil Genius alex_read's Avatar
    Join Date
    May 2000
    Location
    Espoo, Finland
    Posts
    5,538
    Hi Martin, that's quite a bit of code you've got there!
    Can you tell me what the code is trying to achieve & I'll see if there's a way to re-write this for you. Also, can you tell me if you need to use the createobject call to open Excel, i.e. will your users all be using the same version of Excel here?

    Thanks

    Please rate this post if it was useful for you!
    Please try to search before creating a new post,
    Please format code using [ code ][ /code ], and
    Post sample code, error details & problem details

  4. #4

    Thread Starter
    Fanatic Member
    Join Date
    Jul 2001
    Location
    London UK
    Posts
    671
    Hi Alex,

    Yes my users could be using either Office 97 or 2000 so I can't use early binding I'm afraid.

    The code I have submitted is really just to provide a demo of the problem (ie the actual app doesn't fill the spreadsheet with random numbers!)

    What I am trying to do is write an app to process data in excel spreadsheets returned by sales people.

    The number of rows in the spreadsheet can vary and despite instructions to the contrary they sometimes put comments below or above the actual data or blank rows in the middle of the data. I need to sort the data by 2 of the columns anyway prior to processing and selecting all rows in these columns before performing the sort will consolidate the data and remove the blank lines.

    I then loop through inspecting the lines to see if they look as though they have been filled in (not all column values are mandatory) and when I get to the first non filled in line I check if the LastCell is outside where it should be. If it is I check if there is a difference between CountA in my expected range is and CountA in the range ended by the last cell.

    If so I want to abort processing and comment these fields so that regardless of where on the spreadsheet they are they can easily be found with Goto/Special and manually inspected and corrected by the admin person that will be using the app.

    The Find(*) method does this fine. The only problem however is that after using the sort method it takes absolutely ages (about 5 minutes on my Win98/Office 2000 setup). When I haven't called the Sort method though it only takes less than a second to do the same thing!

  5. #5
    Evil Genius alex_read's Avatar
    Join Date
    May 2000
    Location
    Espoo, Finland
    Posts
    5,538
    okkay cool - give me till monday & I'll knock you up a sample!

    Please rate this post if it was useful for you!
    Please try to search before creating a new post,
    Please format code using [ code ][ /code ], and
    Post sample code, error details & problem details

  6. #6

    Thread Starter
    Fanatic Member
    Join Date
    Jul 2001
    Location
    London UK
    Posts
    671
    Cheers,

    Have you any inkling of an idea though why I should be getting this problem?

    The sort method itself doesn't take an undue length of time and I can't see why the fact that I have called it should be even vaguely relevant to the length of time taken by the Find function?

  7. #7

    Thread Starter
    Fanatic Member
    Join Date
    Jul 2001
    Location
    London UK
    Posts
    671

    Bizarre Update!

    Purely by accident I found that if the Excel Application was visible and
    I clicked on it whilst it was hanging on the

    Set objFoundCell = PassedRange.FindNext(objFoundCell)

    line it returned to the VB application with the correct answer instantly
    instead of taking 5 minutes!

    I have now got to the stage that sandwiching the line as below works fine...

    objXLApplication.Visible = True
    objFoundCell.Activate
    Set objFoundCell = PassedRange.FindNext(objFoundCell)
    objXLApplication.Visible = False

    But I don't really want to have to make the app visible at all (the
    objFoundCell.Activate line on its own doesn't do it)

    This must be a bug but I couldn't find anything on MSDN about it!

    Has anyone experienced anything similar before?

    If anyone wants to help me troubleshoot this my demo code to date is as
    follows...

    VB Code:
    1. 'Paste this code into a standard vbmodule.
    2. Option Explicit
    3.  
    4. Private Sub Main()
    5.     If Dir("C:\Test.xls") = "" Then
    6.     Open "C:\Test.xls" For Output As #1
    7.     Close #1
    8.     End If
    9. Dim collCells As Object, collComments As Object, objComment As Object
    10. Dim i As Long, objXLApplication As Object, lngNumberOfCellsToFind As Long
    11. Const xlMinimized = -4140
    12. Const xlAscending = 1
    13. Const xlYes = 1
    14. Const xlTopToBottom = 1
    15. Set objXLApplication = CreateObject("Excel.Application")
    16. objXLApplication.WindowState = xlMinimized
    17. objXLApplication.Visible = False
    18. objXLApplication.Workbooks.Open ("C:\Test.xls")
    19. objXLApplication.ActiveSheet.Cells.ClearContents
    20.  
    21.  
    22. Set collComments = objXLApplication.ActiveSheet.Comments
    23.     For Each objComment In collComments
    24.     objComment.Delete
    25.     Next
    26. Set collComments = Nothing
    27.  
    28.  
    29.  
    30. Set collCells = objXLApplication.Range("A2:U52").Cells
    31.     For i = 1 To collCells.Count
    32.     collCells(i).Value = Int((100 * Rnd) + 1)
    33.     Next
    34. Set collCells = Nothing
    35.  
    36. objXLApplication.Range("B53").Value = "First dodgy cell"
    37. objXLApplication.Range("IV65536").Value = "Second dodgy cell"
    38. lngNumberOfCellsToFind = 2
    39.  
    40. 'THIS IS THE PROBLEM LINE BELOW!!!!!
    41. objXLApplication.ActiveSheet.Columns("A:U").Sort objXLApplication.Range("N2"), xlAscending, _
    42. objXLApplication.Range("S2"), , xlAscending, , , xlYes, 1, False, xlTopToBottom
    43.  
    44. CommentNonBlankCells objXLApplication, objXLApplication.Range("A53:IV65536"), lngNumberOfCellsToFind
    45. objXLApplication.DisplayAlerts = False
    46. objXLApplication.ActiveWorkbook.SaveAs FileName:="C:\Test.xls", FileFormat:=-4143, _
    47.         Password:="", WriteResPassword:="", ReadOnlyRecommended:=False, _
    48.         CreateBackup:=False
    49. objXLApplication.ActiveWorkbook.Close True
    50. objXLApplication.Quit
    51. Set objXLApplication = Nothing
    52. End Sub
    53.  
    54. Private Sub CommentNonBlankCells(ByRef objXLApplication As Object, ByRef PassedRange As Object, ByRef lngNumberOfCellsToFind As Long)
    55. Dim objFoundCell As Object, strFirstAddressInRange As String, blnExitDo As Boolean
    56. Const xlFormulas = -4123
    57. Const xlPart = 2
    58. Const xlByRows = 1
    59. Const xlNext = 1
    60. Set objFoundCell = PassedRange.Find("*", , xlFormulas, xlPart, xlByRows, xlNext, False)
    61.     If TypeName(objFoundCell) = "Nothing" Then Exit Sub
    62. lngNumberOfCellsToFind = lngNumberOfCellsToFind - 1
    63. strFirstAddressInRange = objFoundCell.Address
    64. objFoundCell.AddComment ("Data outside expected area")
    65.     Do Until blnExitDo Or (lngNumberOfCellsToFind = 0)
    66.     DoEvents
    67.     Debug.Print Now()
    68.     objXLApplication.Visible = True
    69.     objFoundCell.Activate
    70.     Set objFoundCell = PassedRange.FindNext(objFoundCell)
    71.     objXLApplication.Visible = False
    72.     Debug.Print Now()
    73.         If objFoundCell.Address = strFirstAddressInRange Then
    74.         blnExitDo = True
    75.         Else
    76.         objFoundCell.AddComment ("Data outside expected area")
    77.         lngNumberOfCellsToFind = lngNumberOfCellsToFind - 1
    78.         End If
    79.     Loop
    80. Set objFoundCell = Nothing
    81. End Sub

  8. #8
    Evil Genius alex_read's Avatar
    Join Date
    May 2000
    Location
    Espoo, Finland
    Posts
    5,538
    they sometimes put comments below or above the actual data....
    If you're only interested in the used range cells which you sorted, can't you just delete any other rows from the sheet?

    This one I think might be what you're looking for, note that I did this within Excel, so you might have to alter some of it slightly (ie. declare the excel.application object & specify any Excel constants if vb doesn't pick them up):
    VB Code:
    1. Private Sub CommandButton1_Click()
    2.     Dim rngCurrentUsedRange As Range
    3.     Dim rngNonUsedCellRange As Range
    4.     Dim comCellComment As Comment
    5.    
    6.     ' Setup the range of valid data to be sorted
    7.     Set rngCurrentUsedRange = Range("A2:C6")
    8.  
    9.     If Not (rngCurrentUsedRange Is Nothing) Then
    10.         With rngCurrentUsedRange
    11.        
    12.             ' Perform your sorting
    13.             .Sort .Range("B2"), xlAscending, .Range("C2"), , _
    14.             xlAscending, , , xlYes, 1, False, xlTopToBottom
    15.    
    16.             ' Delete any blank rows within this used range
    17.             For Each Cell In rngCurrentUsedRange
    18.                 If (WorksheetFunction.CountA(Cell.EntireColumn) = 0) Then
    19.                     Cell.EntireColumn.Delete
    20.                 End If
    21.             Next Cell
    22.            
    23.             ' Clear all comments from past used range
    24.             Rows("6:" & ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row).Select
    25.             Selection.ClearComments
    26.         End With
    27.     End If
    28. End Sub

    Please rate this post if it was useful for you!
    Please try to search before creating a new post,
    Please format code using [ code ][ /code ], and
    Post sample code, error details & problem details

  9. #9

    Thread Starter
    Fanatic Member
    Join Date
    Jul 2001
    Location
    London UK
    Posts
    671
    Thanks Alex.

    I might end up using that.

    I need to check with the customer whether they are happy with this.

    At the very least though your code has shown me that my deletion of comments by looping through the ActiveSheet.Comments collection was completely unnecessary

    I didn't realise there was a Range.ClearComments function!

  10. #10
    Evil Genius alex_read's Avatar
    Join Date
    May 2000
    Location
    Espoo, Finland
    Posts
    5,538
    I'll be honest - neither did I, but just started looking around at ways of doing this & stumbled across it - I've never needed to do any work with comments before but I'll remember this one if I need it in future!

    Please rate this post if it was useful for you!
    Please try to search before creating a new post,
    Please format code using [ code ][ /code ], and
    Post sample code, error details & problem details

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width