I need your help in nexted VBA loop. I have some data in two columns and blank rows between rows. This macro loop through a column and find out if it contain certain character. If it' blank then I want it to move to next row. If it contain "Den", then select a specific worksheet ("D-Temp") else select ("M-Temp").
After selecting right Worksheet, it need to fill up text boxs with data from 2nd column as per Row no. The code I have created so far is
Sub Template()
Dim j As Long
Dim c As Range, t As Range
Dim ws As String
j = 5
With Sheets("Sample ")
For Each c In .Range("I3", .Cells(.Rows.Count, "I").End(xlUp))
If c.Value = "" Then
Next ' `Not getting how to jump to next one`
ElseIf c.Value = "DEN" Then
ws = "D-Temp"
Else
ws = "M-Temp"
End If
For Each t In .Range("P3", .Cells(.Rows.Count, "P").End(xlUp))
If t.Value <> "" Then
j = j + 1
Sheets("M-Temp").Copy after:=Sheets(Sheets.Count)
ActiveSheet.Shapes("Textbox 1").TextFrame.Characters.Text = t.Value
ActiveSheet.Shapes("textbox 2").TextFrame.Characters.Text = t.Offset(, -1).Value
End If
Next
Next
End With
Any help ??
Below is the sample Data I have :
Type Name 1 Name2
DEN Suyi Nick
'Blank row'
PX Mac Cruise
I want macro to Identify Type & select template worksheet (D or M) as per that and fill textboxes on that template with Name 1 & Name2 respectively.
may be you're after this:
Option Explicit
Sub Template()
Dim c As Range
With Sheets("Sample")
For Each c In .Range("I3", .Cells(.Rows.Count, "I").End(xlUp)).SpecialCells(xlCellTypeConstants) ' loop through referenced sheet column C not empty cells form row 3 down to last not empty one
Worksheets(IIf(c.Value = "DEN", "D-Temp", "M-Temp")).Copy after:=Sheets(Sheets.Count) ' create copy of proper template: it'll be the currently "active" sheet
With ActiveSheet ' reference currently "active" sheet
.Shapes("Textbox 1").TextFrame.Characters.Text = c.Offset(, 7).Value ' fill referenced sheet "TextBox 1" shape text with current cell (i.e. 'c') offset 7 columns (i.e. column "P") value
.Shapes("Textbox 2").TextFrame.Characters.Text = c.Offset(, 6).Value ' fill referenced sheet "TextBox 2" shape text with current cell (i.e. 'c') offset 6 columns (i.e. column "O") value
End With
Next
End With
End Sub
If I'm not mis-understanding your current nesting...
With Sheets("Sample ")
For Each c In .Range("I3", .Cells(.Rows.Count, "I").End(xlUp))
If c.Value <> "" Then
If c.Value = "DEN" Then
ws = "D-Temp"
Else
ws = "M-Temp"
End If
For Each t In .Range("P3", .Cells(.Rows.Count, "P").End(xlUp))
If t.Value <> "" Then
j = j + 1
Sheets("M-Temp").Copy after:=Sheets(Sheets.Count)
ActiveSheet.Shapes("Textbox 1").TextFrame.Characters.Text = t.Value
ActiveSheet.Shapes("textbox 2").TextFrame.Characters.Text = t.Offset(, -1).Value
End If
Next
End if 'not blank
Next
End With
If I understand your question, correctly, you need to change your if/then logic slightly:
Sub Template()
Dim j As Long
Dim c As Range, t As Range
Dim ws As String
j = 5
With Sheets("Sample ")
For Each c In .Range("I3", .Cells(.Rows.Count, "I").End(xlUp))
If c.Value <> "" Then
If c.Value = "DEN" Then
ws = "D-Temp"
Exit For
Else
ws = "M-Temp"
Exit For
End If
End If
Next
For Each t In .Range("P3", .Cells(.Rows.Count, "P").End(xlUp))
If t.Value <> "" Then
j = j + 1
Sheets("M-Temp").Copy after:=Sheets(Sheets.Count)
ActiveSheet.Shapes("Textbox 1").TextFrame.Characters.Text = t.Value
ActiveSheet.Shapes("textbox 2").TextFrame.Characters.Text = t.Offset(, -1).Value
End If
Next
End With
End Sub
You might want to add code to make sure that ws is set to something (not all columns were blank).
Related
I am very new to VBA and have been stuck on this for a few days now.
I would like to compare H2 and H3. If equal then turn the cell green , If not equal then turn the cell red.
Once this is complete I would like to do the same for H4 and H5 , then H6 and H7...... all the way down to the last row of data.
Thank you in advance for your help .
How about something like this?
Sub ForLoopTest()
Dim loop_ctr As Integer
Dim Max As Integer
Max = ActiveSheet.UsedRange.Rows.Count
For loop_ctr = 1 To Max
If loop_ctr Mod 2 = 0 Then
row_below = loop_ctr + 1
If Cells(loop_ctr, "H") = Cells(row_below, "H") then
Cells(loop_ctr, "H").Interior.ColorIndex = 4
Cells(row_below, "H").Interior.ColorIndex = 4
Else
Cells(loop_ctr, "H").Interior.ColorIndex = 3
Cells(row_below, "H").Interior.ColorIndex = 3
End If
End If
Next loop_ctr
End Sub
I still feel like conditional formatting is they way to go here so that it's reactive to values changing in the worksheet, but if you are stuck on VBA as a solution here, something like this should do the trick:
Sub greenOrRed()
Dim lngRow As Long
For lngRow = 2 To Sheet1.Range("H2").End(xlDown).Row Step 2
If Sheet1.Range("H" & lngRow).Value = Sheet1.Range("H" & lngRow + 1).Value Then
Sheet1.Range("H" & lngRow & ":H" & lngRow + 1).Interior.ColorIndex = 4
Else 'didn't match
Sheet1.Range("H" & lngRow & ":H" & lngRow + 1).Interior.ColorIndex = 3
End If
Next lngRow
End Sub
You could also use a For Each loop to walk down the column which makes for some nice to read code. You just have to apply a test for Mod 2 on the row you are analyzing instead of using the very handy STEP 2 like in the For loop above:
Sub greenOrRed()
Dim rngCell As Range
For Each rngCell In Sheet1.Range("H:H").Cells
If rngCell.Value = "" And rngCell.Row > 1 Then Exit For
If rngCell.Row Mod 2 = 0 Then
If rngCell.Value = rngCell.Offset(1).Value Then
rngCell.Resize(2).Interior.ColorIndex = 4
Else
rngCell.Resize(2).Interior.ColorIndex = 3
End If
End If
Next rngCell
End Sub
And if you really want to condense it you can apply some boolean math to the setting of the interior.ColorIndex, but this only works because red and green are 1 colorindex value away from each other. Also the next person that adopts your code will hate you and won't think your nearly as clever as you think you are.
Sub greenOrRed()
Dim rngCell As Range
For Each rngCell In Sheet1.Range("H:H").Cells
If rngCell.Value = "" And rngCell.Row > 1 Then Exit For
If rngCell.Row Mod 2 = 0 Then rngCell.Resize(2).Interior.ColorIndex = 3 + Abs(rngCell.Value = rngCell.Offset(1).Value)
Next rngCell
End Sub
some other ways
another loop approach:
Sub CompareCells()
Dim i As Long
With Range("H2", Cells(Rows.Count,"H").End(xlUp)) ' reference column H cells from row 2 down to last not empty one
For i = 1 To .Count Step 2 ' loop through referenced range skipping every other row
With .Cells(i, 1) ' reference current cell
.Interior.Color = IIf(.Value2 = .Offset(1).Value2, vbGreen, vbRed) 'set current cell color with respect to below cell content
End With
Next
End With
End Sub
a no-loop approach:
Sub CompareCells()
With Range("H2", Cells(Rows.Count, "H").End(xlUp)) ' reference column H cells from row 2 down to last not empty one
With .Offset(, 1) ' reference referenced range 1 column to the right offset range. this is a "helpre" column
.FormulaR1C1 = "=IF(even(row())=row(),1,"""")" ' write 1's every two rows in referenced range
With .SpecialCells(xlCellTypeFormulas, xlNumbers) ' reference referenced range "numbered" rows
.Offset(, -1).Interior.Color = vbRed ' mark referenced range 1 column left offset in red
.FormulaR1C1 = "=IF(RC[-1]=R[1]C[-1],1,"""")" ' signal referenced range cells with 1 if corresponding 1 column to the left offset cell content equals its below cell content
.SpecialCells(xlCellTypeFormulas, xlNumbers).Offset(, -1).Interior.Color = vbGreen ' turn reference referenced range "numbered" cells color to green
End With
.ClearContents ' clear referenced "helper" column
End With
End With
End Sub
I have created an excel VBA script to move rows to different sheets based on the status of the item in that row. However, when I run the code it does not always move all the items at once if there is more than one status update. I would like to make it so that if multiple rows have status updates, when I run the script they all move at once. I'm assuming it has something to do with the "if statements" but I am drawing a blank on any other ways to do it. Any help is greatly appreciated. Thanks!
Below is my code:
Sub MoveRows()
Dim xRg As Range
Dim xCell As Range
Dim A As Long
Dim B As Long
Dim C As Long
Dim D As Long
A = Worksheets("Tracking").UsedRange.Rows.Count
B = Worksheets("In Progress").UsedRange.Rows.Count
C = Worksheets("Completed").UsedRange.Rows.Count
D = Worksheets("Removed").UsedRange.Rows.Count
If B = 1 Then
If Application.WorksheetFunction.CountA(Worksheets("In Progress").UsedRange) = 0 Then B = 0
ElseIf C = 1 Then
If Application.WorksheetFunction.CountA(Worksheets("Completed").UsedRange) = 0 Then C = 0
ElseIf D = 1 Then
If Application.WorksheetFunction.CountA(Worksheets("Removed").UsedRange) = 0 Then D = 0
End If
Set xRg = Worksheets("Tracking").Range("S1:S" & A)
On Error Resume Next
Application.ScreenUpdating = False
For Each xCell In xRg
If CStr(xCell.Value) = "In Progress" Then
xCell.EntireRow.Copy Destination:=Worksheets("In Progress").Range("A" & B + 1)
xCell.EntireRow.Delete
B = B + 1
ElseIf CStr(xCell.Value) = "Completed" Then
xCell.EntireRow.Copy Destination:=Worksheets("Completed").Range("A" & C + 1)
xCell.EntireRow.Delete
C = C + 1
ElseIf CStr(xCell.Value) = "Remove" Then
xCell.EntireRow.Copy Destination:=Worksheets("Removed").Range("A" & D + 1)
xCell.EntireRow.Delete
D = D + 1
End If
Next
Application.ScreenUpdating = True
End Sub
edited to add selected rows deletion
just be sure to have a header row in your "Tracking" worksheet and then AutoFilter() will make your life as easy as the following code:
Option Explicit
Sub MoveRows()
Application.ScreenUpdating = False
With Worksheets("Tracking")
With .Range("S1", .Cells(.Rows.count, "S").End(xlUp))
FilterAndCopy .Cells, "In Progress"
FilterAndCopy .Cells, "Completed"
FilterAndCopy .Cells, "Remove"
End With
End With
Application.ScreenUpdating = True
End Sub
Sub FilterAndCopy(rng As Range, filterStrng As String)
With rng '<--| reference passed 'rng' range
.AutoFilter Field:=1, Criteria1:=filterStrng '<--| filter its 1st column with passed 'filterStrng'
If Application.WorksheetFunction.Subtotal(103, .Resize(, 1)) > 1 Then '<--| if any cell filtered other than header
With .Resize(.Rows.count).Offset(1).SpecialCells(xlCellTypeVisible).EntireRow
.Copy Destination:=Worksheets(filterStrng).Cells(Rows.count, "A").End(xlUp).Offset(1) '<--|copy filtered cells (skipping headers row) to passed 'filterStrng' named worksheet 1st column from its column A first empty row after last not empty one
.Delete
End With
End If
.Parent.AutoFilterMode = False
End With
End Sub
I have two columns of numbers, together they will be unique (composite key). I would like to create an unique ID number (third column) similar to how MS Access would use a primary key. I would like to do this in VBA but I am stuck on how to do it.
My VBA in excel isn't very good so hopefully you can see what I've started to attempt. it may be completely wrong... I don't know?
I don't know how to make the next concatenation and I am unsure about how to go down to the next row correctly.
Sub test2()
Dim var As Integer
Dim concat As String
concat = Range("E2").Value & Range("F2").Value
var = 1
'make d2 activecell
Range("D2").Select
Do Until concat = ""
'if the concat is the same as the row before we give it the same number
If concat = concat Then
var = var
Else
var = var + 1
End If
ActiveCell.Value = var
ActiveCell.Offset(0, 1).Select
'make the new concatination of the next row?
Loop
End Sub
any help is appreciated, thanks.
Give the code below a try, I've added a loop which executes for each cell in the E Column. It checks if the concat value is the same as the concat value in the row above and then writes the id to the D cell.
Sub Test2()
Dim Part1 As Range
Dim strConcat As String
Dim i As Long
i = 1
With ThisWorkbook.Worksheets("NAME OF YOUR SHEET")
For Each Part1 In .Range(.Cells(2, 5), .Cells(2, 5).End(xlDown))
strConcat = Part1 & Part1.Offset(0, 1)
If strConcat = Part1.Offset(-1, 0) & Part1.Offset(-1, 1) Then
Part1.Offset(0, -1).Value = i
Else
i = i + 1
Part1.Offset(0, -1).Value = i
End If
Next Part1
End With
End Sub
Something like this should work, this will return a Unique GUID (Globally Unique Identifier):
Option Explicit
Sub Test()
Range("F2").Select
Do Until IsEmpty(ActiveCell)
If (ActiveCell.Value <> "") Then
ActiveCell.Offset(0, 1).Value = CreateGUID
End If
ActiveCell.Offset(1, 0).Select
Loop
End Sub
Public Function CreateGUID() As String
CreateGUID = Mid$(CreateObject("Scriptlet.TypeLib").GUID, 2, 36)
End Function
If you walk down column D and examine the concatenated values from column E and F with the previous row, you should be able to accomplish your 'primary key'.
Sub priKey()
Dim dcell As Range
With Worksheets("Sheet12")
For Each dcell In .Range(.Cells(2, 4), .Cells(Rows.Count, 5).End(xlUp).Offset(0, -1))
If LCase(Join(Array(dcell.Offset(0, 1).Value2, dcell.Offset(0, 2).Value2), ChrW(8203))) = _
LCase(Join(Array(dcell.Offset(-1, 1).Value2, dcell.Offset(-1, 2).Value2), ChrW(8203))) Then
dcell = dcell.Offset(-1, 0)
Else
dcell = Application.Max(.Range(.Cells(1, 4), dcell.Offset(-1, 0))) + 1
End If
Next dcell
End With
End Sub
You could use collections as well.
Sub UsingCollection()
Dim cUnique As Collection
Dim Rng As Range, LstRw As Long
Dim Cell As Range
Dim vNum As Variant, c As Range, y
LstRw = Cells(Rows.Count, "E").End(xlUp).Row
Set Rng = Range("E2:E" & LstRw)
Set cUnique = New Collection
On Error Resume Next
For Each Cell In Rng.Cells
cUnique.Add Cell.Value & Cell.Offset(, 1), CStr(Cell.Value & Cell.Offset(, 1))
Next Cell
On Error GoTo 0
y = 1
For Each vNum In cUnique
For Each c In Rng.Cells
If c & c.Offset(, 1) = vNum Then
c.Offset(, -1) = y
End If
Next c
y = y + 1
Next vNum
End Sub
How can I read in Visual Basic from column B, sheet "control" in Excel cell for cell till an empty cell?
After that I would like to generate for every cell a new sheet with the name from cells. In this:
:
you see the content of this column, which could be different from time to time. After reading it I want to generate empty sheets with names: RW_BONDS, ... .
You can do something like this.
Private Sub CommandButton1_Click()
Dim ws As Excel.Worksheet
Dim lRow As Long
Dim lastRow As Long
'Set the sheet to read from
Set ws = Application.Sheets("control")
'Set the row to start reading at
lRow = 3
lastRow = wsOwners.Cells(wsOwners.Rows.Count, "B").End(xlUp).Row
'Loop through the rows
Do While lRow <= lastRow
If ws.Range("B" & lRow).Value <> "" then
'Add a new sheet
ActiveWorkbook.Sheets.Add Before:=Worksheets(Worksheets.Count)
'Change the name to the value of column B in the current row
ActiveWorkbook.ActiveSheet.Name = ws.Range("B" & lRow).Value
End If
'Increment your row to the next one
lRow = lRow + 1
Loop
End Sub
Sub test()
Dim i As Long
i = 1
While Len(Sheets("Control").Cells(i, 2))
Worksheets.Add.Name = Sheets("Control").Cells(i, 2): i = i + 1
Wend
End Sub
EDIT answer for the comment:
Sub test()
Dim i As Long
i = 1
With Sheets("Control")
On Error Resume Next
Application.DisplayAlerts = False
While Len(.Cells(i, 2))
If Len(Sheets(.Cells(i, 2).Value).Name) = 0 Then Else Sheets(.Cells(i, 2).Value).Delete
Worksheets.Add.Name = .Cells(i, 2): i = i + 1
Wend
Application.DisplayAlerts = True
On Error GoTo 0
End With
End Sub
set ws = worksheets("Source")
row = 1
col = "B"
Do
row = row + 1
if ws.range(col & row).text = "" then exit do
worksheets.add.name = ws.range(col & row).text
Loop
End Sub
Sub createSheets()
With Worksheets("control")
iRow = 1 'Start on the first row
While Len(.Cells(iRow, 2)) > 0 'While there isn't a blank cell
Worksheets.Add.Name = .Cells(iRow,2) 'Create/rename sheet
iRow = iRow + 1
Wend
End With
End Sub
I have five worksheet in all that are using the below code which is stored in a workbook. The first worksheet works perfectly well with the code. The second spreadsheet can check for the first item before returning the error. The subsequent third and fourth worksheet return the error immediately. The fifth worksheet on the other hand return error 400. May I know is my code the source of the problem or it's the checkbox because I copied and paste from the first worksheet.
Sub test5()
Dim MyFile As String
Dim FinalRow As Long
Dim Row As Long
Dim i As Integer
Dim d As Integer
d = 2
i = 0
FinalRow = Cells(Rows.count, "S").End(xlUp).Row
For Row = 3 To FinalRow
If Not IsEmpty(ActiveSheet.Cells(Row, "S")) Then
i = i + 1
d = d + 1
MyFile = ActiveSheet.Cells(Row, "S").Value
If Dir(MyFile) <> "" Then
ActiveSheet.OLEObjects("CheckBox" & i). _
Object.Value = True ' <~~~~~~~~~~~~~~~~ Error occurs here
With ActiveSheet.Cells(d, "F")
.Value = Now
.NumberFormat = "dd/mm/yy"
'If (ActiveSheet.Cells(d, "F") - ActiveSheet.Cells(d, "G") >= 0) Then
' ActiveSheet.Cells(d, "F").Font.Color = vbRed
'End If
If (.Value - .Offset(0, 1).Value) >= 0 Then
.Font.Color = vbRed
Else
.Font.Color = vbBlack
End If
End With
' i = i + 1
'd = d + 1
End If
End If
Next
End Sub
The program terminates after stepping into this line of code:
ActiveSheet.OLEObjects("CheckBox" & i). _ Object.Value = True
OLEObject does not have a member called value. If you are trying to display the OLEObject, use visible instead
ActiveSheet.OLEObjects("CheckBox" & i).Visible = True
See all OLEObject members here :
OLEObject Object Members