How do I make value static in last row value? - vba

Here is the code below:
Public n as Long ' <--above sub procedure
With Sheets("Sheet1").Range("A6").Offset(n, 0)
If n = 0 Then
.Value = 1
Else
.Value = .Parent.Range(.Address).Offset(-1, 0) + 1
End If
n = n + 1
End With
(See pic below) If I delete 4 then click command button again it just reset back to 1. I want to make it static so even I deleted the last value of row it still continue increment from the last value.
Store number
1
2
3
4

Try this:
Sub Test()
Dim trow As Long
With Sheets("Sheet1") '~~> change to suit
trow = .Range("A:A").Find(vbNullString, [A5]).Row
With .Range("A" & trow)
If trow = 6 Then .Value = 1 _
Else .Value = .Offset(-1, 0).Value + 1
End With
End With
End Sub
Above code finds the first blank cells. If it is A6 it assigns a value of 1.
Otherwise it assigns previous cell value plus 1. Is this what you're trying?
Edit1: Explanation
trow = .Range("A:A").Find(vbNullString, [A5]).Row
This finds the first empty row in Column A starting A5.
[A5] is used to return Range("A5") object. So it can also be written as:
trow = .Range("A:A").Find(vbNullString, .Range("A5")).Row
We used a VBA vbNullString constant as What argument in Range Object Find Method.
Find Method returns a Range Object so above can be written also like this:
Sub Test()
Dim r As Range
With Sheets("Sheet1") '~~> change to suit
Set r = .Range("A:A").Find(vbNullString, [A5])
With r
If .Row = 6 Then .Value = 1 _
Else .Value = .Offset(-1, 0).Value + 1
End With
End With
End Sub

What your asking for, a button with memory doesn't sound neatly solvable using just VBA.
You could potentially have a list on a hidden sheet that gets a value added to it each time the commandButton is pressed and it writes the max of the list values back to the target cell?
Alternatively you could investigate using a scrollbar from the form control section of the developer tab with a link to your target cell. I often use this technique for interactive sheets.
Named Range Method
Public sub btnPress
dim val as long
val = Range("PreviousCellValue")
set Range("PreviousCellValue") = val+1
Sheets("Sheet1").Range("A6").Offset(n, 0).value = Range("PreviousCellValue")
End sub btnPress

Related

Copying info from one sheet to another

I am trying to copy data from one sheet as long as the meet the twp below criteria. However, not all the data is being transferred. Any thing stand out to anyone as wrong in my code?
Private Sub FIlist()
Dim LastRow As Long, fgLastRow As Long
Dim c As Integer
LastRow = ActiveWorkbook.Sheets("DaysReport").Range("A1000000").End(xlUp).Row
LastRow = LastRow + 1
Call StartCode
With ActiveWorkbook
For c = 1 To LastRow
If .Sheets("DaysReport").Range("B1").Offset(c - 1, 0) = "ACCEPT" And .Sheets("DaysReport").Range("C1").Offset(c - 1, 0) = "ST" Then
fgLastRow = ActiveWorkbook.Sheets("FG LIST").Range("A1000000").End(xlUp).Row
fgLastRow = fgLastRow + 1
.Sheets("FG LIST").Range("A" & fgLastRow) = .Sheets("DaysReport").Range("A2").Offset(c - 1, 0)
End If
c = c + 1
Next c
End With
Call EndCode
End Sub
The first thing that jumps out is that c should be Long as well.
The use of ActiveWorkbook may be a deliberate design choice - but if it always runs from this workbook, then use ThisWorkbook. Your user could change the workbook or active window at any time, thus causing chaos and mayhem (or at least unknown or undefined results).
Don't use Call - this is now deprecated. Not a show stopper, but still a bad habit.
Watch your index offsets, they can be confusing. Instead of c-1 all the time, just set your start parameters earlier. This means that we remove a +1 in a couple of spots as well!
Now that I tidied the code up - I saw the biggie. And the cause of your problems. I have left it commented in the code below. You are in a loop, and you also increment c (c = c + 1). This means that you skip every second row. If you really want to skip every second row then use For c = 0 To LastRow Step 2 because it is clearer code and your intention is obvious.
Private Sub FIlist()
Dim LastRow As Long, fgLastRow As Long
Dim c As Integer
StartCode
With ThisWorkbook.Sheets("DaysReport")
LastRow = .Range("A1000000").End(xlUp).Row
For c = 0 To LastRow
If .Range("B1").Offset(c, 0) = "ACCEPT" And .Range("C1").Offset(c, 0) = "ST" Then
fgLastRow = ThisWorkbook.Sheets("FG LIST").Range("A1000000").End(xlUp).Row + 1
ThisWorkbook.Sheets("FG LIST").Range("A" & fgLastRow) = .Range("A2").Offset(c, 0)
End If
'c = c + 1
Next c
End With
EndCode
End Sub
You must get rid of that
c = c + 1
Which is making your loop variable update by steps of two !
Furthermore you may want to adopt the following refactoring of your code:
Private Sub FIlist()
Dim cell As Range
Dim fgSht As Worksheet
Set fgSht = ActiveWorkbook.Sheets("FG LIST")
StartCode
With ActiveWorkbook.Sheets("DaysReport")
For Each cell In .Range("A1", .Cells(.Rows.Count, 1).End(xlUp))
If cell.Offset(,1).Value = "ACCEPT" And cell.Offset(,2).Value = "ST" Then fgSht.Cells(fgSht.Rows.Count, 1).End(xlUp).Offset(1).Value = cell.Offset(1).Value
Next
End With
EndCode
End Sub
Please note that I wrote:
If cell.Offset(,1).Value = "ACCEPT" And cell.Offset(,2).Value = "ST" Then fgSht.Cells(fgSht.Rows.Count, 1).End(xlUp).Offset(1).Value = cell.Offset(1).Value
To cope with your code that copied the value in column A one row below the current loop row
Should you actually need to copy the value in column A current row, then just remove that last .Offset(1)

VBA Array doesn't work?

I have this practice file with 5 order prices. The goal is to add $20 to each of the record and have a message box to display the result.
Here is the data:
My code is this:
Sub TotalDelivery()
Dim curDelCharge As Currency
Dim curTotal(4)
Dim i As Integer
Worksheets("Sheet1").Range("B10").Activate
Const curDelCharge = 20
For i = 0 To 4
curTotal(i) = ActiveCell.Offset(i, 1).Value + curDelCharge
MsgBox (curTotal(i))
Next i
End Sub
However the message box only displays 20 which is only my curDelCharge value.
To debug, I change the msgbox code into:
MsgBox (ActiveCell.Offset(i, 1).Value)
The return value is blank which means the code doesn't read my ActiveCell value. Why is that?
Thanks in advance!
This line:
curTotal(i) = ActiveCell.Offset(i, 1).Value + curDelCharge
should instead be:
curTotal(i) = ActiveCell.Offset(i, 0).Value + curDelCharge
Putting a "1" will move the offset 1 column to the right, which you don't want.
Sub TotalDelivery()
Dim curTotal(4)
Dim i As Integer
Dim rngCellsToChange As Range 'range of cells you are targeting
Dim rCell As Range 'individual cell in collection of cells. See alternative solution below
'You can refer to cells directly, without activating them.
'You are highly discouraged to use Activate or Select methods.
'Use ThisWorkbook to explicitly tell VBA, which workbook you are targeting
Set rngCellsToChange = ThisWorkbook.Worksheets("Sheet1").Range("B10:B14")
Const curDelCharge = 20
For i = 0 To 4
curTotal(i) = rngCellsToChange(i + 1).Value + curDelCharge
MsgBox (curTotal(i))
Next i
'Alternatively, you can use the Range object to loop through all it's cells, like so:
For Each rCell In rngCellsToChange
MsgBox rCell.Value + curDelCharge
Next
End Sub

Highlight values on Sheet1 if matched on Sheet2

I'm looking for a way to highlight cells in sheet1 if they match the value in sheet2. Here is the code I have, there aren't any errors coming up but it does nothing. Basically I thought a Do while loop to go through all the records until it hit a blank and then it would read the cell value selected by my offset and compare it to the next sheets cell value while staying on the same row, and if it matched it would highlight on sheet 1 but if it didn't it would move on. Let me know how much I'm off here as I don't have much VBA knowledge. Thanks.
Public Sub RoundedRectangle1_Click()
Dim resource As Range
Dim register As Range
Dim cancel As Range
Set resource = Worksheets("Resource List1").Cells(2, 4)
Set register = Worksheets("Registered List").Cells(2, 1)
Set cancel = Worksheets("Cancelled List").Cells(2, 1)
Call findRegister(resource, register)
End Sub
Public Sub findRegister(ByRef resource As Range, ByRef register As Range)
Dim i As Integer
i = 0
Do While resource.Offset(i, 3) <> ""
If resource.Offset(i, 3).Value = register.Range("A2").Value Then
resource.Offset(i, 3).Cells.Interior.ColorIndex = 37
End If
i = i + 1
Loop
End Sub
Your code is essentially correct, but I think you're having trouble with referencing the right cells. A good debugging technique would be to add .Cells.Interior.ColorIndex = 4 or something similar in your code to see visually whether you're referencing the proper cells. You can also put "F5", "F8", and breakpoints to good use in figuring out what's wrong. See http://www.excel-easy.com/vba/examples/debugging.html if you've never used these.
For example:
Do While resource.Offset(i, 3) <> "" '<--Insert a breakpoint on this line,
'then press "F8" to make sure the
'code inside your Do While loop is
'being executed
resource.Offset(i, 3).Cells.Interior.ColorIndex = 4
register.Range("A2").Cells.Interior.ColorIndex = 6
If resource.Offset(i, 3).Value = register.Range("A2").Value Then
resource.Offset(i, 3).Cells.Interior.ColorIndex = 40
End If
i = i + 1
Loop
Maybe something as simple as this . . . .
Sub Compare2Shts()
For Each Cell In Worksheets("CompareSheet#1").UsedRange
If Cell.Value <> Worksheets("CompareSheet#2").Range(Cell.Address) Then
Cell.Interior.ColorIndex = 3
End If
Next
For Each Cell In Worksheets("CompareSheet#2").UsedRange
If Cell.Value <> Worksheets("CompareSheet#1").Range(Cell.Address) Then
Cell.Interior.ColorIndex = 3
End If
Next
End Sub

Calculate max (i.e. the largest number) of certain cells in a row conditionally for a dynamic range

I am trying to create a macro that will find the maximum value (i.e. the largest) for specific columns in row.
Figure 1:
For example, In FIGURE 1 I have shown a simple example table ranging A1 to K12. Where the top 2 rows represent ‘Height’ and ‘Year’ respectively. And they are always in ascending order. The figure shows 2 years data and I am trying to create the maximum for each height between years. I have highlighted in red text what I am trying to do. For example, cell L3 is the Max of B3 and G3 (i.e. =MAX(B3,G3)) and similarly all the cells for range L3:P12 in red are the maximum values for each heights.
I know I can do this easily just by manually calculating using Max(cell1,cell2) function or by using the following Macro:
Sub test()
Range("G1").Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
Range("L1").Select
ActiveSheet.Paste
Range("L3").Select
Application.CutCopyMode = False
ActiveCell.FormulaR1C1 = "=MAX(RC[-10],RC[-5])"
Range("L3").Select
Selection.AutoFill Destination:=Range("L3:P3"), Type:=xlFillDefault
Range("L3:P3").Select
Selection.AutoFill Destination:=Range("L3:P12")
Range("L3:P12").Select
End Sub
But my actual table is far more larger with many more years of data with more heights and I will be running this in a loop for many spreadsheets. There for the number of rows and columns can vary. So I am just wondering how I can adopt a dynamic argument that will dynamically calculate the max based on the top two rows (i.e. height and year).
I was thinking if any way I could set a range for the top row as the height will be always increasing until the next year when it restart from the lowest value again. My plan was to then try to put some conditions to calculate the max values and autofill the range. But I am just not able to even define the range as I am strugling to logically plan this code. The following is what I have tried and I would really appreciate any guidance on how logically I could achieve this problem. Many thanks in Advance!
Sub test()
Dim LR As Long, i As Long, r As Range
LR = Range("1" & Columns.Count).End(xlToRight)
For i = 1 To LR
If Range("1" & i).Value > 10 Then
If r Is Nothing Then
Set r = Range("1" & i)
Else
Set r = Union(r, Range("1" & i))
End If
End If
Next i
r.Select
End Sub
Due to the unlimited possibility of height values, using a class was the best solution that I could think of for now. Hopefully this provides a good foundation to build from.
In a class module named 'HeightClass':
Option Explicit
Dim rngRangeStore As Range
Dim sValueStore As String
Public Property Set rngRange(rngInput)
Set rngRangeStore = rngInput
End Property
Public Property Get rngRange() As Range
Set rngRange = rngRangeStore
End Property
Public Property Let sValue(sInput As String)
sValueStore = sInput
End Property
Public Property Get sValue() As String
sValue = sValueStore
End Property
Then in a standard Module:
Option Explicit
Sub Get_Max()
Dim lRecord As Long, lRange As Long, lLastRecord As Long, lLastColumn As Long
Dim colRanges As New Collection
Dim clsRange As HeightClass
'Find Last used column in the year row
lLastColumn = Rows(2).Find(What:="*", SearchDirection:=xlPrevious).Column
'Find last used row in column 1
lLastRecord = Columns(1).Find(What:="*", SearchDirection:=xlPrevious).Row
For lRange = 2 To lLastColumn
On Error Resume Next
Set clsRange = Nothing
Set clsRange = colRanges(Trim$(Cells(1, lRange).Value))
On Error GoTo 0
If Not clsRange Is Nothing Then
'Add to existing range
Set clsRange.rngRange = Union(clsRange.rngRange, Cells(1, lRange))
Else
'Add range to colletion in order of smallest to largest
Set clsRange = New HeightClass
Set clsRange.rngRange = Cells(1, lRange)
clsRange.sValue = Cells(1, lRange).Value
If colRanges.Count = 0 Then
colRanges.Add Item:=clsRange, Key:=clsRange.sValue
Else
For lRecord = 1 To colRanges.Count
If clsRange.sValue < colRanges(lRecord).sValue Then
colRanges.Add Item:=clsRange, Key:=clsRange.sValue, Before:=colRanges(lRecord).sValue
Exit For
ElseIf lRecord = colRanges.Count Then
colRanges.Add Item:=clsRange, Key:=clsRange.sValue, After:=colRanges(lRecord).sValue
Exit For
End If
Next lRecord
End If
End If
Next lRange
'Place height headers
For lRange = 1 To colRanges.Count
With Cells(1, lLastColumn + lRange)
.Value = colRanges(lRange).sValue
.Font.Color = vbRed
End With
Next lRange
'Process each record
For lRecord = 3 To lLastRecord
For lRange = 1 To colRanges.Count
With Cells(lRecord, lLastColumn + lRange)
.Value = Application.Max(colRanges(lRange).rngRange.Offset(lRecord - 1))
.Font.Color = vbRed
.NumberFormat = "0.00"
End With
Next lRange
Next lRecord
End Sub
This is written to perform the desired process on whatever sheet is in focus.
So the array formula (enter it with Ctrl+Shift+Enter)version would be, in L3 etc.:
=MAX(IF($B$1:$K$1=L$1,$B3:$K3,""))
It says:
look in the headers $B$1:$K$1 to check a match for your column's height (=L$1)
if it matches, take the value ,$B3:$K3
otherwise ignore it ,""
take the MAX of those non-ignored values
I tried this with 100 columns (5 heights * 20 years) and 1000 rows of RAND produced random numbers and the recalculation time was negligible

VBA - Changing row shading when a column value changes even with filtered

I'm trying to write a macro to change the colors of rows when the values in column B change. Column A will be my controlling column using 1's and 0's, i.e. column A will stay a 1 as long as column B stays the same; whenever B changes, A will flip to a 0, and so on.
I can get it to color the rows correctly when the values in column B change, but the problem arises when I filter the data. For example: let's say I have B2-B4 set to "test1", B5-B7 set to "test2", and B8-B10 set to "test3", then I filter column B to not include "test2". Originally, the rows would be colored differently where the column values changed, but rows B2-B4 and B8-B10 are set to the same color and now they're touching since the "test2" rows are hidden.
Here's the code I used to color the rows, but it doesn't work for filtering:
Sub ColorRows()
Dim This As Long
Dim Previous As Long
Dim LastRow As Long
Dim Color As Integer
Dim R As Long
LastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
RwColor = Array(15,0)
Color = 0
For R = 2 To LastRow
This = Cells(R, 1).Value
Previous = Cells(R - 1, 1).Value
If This <> Previous Then Color = 1 - Color
Range("A" & R & ":M" & R).Select
Selection.Interior.ColorIndex = RwColor(Color)
Next R
End Sub
How can I fix it so that even after filtering the rows are colored correctly when there is a change in column values?
Here's a way to do this:
1.) Insert the code below as a UDF in a code module.
2.) Then put the formula in A, as A2: =analyseVisible(B2).
This will compare B-cells to the next visible cell above and result in a 'rank'-counter in A.
Now that the counter in A in contiunous (even if rows are hidden), you can use MOD 2 to color it with conditional formatting:
3.) Add a conditional format (from A2 for the whole table): =MOD($A2,2)=1 and set the fill color.
If you use the filter now or change values in B, the rows are re-colored in realtime.
Public Function analyseVisible(r As Range) As Integer
Dim i As Long
If Application.Caller.Row <= 2 Or _
r.Row <> Application.Caller.Row Then
analyseVisible = 1
Exit Function
End If
i = r.Row - 1
While r.Worksheet.Rows(i).Hidden And i > 1
i = i - 1
Wend
If i = 1 Then
analyseVisible = 1
Else
analyseVisible = r.Worksheet.Cells(i, Application.Caller.Column).Value
If r.Worksheet.Cells(i, r.Column).Value <> _
r.Value Then analyseVisible = analyseVisible + 1
End If
End Function
The code below handles the issue by checking only the used & visible rows. It works pretty well, but I was unable to figure out how to fire it when the filter changes. It also does it's comparisons directly on the values that are changing.
Private Sub colorRows()
Dim this As Variant
Dim previous As Variant
Dim currentColor As Long
Dim rng As Range 'visible range
Dim c As Range ' cell
' pick a color to start with
currentColor = vbYellow
' rng = used and visible cells
Set rng = ActiveSheet.UsedRange.SpecialCells(xlCellTypeVisible)
For Each c In rng ' For each cell that is visible and used
If Not c.Row = 1 Then ' skip header row
this = c.Value
'some simple test logic to switch colors
If this <> previous Then
If currentColor = vbBlue Then
currentColor = vbYellow
ElseIf currentColor = vbYellow Then
currentColor = vbBlue
End If
End If
'set interior color
c.Interior.color = currentColor
previous = this
End If
Next c
End Sub
Then, in the module of the worksheet that you want to colorize, call the sub from the Worksheet_Activate() event. (In reality, you probably want a different event. I mostly work with Access, so I don't really know what's available to you. I'm just trying to point you in the right direction to what I'm sure is your next question if you stick with the method you started with.)
Private Sub Worksheet_Activate()
colorRows
End Sub