How to exclude the topmost selection from Application.Selection.Areas? - excel

I want to exclude the topmost selection from a list of selection(CTRL + Click) in a column. For example, if I selected Cell V12 + V10 + V14 + V9. The topmost selection is V9. I have this code that loops through all the selected cells but I need to exclude the topmost selection (i.e V9).
Here's the working code:
Dim rngPart as Range
For Each rngPart in Application.Selection.Areas
MsgBox rngPart.Address
Next
I need help with excluding the topmost selection

I would break this up into a few functions to compose it together.
First I would want a function to find the top range based on its row. To do this we just need a simple loop and assign the range if it is less than the previous cell.
Private Function getTopRange(ByRef source As Range) As Range
Dim cell As Range
For Each cell In source
If getTopRange Is Nothing Then
Set getTopRange = cell
End If
If cell.Row < getTopRange.Row Then
Set getTopRange = cell
End If
Next cell
End Function
Then I would create the function that would return the range excluding the top one.
Again to do that, we just need a simple loop. If it isn't the top cell, then union it to our return range value.
Private Function excludeTopRange(ByRef source As Range) As Range
Dim topRange As Range
Set topRange = getTopRange(source)
Dim cell As Range
For Each cell In source
' Only add if not the top cell
If cell.Address <> topRange.Address Then
If excludeTopRange Is Nothing Then
Set excludeTopRange = cell
Else
Set excludeTopRange = Union(excludeTopRange, cell)
End If
End If
Next cell
End Function
Putting it all together you just call our new function!
Private Sub test()
Dim source As Range
Set source = Application.Selection
Dim excluded As Range
Set excluded = excludeTopRange(source)
MsgBox excluded.Address
End Sub
It's a good design to try and keep your functions small and reusable like this. It's easier to read, debug, test, and reuse!

Related

Correcting a selected range

I have a sheet where the user selects a non-contigious range of cells (ie E4, F6, G7) which I want to be able to convert into (A4, A6, A7) keeping the row number but changing the column. I want to only return a single value however if they select more than one cell in the same row or worse still select the entire row. I am a little out of practice with my VBA and can't figure this one out
.Offset and .Resize do not work with non-continous ranges. But dou can use some tricks to get that done.
The idea is to convert the user's selection into an entire row selection and intersect that with the range of column A. The result is the intersecting cells in column A with the originally selected rows.
Option Explicit
Public Sub SelectAInSelectedRows()
Dim UserSelection As Range
Set UserSelection = Selection.EntireRow ' make user's selection entire row
Dim SelectARange As Range
Set SelectARange = Intersect(UserSelection.Parent.Columns("A"), UserSelection) ' intersect the rows with column A
' select the resulting range in column A
SelectARange.Select
End Sub
Put this into a relevant worksheet module.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Count > 1 Then Exit Sub
If Not (Intersect(ActiveSheet.Columns(1), Target) Is Nothing) Then Exit Sub
ActiveSheet.Cells(Target.Row, 1).Value2 = Target.Value2
End Sub
You can also check if the output cell is not empty, then change

Excel VBA Inserting a Row in a Loop for Each Occurrence

I'm trying to build a VBA application that checks for a certain value, then adds a row on top for each time this value is found.
Sub copy()
Dim rng As Range
Dim row As Range
Dim cell As Range
Set rng = Range("B2:B10")
For Each row In rng.Rows
For Each cell In row.Cells
If cell.value = "test" Then
MsgBox "found" + cell.Address
cell.EntireRow.Insert
End If
Next cell
Next row
End Sub
Every time I try to run this function, however, it keeps adding rows on top of each other continuously and not for each occurrence.
If you loop the cells from top to bottom, adding the row will push your original range down, causing the next loop to evaluate the previous cell.
To avoid this, loop backwards (i.e. bottom to top):
Sub copy_test()
Dim rng As Range
Set rng = Range("B2:B10")
Dim i As Long
For i = rng.Cells.Count To 1 Step -1
If rng.Cells(i).Value = "test" Then
Debug.Print "Found"
rng.Cells(i).EntireRow.Insert
End If
Next i
End Sub
Note: Set rng = Range("B2:B10") is telling VBA that you are referring to Cells B2:B10 of the ActiveSheet which might not be what you want.
Please fully qualify your range to avoid this. (e.g. ThisWorkBook.Worksheets("Sheet1").Range("B2:B10") or use the code name of the worksheet Sheet1.Range("B2:B10").)

Change Excel Column Background colour with Excel Macro

For a Excel document that is becoming larger then the internet I am trying to get rid of automatic lay outs cause they are seriously slowing our excel to an extend where its becomes non usable.
I attempted to create a macro that colours the background of a cell based on the active cell value.
Sub find()
Dim CurValue As String
Dim ColorIndex As String
Dim Findr As Range
Dim MyRange As Range
Set MyRange = ActiveCell
CurValue = ActiveCell.Value
With ActiveCell
Set Findr = Range("A1:A10").find(CurValue)
If Not Findr Is Nothing Then
ColorIndex = Findr.Offset(ColumnOffset:=1).Value
MyRange.Interior.ColorIndex = ColorIndex
' rngStart.Select
End If
End With
End Sub
This sub works perfectly.
However for the problem:
Now i want to call it whenever a cell changes but if I call the macro whenever a cell changes in my Sheet.I tried using the sheet sourcecode for every change.
But then it uses the cell the user jumps to after the change rather then the previous edited cell.
How do i get this Macro to call for every changed cell rather then the new select cell?
Putting the following in the appropriate Worksheet object should work:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim CurValue As String
Dim ColorIndex As String
Dim Findr As Range
For Each AC In Target.Cells
CurValue = AC.Value
Set Findr = Range("A1:A10").find(CurValue)
If Not Findr Is Nothing Then
ColorIndex = Findr.Offset(ColumnOffset:=1).Value
AC.Interior.ColorIndex = ColorIndex
End If
Next
End Sub
Note - one of the A1:A10 needs to be blank, but the value in B next to the blank must have a ColorIndex value and cannot be blank. I'd suggest a zero to empty all colour out of the cell but I can't see what your sheet's 'empty' cells look like..
The For..Each loop is to handle where more than one cell is changed at once, it performs the colour-change on each changed cell.
Also, cells that 'change' due to the result of a formula changing, rather than an edit, will not change using this method.

Excel VBA - how to check for non-blank values in hidden range

I'll start by making my objective clear, and then explaining it fully.
My goal is to check for non-blank values in a range, but only in the hidden cells of that range, and then use conditional formatting in a different cell, depending on whether the cells in the range are empty or not.
I have a named range called Location_Address_RangeCheck that covers the cells directly to the right of the location numbers, like this (location numbers are not part of the range).
When the Number of Locations is changed, the rows that go beyond that number (up to 25) are automatically hidden on worksheet_change to reduce clutter and reduce scrolling to see the stuff below it. That code works fine, so I'm not posting it here so as to not confuse anyone with what I'm trying to accomplish.
I want to provide a safeguard to ensure that there aren't values in the hidden rows that could affect outputs (i.e., if someone selects "3" for Number of Locations, but there is data in cells that might be on the row of the 8th location).
My goal is to check for non-blank values in the range, but only in the hidden cells, and then use conditional formatting in the cell next to the number of locations chosen, depending on whether the cells in the range are empty or not.
So if there is data in the hidden cells, then it would cause the sheet to look like this
.
I've tried so many different things so far, but I'm not making any progress. I've scoured the internet trying to find a solution, but everything I've found is about finding things in visible cells, which is the opposite of what I'm trying to achieve.
Here is the code I have written so far, which I know does not achieve my objective:
Sub testhiddencells()
Dim myRange As Range
Set myRange = Range("Location_Address_RangeCheck")
NumRows = Application.WorksheetFunction.CountA(myRange)
If Range("Location_Address_RangeCheck").Hidden = True Then
If Application.WorksheetFunction.CountA(Range("Location_Address‌​_RangeCheck")) <> 0 Then
MsgBox "There's something there"
End If
End If
End Sub
Here is a minimal example to check with a cell is both hidden and is non-empty:
Option Explicit
Sub Test()
Dim ws As Worksheet
Dim rngToCheck As Range
'test range - all cells populated with 'a' and 3 are hidden
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set rngToCheck = ws.Range("A1:A7")
If TestForNonBlankCellsInHiddenRange(rngToCheck) Then
'do you conditional format stuff here
End If
End Sub
Function TestForNonBlankCellsInHiddenRange(rngToCheck As Range) As Boolean
Dim rngCell As Range
Dim blnCheck As Boolean
'assume that hidden cells are blank
blnCheck = False
'iterate range
For Each rngCell In rngToCheck
If rngCell.EntireRow.Hidden And Not IsEmpty(rngCell.Value) Then
'found a hidden and non-empty cell
blnCheck = True
'debug address of this cell
Debug.Print rngCell.Address
End If
Next rngCell
'return check
TestForNonBlankCellsInHiddenRange = blnCheck
End Function
Looking at the code you used already, you should be able to adapt this to the particular use case of your worksheet.

Visual Basic 2007 Adding values

To start with I'm not really a wise man. So I was trying to add up two values and display them in a third (that's easy) but I also wanted it to repeat an infinite amount of times (namely that it does the same for every row, let's say I place the result in I5, I want also, on every I under it (I6, I7, I8, etc...)
Should it be:
Private Sub Worksheet_Change()
if IsNumeric(Range("B1").sort) And IsNumeric(Range("B2").sort) then
Dim value1 As Single
Dim value2 As Single
range(I5).sort = value+1 + value2
End Sub
Or as I think I'm horribly mistaken?
You're using the .Sort property of Range where you should be using .Value.
There's a couple of ways to achieve what you're looking to do. First option is to iterate through the range and add the relevant value to each cell like so:
Public Sub addCells()
Dim rng As Range, cell As Range
'Set the sheet name
With ThisWorkbook.Worksheets("Sheet_Name")
'Set the range below:
Set rng = .Range("I1:I10")
'Loop through range and add cells together
For Each cell In rng
cell.Value = cell.Offset(0, 2) + cell.Offset(0, 1)
Next cell
End Sub
Another way to do it if the values to be added is ordered in for example column A and B would be:
Public Sub addCells()
Dim rng As Range, cell As Range
'Set the sheet name
With ThisWorkbook.Worksheets("Sheet1")
'Add the first formula to the sheet
.Range("C1").Value = "=SUM(A1+B1)"
'Change the range below to the range to be filled
.Range("C1").AutoFill Destination:=.Range("C1:C10")
End With
End Sub

Resources