How to use Count If function inside of a sub? - excel

I am trying to count the number of times the string shows up in the range and return to worksheet 1. I am assuming this can be done in a Function () but I'm don't know how to write out the syntax.
Sub DistributedApps()
Dim LastRow As Long
Dim Dist As Long
LastRow = Worksheets(3).Cells(Rows.Count, 25).End(xlUp).Row
Dist = Application.Worksheets(3).WorksheetFunction.CountIf(Range("Y1:Y" & LastRow), "Distributed Apps")
Worksheets(1).Range("N66:P69").Value = Dist
End Sub
Object doesn't support this property or method

Dist = Application.Worksheets(3).WorksheetFunction...
The Worksheets property is returning an Object that could be either a Sheets collection (if given an array of sheet names) or a Worksheet object (if given one sheet name, or a sheet index), so you're getting a Worksheet object, but VBA only knows this at run-time.
Move that knowledge to compile-time by introducing a local Worksheet variable; note that Application.Worksheets will give you the sheets collection of whatever workbook is currently active, so it might be a good idea to make that more explicit by qualifying the member call with an actual Workbook object:
Dim sheet As Worksheet
Set sheet = ActiveWorkbook.Worksheets(3)
Now that VBA knows what interface this object has, the editor can help you: when you type the dot in sheet., you'll get a list of all members of the Worksheet interface - and see that none of them is WorksheetFunction: that is why error 438 is raised at run-time, the object doesn't support this property.
The Application object does have a WorksheetFunction member though, so this will work.
lastRow = sheet.Cells(sheet.Rows.Count, 25).End(xlUp).Row
dist = Application.WorksheetFunction.CountIf(sheet.Range("Y1:Y" & lastRow), "Distributed Apps")
Note that the Range member call inside the CountIf argument list is also explicitly qualified with the sheet object. Without this qualifier, Range would be referring to whatever the ActiveSheet is1, and since you don't want to need to Activate any sheets for this to work, using an explicit worksheet object as a qualifier ensures you're computing the count off the correct sheet.
1Unless that code is written in the code-behind for a Worksheet module - in which case the implicit qualifier is Me and the unqualified Range call is referring to that worksheet.

I think this will work.
Sub DistributedApps()
Dim LastRow As Long
Dim Dist As Long
LastRow = Worksheets(3).Cells(Rows.Count, 25).End(xlUp).Row
Dist = Application.WorksheetFunction.CountIf(Worksheets(3).Range("Y1:Y" & LastRow), "Distributed Apps")
Worksheets(1).Range("N66:P69").Value = Dist
End Sub

Related

VBA copy number of Rows to new Workbook

as a beginner to coding and VBA in specific im currently trying to automate as much worksteps as im able to, mainly to get into it. So for now i got a set of >50 Excel Workbooks and the "simple" task to collect the number of Datapoints (one row for each datapoint) in each and pass this value to a new workbook . What i built together until now is this (the credit for basic construct goes fully to Henrik Schiffner, i used it for several other operations):
Sub count_rows()
'Define variables:
Dim numberOfFilesChosen, i As Integer
Dim tempFileDialog As FileDialog
Dim mainWorkbook, sourceWorkbook As Workbook
Dim tempWorkSheet As Worksheet
Dim LastRow As Integer
Set mainWorkbook = Application.ActiveWorkbook
'This Step is not mandatory of course but quite comfortable to choose
'the workbooks to work with
Set tempFileDialog = Application.FileDialog(msoFileDialogFilePicker)
tempFileDialog.AllowMultiSelect = True
numberOfFilesChosen = tempFileDialog.Show
'Loop through all selected workbooks
For i = 1 To tempFileDialog.SelectedItems.count
Workbooks.Open tempFileDialog.SelectedItems(i)
Set sourceWorkbook = ActiveWorkbook
For Each tempWorkSheet In sourceWorkbook.Worksheets
LastRow = tempWorkSheet.UsedRange.Rows.count - 1
MsgBox LastRow
Next tempWorkSheet
Application.DisplayAlerts = False
sourceWorkbook.Close
Application.DisplayAlerts = True
Next i
mainWorkbook.Save
End Sub
This gives me the correct Value from each file in the prompted message box. However, im failing to grab the LasRow value and simply copy it to the mainWorkbook. Aim is to have one value after another in one column (lets say "A"). Failure is on different levels like: Searching for the last empty row in mainWorkbook with:
destinationRow = mainWorkbook.Worksheets("Sheet1").Cells(Rows.count, 1).End(xlUp) + 1
Or even to give the lastRow Value to any spot in mainWorkbooks with e.g.:
mainWorkbook.Worksheets("Sheet1").Rows(destinationRow) = LastRow
Or
LastRow.Copy After:=XXXX
Im pretty sure im misunderstanding a basic concept of VBA, so it would be awesome to get a short explanation why my operations did not work out instead of just getting a working code.
However, adding the name of each Workbook as a header for its value would be magnificent!
Searching for the last empty row in mainWorkbook would use the UsedRange property of Worksheet object; this returns a Range object which you then access the Address property of. You then want to use the Split() function which will take your string and place each substring seperated by a delimiter ($ in this case) into an index of an array. For your purposes you want the 4th index which will be the last row in the UsedRange:
'The MsgBox's should help you understand what is happening with this code
MsgBox (mainWorkbook.Sheets("Sheet1").UsedRange.Address)
lastRow = Split(mainWorkbook.Sheets("Sheet1").UsedRange.Address, "$")(4)
MsgBox (lastRow)
I am a little confused by what you meant with, "give the lastRow Value to any spot in mainWorkbooks." If you meant you would like the set the value of a cell in your mainWorkbook to the value of the lastRow, you would use:
mainWorkbook.Sheets("Sheet1").Cells(1, 1).Value = lastRow
And I am not sure what you are trying to do with: LastRow.Copy After:=XXXX. Your LastRow variable is an Integer Dim LastRow As Integer. An Integer Data Type cannot use the Copy Method. I mostly use the .Copy After:= Method to copy one worksheet and paste it after another. If that is your goal:
'Copy Sheet1 and place it after the last sheet in the workbook
Sheets("Sheet1").Copy After:=Sheets(ActiveWorkbook.Sheets.count)
Otherwise, if you could explain your goal with that I would be happy to help.
As a beginner to code and VBA I would suggest a few things. The first is to do research on OOP (Object Oriented Programming). A very basic breakdown is that you have Objects which then have Methods and Properties (for example the CoffeMug object has a .Temperature property and a .Spill Method, and timsMug and tinasMug are both CoffeeMug objects). I love OOP, but it takes a while to get a truly deep understanding of how it works. After that though, it's a lot of fun and really powerful.
Other than that, use https://learn.microsoft.com/en-us/office/vba/api/overview/ to learn about VBA. I use the Excel section and Language Reference section all the time.

Why am I getting a 'type mismatch' error?

I am trying to copy a range from one workbook to a table in another workbook to capture production data. I am able to copy the correct range and open the workbook with the table successfully. However, when it tries to paste the information into the next available row in the table I get the error 13. I am rather new to vba and can't seem to find a solution, any help would be greatly appreciated.
Dim wbTime As Workbook
Set wbTime = ThisWorkbook
Dim wbData As Workbook
Dim N As Long
N = Cells(Rows.Count, "A").End(xlUp).Row + 1
Dim UsdRws As Long
UsdRws = Cells.Find("*", After:=Range("A32"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
wbTime.ActiveSheet.Range("A6:O" & UsdRws).Copy
Set wbData = Workbooks.Open("S:\Lean Carrollton Initiative\Allen\Buffering Interrupters\1st Shift\B10\Data.xlsx")
wbData.ActiveSheet.Paste Destination:=Worksheets(Sheet1).Range(N & "A")
wbData.Close SaveChanges:=True
End Sub
Destination1:=Worksheets(Sheet1).Range(N & "A")
#QHarr correctly identified the problem with Range(N & "A"), and proposed a fix for the type mismatch error you're getting at Worksheets(Sheet1).
I just wanted to explain what's going on in more details than can fit a little comment box.
Sheet1 is an implicitly declared object variable of type Worksheet, whose compile-time identifier is determined by the (Name) property of the worksheet:
If you change this property value to, say, SummarySheet, then Sheet1 is no longer a valid identifier, and SummarySheet becomes one - and this is part of why specifying Option Explicit at the top of every module is important, because without it VBA will happily compile and run, except now instead of Sheet1 being illegal, it's now an undefined Variant/Empty value that can merrily passed around accidentally, which can make things very hard to debug (the same holds true for any undeclared variable, not just worksheet code names).
So Sheet1 is a Worksheet object reference. If Worksheet had a default member that returned the value of its Name property, your code would work.
But Worksheet doesn't have a default member, so Worksheets(Sheet1) is passing the Worksheet object reference as an argument to the Worksheets.Item indexer (implicitly - because the Worksheets class does have a default member, its Item property), which is expecting a Variant holding either the name of a worksheet (Variant/String), or a numeric index (Variant/Integer or Variant/Long).
Passing a Worksheet object to Worksheets.Item is what's throwing the type mismatch error.
Therefore, assuming that Sheet1 worksheet is the intended destination, this would fix it (making abstraction of the Range parameter bug identified earlier by QHarr):
wbData.ActiveSheet.Paste Destination:=Sheet1.Range(...)
There is never a need to retrieve a worksheet that exists at compile-time in ThisWorkbook, from the Worksheets collection.
Note that in the original code:
Destination1:=Worksheets(Sheet1).Range(N & "A")
Since Worksheets isn't qualified, the Workbook it belongs to is ambiguous: if that code is written in ThisWorkbook, then Worksheets is a member call against Me, i.e. ThisWorkbook.Worksheets. Otherwise, it "conveniently" implicitly refers to whatever workbook happens to be currently active, and that's often a risky assumption for code to be making.
Your range needs to be for correct syntax
Range("A" & N)
You could also use
Cells(N, "A")
And sheet1 should be "Sheet1" i.e. Worksheets("Sheet1"). Sheet1 by itself, unquoted, will be seen as a variable. Unless you have a string variable by that name? Use Option Explicit at the top of your module to check for variable declarations.
Additional comment from #MathieuGuidon:
I'll add that Sheet1 is the default code name of the "Sheet1" worksheet in a new workbook; it's a project-scoped identifier VBA creates for free, giving you a compile-time reference to that specific Worksheet object - thus, retrieving that object from the Worksheets collection is redundant altogether: Sheet1.Range(...) would be preferable -- and ideally, that sheet's (Name) property should be modified to a meaningful identifier, e.g. SummarySheet, which makes SummarySheet.Range(...) legal, without needing to declare any SummarySheet variable

Excel VBA Subscription out of range

I only have 2 simple lines and Excel VBA keeps telling me Subscription out of range... why?
Sub try_again()
'Dim res As Variant
Dim Per_Mnd As Variant
Dim Per_Mnd2 As Variant
Per_Mnd = Worksheets("Res_Lege").Range("F41:AD41")
Worksheets("Res_Kontor_over").Range("F6:AD6").Value = Per_Mnd
End Sub
Try something like this:
Set Per_Mnd = Worksheets("Res_Lege").Range("F41:AD41")
Worksheets("Res_Kontor_over").Range("F6:AD6").Value = Per_Mnd.Cells(1, 1)
It Set-s Per_Mnd as a Range, thus it should be working. Then, when you are asking to put values in Range("F6:AD6"), it takes the first cell of Per_Mnd and it assigns it there.
That would be subscript out of range, but never minding the typo, Rubberduck (disclaimer: I own/manage this open-source project) would fire an inspection result here:
Per_Mnd = Worksheets("Res_Lege").Range("F41:AD41")
And another one here:
Worksheets("Res_Kontor_over").Range("F6:AD6").Value = Per_Mnd
The inspection result would say:
Implicit Active Workbook Reference
Implicit references to the active workbook make the code frail and harder to debug. Consider making these references explicit when they're intended, and prefer working off object references.
In other words, if you're 100% sure that there's no typo in the sheet name, then it looks like the ActiveWorkbook isn't the workbook your code is [implicitly] assuming.
i.e. this code is exactly equivalent:
Per_Mnd = ActiveWorkbook.Worksheets("Res_Lege").Range("F41:AD41").Value
If you mean to work with a workbook your code has opened, make sure you keep a reference to that object when you open it:
Dim wb As Excel.Workbook
Set wb = Application.Workbooks.Open(path)
'...
'now qualify the Worksheets collection with this 'wb' object:
Per_Mnd = wb.Worksheets("Res_Lege").Range("F41:AD41")
If you mean to work with the workbook that contains this code, qualify the Worksheets call with ThisWorkbook:
Per_Mnd = ThisWorkbook.Worksheets("Res_Lege").Range("F41:AD41")
If the worksheet exists at compile-time, give it a code name (the (Name) property in the properties toolwindow /F4) instead - say, ResLegeSheet:
Per_Mnd = ResLegeSheet.Range("F41:AD41")

Initialize a variable to denote worksheet

I have a snippet which throws an error, I assume because the variable s is not initialized. How would I declare the variable s?
Dim X As Integer
Dim WS As Worksheet
'Look for existing sheets named "For Export
'If found, delete existing sheet
For Each s In ActiveWorkbook.Sheets
If s.Name = "For Export" Then
Application.DisplayAlerts = False
' s.Delete
End If
Next s
Probably the easiest way is to search for Option Explicit in your code and to delete it. Then you would not be forced to declare every variable. However, if you are not fan of writing ugly & dirty code try with the following:
Dim s as Sheet
In general, it is better to use the Worksheets collection, instead of the Sheets. Thus:
Dim s as Worksheet
For each s in ActiveWorkbook.Worksheets
The Sheets collection contains both Charts and Worksheets, thus it is better to be more specific.

Load combobox with range of another sheet

I have one combobox in one sheet named extraçao with a combobox,
and I have written this code:
Sub Validar_Idades()
Dim aba1 As Worksheet
Set aba1 = Sheets("IDADE")
Dim aba2 As Worksheet
Set aba2 = Sheets("EXTRAÇÃO")
aba2.ComboBox1.Clear
aba2.ComboBox1.List = aba1.range("A2:A" & aba1.range("A" & aba1.Rows.Count).End(xlUp).Row).Value
End Sub
Why don't I see combobox1?
There is an important distinction between Worksheet objects and Sheet objects:
Worksheet can contain only data (in cells)
Sheet can contain data, and other objects like Charts, combo boxes, list boxes, etc
To declare a Worksheet variable use Worksheet type
To declare a Sheet variable use its CodeName, that appears before the name in brackets:
.
Try one of the 2 options bellow
Option Explicit
Sub Validar_Idades()
Dim aba1 As Worksheet 'Worksheet contain only data (no objects)
Set aba1 = Worksheets("IDADE")
'To declare a variable of type Sheet you need to use its "CodeName"
Dim aba2 As Sheet2 'Sheet can contain data and other objects
Set aba2 = Sheets("EXTRAÇÃO") 'like ListBoxes, Charts, ComboBoxes, etc
aba2.ComboBox1.Clear
aba2.ComboBox1.List = aba1.Range("A2:A" & _
aba1.Range("A" & aba1.Rows.Count).End(xlUp).Row).Value
End Sub
Sub Validar_Idades2()
'or simply use its code name instead of a variable
Sheet2.ComboBox1.Clear
Sheet2.ComboBox1.List = Sheet1.Range("A2:A" & _
Sheet1.Range("A" & Sheet1.Rows.Count).End(xlUp).Row).Value
End Sub
The Worksheet class does not have a combobox1 property - since of course not every worksheet has that control on it - which is why the compiler objects to your code. In addition to using the codename, as Paul Bica suggested, (though there is no generic Sheet object - each sheet is effectively its own class), you could simply declare your variable as Object rather than Worksheet so the code is late bound and the compiler won't object. Alternatively, you can access the control through the OLEObjects property of the Worksheet class:
aba2.OLEObjects("ComboBox1").Object.List = aba1.Range("A2:A" & _
aba1.Range("A" & aba1.Rows.Count).End(xlUp).Row).Value

Resources