Excel VBA store countifs output for further operation - excel

I am writing an application in VBA for excel. Using some conditions and counting the number of records by the following functions which is working fine.
cnt = appExcel.Application.CountIfs(R1, AuditType, R2, "Fail", UserRange1, UserName & "*")
Here R1,R2 and UserRange1 are Range types which is describing individual cells.
If we print
R1.Address '$E:$E
R2.Address ' $M:$M so on.
I am getting the number of rows present with those criteria in "cnt" variable.
I need further manipulation like searching some custom cells from the above range of cells. To do that I need to store those addresses in a "Range" object.
Please guide me how can I store those filtered records in a range. I don't want to copy to another area also.

You add a new range to a worksheet by declaring a Name object in VBA and then setting it's RefersTo value to the address. This example adds the needed declarations and code to do this. It assumes that you are interested in the cells in your R1 range which contain constants:
Dim rngFiltered As Excel.Range
Dim nmNew As Excel.Name
'...
Set rngFiltered = R1.SpecialCells(xlCellTypeVisible)
Set nmNew = ws.Names.Add(Name:="NewRange", RefersTo:="=" & rngFiltered.Address)

If I am understanding what you mean, insert a named range which defines the range referenced.
Excel 2003 Insert>Name>Define
Excel 2010 Formulas>Define Name

Related

How to dynamically name a range of blank cells in excel? For loop help also appreciated for the next step involving this range

I am trying to create a dynamic named range for a checklist. This dynamic range should reference blank cells that are sandwiched between two named cells (TestA & TestB).
The purpose of this dynamic range is to eventually be referenced when I create a macro that calls the below for loop if a certain drop down selection at the beginning of this checklist changes to "section not needed".
Right now, I am concerned with this for loop not working & my dynamic named range not working.
I keep getting this error: "Method, range of object Global Failed"
Note: If you input my formula as a named range in Excel it will in fact give you the number of blank cells for however many cells you decide to sandwich between ranges TestA & TestB, but for some reason...Excel cannot recognize that range within VBA.
Any ideas on how to accomplish this would be very much appreciated.
Thank you!
I have searched the web and youtube for instances of someone trying to reference a blank range dynamically, but have failed to find anything helpful.
FORMULA:
Formula used to create named range "BetweenTest"
=COUNTBLANK(OFFSET(TestA,1,0,1,1):OFFSET(TestB,-1,0,1,1))
CODE:
Sub FFO ()
Dim Rng As Range
For Each Rng In Range("BetweenTest")
If Rng.Value = "" Then
With Rng
.Value = "N/A"
End With
End If
Next Rng
End Sub

How to populate filter results in a different worksheet using VBA?

I set up a data to filter by different categories in a drop down list.
The data is on one sheet, and when filters are selected, the results are generated on that same sheet (exactly how a filter would typically work in Excel).
I want that the database is held on a different worksheet, while the filters/drop down menus and results are in another. This is to hide the database with everyone's information, and only populate the results for the filters that were selected.
The autofilter code:
Sub AdvFilt()
Dim rng As Range
Set rng = Range("B13", Range("U") & Rows.Count).End(xlUp))
rng.AdvancedFilter 1, [X1:AE2], 0
End Sub
Is it possible to designate a range from a different worksheet?
If that code is written in the code-behind of, say, your Sheet1 module, then Range("B13") is implicitly Me.Range("B13").
If that code is written in some standard module, say, Module1, then Range("B13") is implicitly referring to whatever worksheet happens to be currently active. Rubberduck (free, open-source VBIDE add-in project that I manage) can locate every instance of such implicit ActiveSheet reference for you.
What you want, is to never use Range, Columns, Rows, Names, or Cells, without a proper, explicit Worksheet qualifier object.
So you declare and assign a Worksheet object:
Dim sourceSheet As Worksheet
Set sourceSheet = ActiveWorkbook.Worksheets("Some Sheet")
Dim destinationSheet As Workshet
Set destinationSheet = ActiveWorkbook.Worksheets("Some Other Sheet")
And now you can get a Range from either sheet:
sourceSheet.Range("B13").Value = 42
destinationSheet.Range("A1").Value = sourceShet.Range("A1").Value
If both sheets exist at compile-time in ThisWorkbook (the host document that contains your VBA project), then you don't need to declare any variables, because VBA already declared them for you - simply locate your sheets in the VBE's Project Explorer, and set their (Name) property to any valid VBA identifier (no spaces, must start with a letter). ...and then you can just use these identifiers in your code as-is.
Watch out here:
Range("U") & Rows.Count
That will throw error 1004, because "U" is not a valid cell reference (unless you have a named range that you conveniently named U, of course), and then the & Rows.Count is being concatenated to whatever Range.[_Default] happens to return for this "U" range (assuming it doesn't just blow up).
You likely meant to concatenate the number of rows with the column heading U, to get a Range off the concatenated cell address:
Range("U" & Rows.Count)

Can I set a dynamic range in vba excel to use in filter

The following excel sub is a filter that is filtering out rows based on the rows in the criteria row.
The code works well when the ranges are set with absolute data. I want to change the code to take the range from references stored as cell values (an indirect reference) but I cannot find a way to adapt other code snippets I see to work and I wonder if anyone can help me. I am not a programmer.
The problem is that as new data is inserted from time to time the range reference will move and the start of the data an the associated filter is in cell (using RC notation 14,14) and the data in cell 13,12. While I know I can’t use the indirect function in vba I wondered if there is a way to dynamically assign a range to be able to use the Advance filter function.
I have the code to find the last column and row of the data block.
I have tried the following code (2 attempts) but it won’t let me use the object in this way
I have tried to crate the cell reference as a string then assign it using the range function. I then read an answer where someone had put the value of the cells directly into the range function and it has worked for them ( they were copying cells). The 2 attempt are broadly the same but in the second I am trying to be more specific.
The issue seems to be as soon as I change from an absolute reference "A50" in the range statement the range no longer works. I am unsure how to resolve this and perhaps it can't be
It may be helpful to know the that data being filtered is rows of name and telephone data along with a tally system to show attendance (one column per week for a year)
The cells with the dynamic data hold them in the form A1 not RC format
Sub UseAdvancedFilterInPlace()
'This version of the sub has absolute references and works perfectly
Dim rdData As Range
Dim rgcriteria As Range
Call TurnOffStuff
Set rgData = Sheet9.Range(“A50”).CurrentRegion
Set rgcriteria = Sheet9.Range(“A46”).CurrentRegion
rgData.AdvancedFilter xlFilterInPlace, rgcriteria
Call TurnOnStuff
End Sub
Sub UseAdvancedFilterInPlace()
'This version of the sub has dynamic references and fails
Dim rdData As Range
Dim rgcriteria As Range
Call TurnOffStuff
Dim Top_of_data As String
Dim Top_of_Criteria As String
Dim My_range As Range
‘Attempt 1
'Set rgData = Range(Sheet9.Cells(13, 12).Value).CurrentRegion
'Set rgcriteria = Range(Sheet9.Cells(14, 14).Value).CurrentRegion
'Attempt 2
Set rgData = Sheet9.Range(Sheet9.Range(Cells(13, 12)).Value).CurrentRegion
Set rgcriteria = Sheet9.Range(Sheet9.Range(Cells(14, 14)).Value).CurrentRegion
rgData.AdvancedFilter xlFilterInPlace, rgcriteria
Call TurnOnStuff
End Sub
The actual error message I get is an application-defined or object-defined error
This worked for me.
Set rdData = Sheet9.Range(Sheet9.Range("L13").Value).CurrentRegion
Set rgcriteria = Sheet9.Range(Sheet9.Range("N15").Value).CurrentRegion
given that Range("L13").Value is A50 and Range("N15").Value is A46.
extra: Use the statement Option Explicit in the first line of every module, out of every sub or function. This option throws an error on undeclared variables, and will help you avoid renameing mistakes on variables.

VBA copy formula to other sheet, do not move "view"

I'm using a VBA routine to copy
Source sheet: named range (multiple 'selections')
Target sheet: copy-paste formula from cells in named range, to cells with a certain offset from a cell on the target sheet (this offset is depending on a certain selection).
I have two types of named ranges on the source sheet; one from which I only want to copy the values (=rng_operationeel_input_data), one from which I would like to copy to formulas (=rng_operationeel_formules). The formulas should be copied 'relatively', to have references on the target sheet (which are part of the copied values from the other range). For that reason, I can't use "targetCell.Formula = sourceCell.Formula", as it then literally copies the absolute formula. Not relative.
That's why I'm using sourceCell.
I do this in a for each loop over all cells in the source range, as the named range is not one single range (set of ranges).
Note. 'datasetReferenceCell' is the cell on the target sheet from which the offset is taken for pasting.
The problem is that, even if I use VBA to do the copy-pasting, without using 'select' somewhere, still at the end the user is confronted with the target sheet. (Excel will move to the target sheet)
This is only happening, for the copy-paste part.
How can I prevent this from happening?
edit: note that I am already using "Application.ScreenUpdating" (at start to false, at the end to true). I also have a MsgBox at the end of the routine (for info that routine was successful). Excel is moving to the target worksheet after the MsgBox is closed.
Below the VBA code part.
' dataset for weeknr found: save data to dataset
Dim dataRange As Range, dataField As Range
' for each cell in input data range: save value in dataset
Set dataRange = Range("rng_operationeel_input_data")
For Each dataField In dataRange
datasetReferenceCell.Offset(dataField.Row, dataField.Column).Value = dataField.Value
Next dataField
' !!! Following are only saved, not loaded, as it are formula based fields
' for each cell in formula range: paste formula
Set dataRange = Range("rng_operationeel_formules")
For Each dataField In dataRange
dataField.Copy
datasetReferenceCell.Offset(dataField.Row, dataField.Column).PasteSpecial (xlPasteFormulas)
'datasetReferenceCell.Offset(dataField.Row, dataField.Column).Formula = dataField.Formula 'not working, as relative formulas are required
Next dataField
The only way I was able to solve it was to reset the active sheet:
Public Sub routine()
Dim activeWs As Worksheet
Set activeWs = ThisWorkbook.ActiveSheet
.. code including copy & PasteSpecial ...
activeWs.Activate
End Sub

excel range seems to keep information about the reason why it has been created

Take a look at the example: i've created a 'regular' range and from it
two ranges: one by calling "columns" property and the other by calling "rows" property.
Now, while they seem to be regular ranges as well, and while they refer to the same worksheet area, the default method behaves differently.
Public Sub RangeTest01()
Dim r As Range
' i get a 'regular' range on the active sheet of 3 columns and 3 rows
Set r = ActiveSheet.Range("A1:C3")
Dim rr As Range
Set rr = r.Rows
Debug.Print "rr: range generated by the call 'Rows' on the original range"
Debug.Print "1. what kind of object is it? " & TypeName(rr)
Debug.Print "2. what are the sheet cell it refers to? " & rr.Address
Debug.Print "3. what does the default method call produce?" & rr(1).Address
Dim rc As Range
Set rc = r.Columns
Debug.Print "rc: range generated by the call 'Columns' on the original range"
Debug.Print "1. what kind of object is it? " & TypeName(rc)
Debug.Print "2. what are the sheet cell it refers to? " & rc.Address
Debug.Print "3. what does the default method call produce?" & rc(1).Address
End Sub
Output
rr: range generated by the call 'Rows' on the original range
1. what kind of object is it? Range
2. what are the sheet cell it refers to? $A$1:$C$3
3. what does the default method call produce?$A$1:$C$1
rc: range generated by the call 'Columns' on the original range
1. what kind of object is it? Range
2. what are the sheet cell it refers to? $A$1:$C$3
3. what does the default method call produce?$A$1:$A$3
Questions
So not all the ranges on the same data 'are created equal', the best description I can find about the behavior I see here is that "a range keeps the piece of information about the reason why it has been created", and behaves differently on the basis of this information.
So if it has been created for working with columns it keeps a sort of 'column-based' flag, and the opposite way round if it has been created for working with rows.
Questions are:
Is it accessible this piece of information? Are out there other 'changing' types of Ranges or other objects that should be looked where they originated to know how they behave?
You're creating an object containing a series of ranges split by either column objects or row objects, they behave differently because you've defined them with different methods.
If you did rcells = r.cells and run the same process I'm sure it would output
$A$1:$A$1 on point 3. Or maybe just $A$1 - Would be interesting to see.
I don't know of a way to tell differently created ranges apart from one another for sure, but on the other hand I don't think it is necessary either.
Since a range object is a collection-like object containing an (ordered) set of other ranges, it can be iterated and addressed by an index.
Lets have a look at the following range properties that return another range object themselves, based on the range they were applied to:
Rows - contains all cells of the current range divided in single rows
Columns - contains all cells of the current range divided in single columns
Cells - contains all cells of the current range, divided in single cells, one after another (if you iterate it you get cells of the first row, column by column, then the same for the second row and so forth)
EntireRow - contains all cells in the worksheet divided by rows, but only those rows that the current range covers: think of the current range being extended to all columns
EntireColumn - contains all cells in the worksheet divided by columns, but only those columns that the current range covers: think of the current range being extended to all rows
So, depending on the property, the return value will have the cells differently 'sliced' into parts that can then be iterated or addressed by an index. But all properties still return the same cells, just ordered differently (except for the Entire* properties that also return all other cells in their respective row/column).
Now, if you have to handle a range object but have no knowledge of how the range was created, just apply any of the above properties to it to have it return the cells in the way you like.
Since all of those range properties return ranges themselves, you can apply them multiple times.
For example, consider changing your code like this:
Set rr = r.Rows.Columns
...
Set rc = r.Columns.Rows
The output will be identical, just switched between the two blocks. What were lists of rows before became lists of columns and vice versa.

Resources