VBA Code to Autofill - vba

Have a column H with alphanumeric characters. Some cells in this column have the content (RAM) followed by 5 digits starting from 00000 to 99999.
If cell H219 has the content (RAM) 23596 then i have to fill cell A219 with a comment "completed".
This has to be done for all cells with the content "(RAM) followed by 5 digits"
Sub Macro16_B()
' ' Macro16_B Macro ' '
intRowCount = Worksheets("Reconciliation").UsedRange.Rows.Count
For i = 11 To intRowCount
If InStr(Range("H" & i).Value, "(RAM 00000-99999") Then
Range("A" & i).Value = "Completed"
End If
Next i
End Sub

A non-VBA answer could be (if the cell doesn't have extra text other than (RAM) & 5 numbers):
=IFERROR(IF(LEN(VALUE(TRIM(SUBSTITUTE(H1,"(RAM)",""))))=5,"completed",""),"")
My VBA answer would be:
Sub Test()
Dim rLastCell As Range
Dim rCell As Range
With Worksheets("Reconciliation")
Set rLastCell = .Columns(8).Find("*", , , , xlByColumns, xlPrevious)
If Not rLastCell Is Nothing Then
For Each rCell In .Range(.Cells(1, 8), rLastCell)
If rCell Like "*(RAM) #####*" Then
rCell.Offset(, -7) = "complete"
End If
Next rCell
End If
End With
End Sub
Cheers #Excelosaurus for heads up on the * would've forgotten it as well. :)

One way is to use the Like operator. The precise format of your string is not clear so you may have to amend (and assuming case insensitive). # represents a single number; the * represents zero or more characters.
Sub Macro16_B()
Dim intRowCount As Long, i As Long
' ' Macro16_B Macro ' '
intRowCount = Worksheets("Reconciliation").UsedRange.Rows.Count
For i = 11 To intRowCount
If Range("H" & i).Value Like "(RAM) #####*" Then
Range("A" & i).Value = "Completed"
End If
Next i
End Sub

Well, there are already 2 good answers, but allow me to paste my code here for good measure, the goal being to submerge #user2574 with code that can be re-used in his/her next endeavors:
Sub Macro16_B()
'In the search spec below, * stands for anything, and # for a digit.
'Remove the * characters if you expect the content to be limited to "(RAM #####)" only.
Const SEARCH_SPEC As String = "*(RAM #####)*"
Dim bScreenUpdating As Boolean
Dim bEnableEvents As Boolean
'Keep track of some settings.
bScreenUpdating = Application.ScreenUpdating
bEnableEvents = Application.EnableEvents
On Error GoTo errHandler
'Prevent Excel from updating the screen in real-time,
'and disable events to prevent unwanted side effects.
Application.ScreenUpdating = False
Application.EnableEvents = False
'Down with business...
Dim scanRange As Excel.Range
Dim cell As Excel.Range
Dim content As String
Dim ramOffset As Long
With ThisWorkbook.Worksheets("Reconciliation").Columns("H")
Set scanRange = .Worksheet.Range(.Cells(11), .Cells(.Cells.Count).End(xlUp))
End With
For Each cell In scanRange
content = CStr(cell.Value2)
If content Like SEARCH_SPEC Then
cell.EntireRow.Columns("A").Value = "Completed"
End If
Next
Recover:
On Error Resume Next
'Restore the settings as they were upon entering this sub.
Application.ScreenUpdating = bScreenUpdating
Application.EnableEvents = bEnableEvents
Exit Sub
errHandler:
MsgBox Err.Description, vbExclamation + vbOKOnly, "Error"
Resume Recover
End Sub

Related

Inserting values from a UserForm to excel sheet which start from a fixed cell?

I have a Cell range Sheets("INVOICE MAKER").Range("D18:D37") (Total 20 Cells), and a little UserForm with name Add Items.
In UserForm there are one Textbox and one Submit Button.
So if I write something in that Textbox and click on Submit Button, Data should be write to next available empty cell in range Sheets("INVOICE MAKER").Range("D18:D37"). And if all 20 cells are filled with data then show a message like "No more rows are available to write data".
Below code don't start writing data from Cell D18, its start writing data from D1.
and doesn't stop after cell D37.
Option Explicit
Dim lRow As Long
Dim ws As Worksheet
Set ws = Worksheets("INVOICE MAKER")
lRow = ws.Cells.Find(What:="*", SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, LookIn:=xlValues).Row + 1
'check for a part number
If Trim(Me.Textbox.Value) = "" Then
Me.Textbox.SetFocus
MsgBox "Please Type Item Name"
Exit Sub
End If
With ws
.Cells(lRow, 5).Value = Me.Textbox.Value
End With
End Sub
Hope below code will help you:
Public Working_Sheet As Worksheet
Public All_Cell_Value As Boolean
Public Write_Cell_No As Integer
Public Content As String
'When button in the form is clicked
Sub Button1_Click()
Write_Content
End Sub
'validation and content writing function
Public Function Write_Content()
All_Cell_Value = True
Set Working_Sheet = Worksheets("Sheet1")
For i = 18 To 37
If Trim(Working_Sheet.Cells(i, "D")) = "" Then
All_Cell_Value = False
Write_Cell_No = i
Exit For
End If
Next i
If All_Cell_Value = False Then
Content = InputBox("Enter the value")
If Content = "" Then
MsgBox ("No Data")
Else
Working_Sheet.Cells(i, "D").Value = Content
End If
Else
MsgBox ("Sorry content is full")
End If
End Function
Maybe this will help!
EDIT #1
Fix some errors, and the code must to be inside a Form, with the TextBox and the Button
EDIT #2
Added a closing statement for the userform or the macro
Option Explicit
Private Sub CommandButton1_Click()
Dim lRow As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("INVOICE MAKER") 'Set from Thisworkbook
ws.Activate 'activate the Invoice Maker
'lRow = ws.Cells.Find(What:="*", SearchOrder:=xlRows, SearchDirection:=xlPrevious, LookIn:=xlValues).Row + 1
Dim iniCell As Integer: iniCell = 18
Dim endCell As Integer: endCell = 37
Dim setCol As Integer: setCol = ws.Range("D1").Column
'As you said, the range is D18:D37, then you can manipulate from this 3 vars
Dim iRange As Range
Dim i As Range
Dim isTheRangeFull As Boolean: isTheRangeFull = False
Dim iMsgbox As Integer
If Trim(Me.TextBox.Value) = "" Then
Me.TextBox.SetFocus
'Ask the user to retry or quit.
iMsgbox = MsgBox("Please Type Item Name" & Chr(10) & "Do you want to retry?", vbYesNo + vbDefaultButton1)
If iMsgbox = 6 Then
GoTo InsertData
'if the user say YES
'do it again
ElseIf iMsgbox = 7 Then
End
'if the user say NO
End If
End If
Set iRange = ws.Range(Cells(iniCell, setCol), Cells(endCell, setCol))
'set your working Range
'This Loop do the job!
For Each i In iRange
isTheRangeFull = True
'if there is no empty cell, won't enter the if, and
'the var continue TRUE, so there is no empty cells...
If i.Value = Empty Then
i.Value = Me.TextBox.Value
isTheRangeFull = False
'the Next line (End) Will close the Form and terminate the macro
'End
'The next line just close the userform
'Unload Me
'Decide which one to uncomment.
Exit For
End If
Next i
If isTheRangeFull Then
MsgBox "No more rows are available to write data"
End
End If
'With ws
' .Cells(lRow, 5).Value = Me.TextBox.Value
'End With
InsertData:
End Sub

VBA copy value of named_range into TextBox

The named range (or user defined range) has 6 columns, but number of rows may change based on other functions.
Now trying to get the entire value of that range, of all 6 columns and all rows into a TextBox. Rows delimited by tab.
Following code returns object defined error etc.
UserForm1.TextBox1.Value = Sheet1.Range("named_range").Value
Any help much appreciated.
Edit:
FunThomas is right, I won'e be able to get the value of the range without declaring it an separate line. So I started with the below code which works well in displaying the range in a multiline msgBox.
How do I get rid of the extra InputBox, and display results in UserForm1.TextBox1 instead of the msgBox which has limited functionality.
Sub showOfferRange()
Dim xRg As Range
Dim xTxt As String
Dim xCell As Range
Dim xStr As String
Dim xRow As Long
Dim xCol As Long
On Error Resume Next
xTxt = ThisWorkbook.Names("offers_running")
Set xRg = Application.InputBox("Offer range:", "Display offers", xTxt, , , , , 8)
'Set xRg = xTxt
If xRg Is Nothing Then Exit Sub
On Error Resume Next
For xRow = 1 To xRg.Rows.Count
For xCol = 1 To xRg.Columns.Count
xStr = xStr & xRg.Cells(xRow, xCol).Value & vbTab
Next
xStr = xStr & vbCrLf
Next
MsgBox xStr
End Sub
Basically, to write the text into the textbox of the form, it´s just
UserForm1.TextBox1.text = UserForm1.TextBox1
UserForm1.show
As you have multiple lines, don´t forget to set the MultiLine-Property of the textbox to true.
Another point: You should change the lines where you read the range. The on error resume next is a very dangerous statement as it eat up all errors and you can get completly lost to find a bug. It's only needed to catch the case that the inputbox doesn't return a Range (for example because Cancel was pressed). So I suggest you change the steatements to
xTxt = ThisWorkbook.Names("offers_running")
Set xRg = Nothing
On Error Resume Next
Set xRg = Application.InputBox("Offer range:", "Display offers", xTxt, , , , , 8)
On Error Goto 0
If xRg Is Nothing Then Exit Sub

Excel macro - finding cells that are used offsheet

I use the standard finance cell formatting where inputs are blue, cells that reference anything offsheet are green, and everything else is black.
All well and good - I was capable of developing macros that basically do what the GoTo -> constants -> numbers and GoTo -> formulas and then looks within the formula text for a "!" symbol.
However is there a way to select and highlight (say, in purple) all cells that are used offsheet, regardless of whether they are input as constants or formulas or whatever on the original sheet?
ie: I'd like to be able to quickly find and identify any cells that are used offsheet via macro. I'm good at making macros in general, but just can't think up any functionality that would accomplish this. Can anyone give me a hint to get me started in the right direction?
EDIT: What I have so far:
Sub Offsheet_Dependents()
Dim xRg As Range
Dim xCell As Range
Dim xTxt As String
On Error Resume Next
xTxt = ActiveWindow.RangeSelection.Address
Set xRg = Application.InputBox("Please select the data range:", "EDI macro", xTxt, , , , , 8)
If xRg Is Nothing Then Exit Sub
' Need to modify the below for loop to only highlight cells where the reference is offsheet. Currently higlights entire range.
' also need to add a cell.cleararrows command somewhere and have it work
For Each cell In xRg
cell.ShowDependents
Worksheet.cell.NavigateArrow TowardPrecedent:=False, ArrowNumber:=1, LinkNumber:=1
If ActiveCell.Worksheet.Name <> Worksheet.cell.Worksheet.Name Then
cell.Interior.Color = RGB(204, 192, 218)
End If
xRg.Select.ActiveSheet.ClearArrows
Next
End Sub
Another possibility, but the second macro doesn't successfully apply the first one across the range :( :
Sub Color_Dependents()
Dim rLast As Range, iLinkNum As Integer, iArrowNum As Integer
Dim stMsg As String
Dim bNewArrow As Boolean
Application.ScreenUpdating = False
ActiveCell.ShowDependents
Set rLast = ActiveCell
iArrowNum = 1
iLinkNum = 1
bNewArrow = True
Do
Do
Application.Goto rLast
On Error Resume Next
ActiveCell.NavigateArrow Towardprecedent:=False, ArrowNumber:=iArrowNum, LinkNumber:=iLinkNum
If Err.Number > 0 Then Exit Do
On Error GoTo 0
If rLast.Address(External:=True) = ActiveCell.Address(External:=True) Then Exit Do
bNewArrow = False
If rLast.Worksheet.Parent.Name = ActiveCell.Worksheet.Parent.Name Then
If rLast.Worksheet.Name = ActiveCell.Parent.Name Then
' local
stMsg = stMsg & vbNewLine & Selection.Address
Else
stMsg = stMsg & vbNewLine & "'" & Selection.Parent.Name & "'!" & Selection.Address
End If
Else
' external
stMsg = stMsg & vbNewLine & Selection.Address(External:=True)
End If
iLinkNum = iLinkNum + 1 ' try another link
Loop
If bNewArrow Then Exit Do
iLinkNum = 1
bNewArrow = True
iArrowNum = iArrowNum + 1 'try another arrow
Loop
rLast.Parent.ClearArrows
Application.Goto rLast
If stMsg Like "*!*" Then
ActiveCell.Interior.Color = RGB(204, 192, 218)
End If
End Sub
Sub Purple_Range()
Dim xRg As Range
Dim xCell As Range
Dim xTxt As String
On Error Resume Next
xTxt = ActiveWindow.RangeSelection.Address
Set xRg = Application.InputBox("Please select the data range:", "EDI macro", xTxt, , , , , 8)
Set xRg = Application.Union(xRg, ActiveSheet.UsedRange)
If xRg Is Nothing Then Exit Sub
For Each cell In xRg
Call Color_Dependents
Next cell
End Sub
In Sub Purple_Range()
replace:
For Each cell In xRg
Cell.Select
Next cell
with:
For Each cell In xRg
Cell.Select
Call Color_Dependents
Next Cell
The reason why the second Macro was failing was because Color_Dependents() was updating the color of the current ActiveCell and Purple_Range() was cycling through the range with out updating the location of ActiveCell to make it current.
Otherwise the Macro was working fine.

VBA Record date of row change in specific column

I'm trying to automatically update the "Updated" column of an excel spreadsheet when any cell of that specific row changes to today's date. I was able to do this by hard-coding where the "Updated" column header would be, however, it is now necessary to search for that column header as it may move.
The code I am trying to implement works but immediately gives me the error Automation error - The object invoked has disconnected from it's clients.
Any help would be appreciated. Here is the code I have currently:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:DX")) Is Nothing Then
Dim f As Range
Set f = ActiveSheet.Range("A1:DD1").Find("Updated", lookat:=xlWhole)
' f.Row = Range(Target).Row
If Not f Is Nothing Then
Range(Split(f.Address, "$")(1) & Target.Row).Value = Now
Else
MsgBox "'Updated' header not found!"
End If
End If
End Sub
You got into an endless loop.
Try this:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:DX")) Is Nothing Then
Dim f As Range
Set f = ActiveSheet.Range("A1:DD1").Find("Updated", lookat:=xlWhole)
' f.Row = Range(Target).Row
If f Is Nothing Then
MsgBox "'Updated' header not found!"
ElseIf Intersect(Target, f.EntireColumn) Is Nothing Then
Intersect(Target.EntireRow, f.EntireColumn).Value = Now
' Else
' MsgBox "We entered this function again because the row above updated the Updated column", vbInformation, "False alarm"
End If
End If
End Sub
To understand what happens,
Uncomment the else and MsgBox
Put a breakpoint on the MsgBox
When you hit it, press [ctrl]-L
In a case such as this, I run into far fewer problems when I simply loop through the available cells to find the column header. Using the .Find method also works, but is less "tunable" to my needs in a custom application.
Public Function FindColumn(header As String) As Long
Dim lastCol As Long
Dim headerCol As Long
Dim i As Long
Dim sh As Worksheet
Set sh = ThisWorkbook.Sheets("VTO2 Labor")
lastCol = sh.Cells(1, sh.Columns.Count).End(xlToLeft).Column
headerCol = 0
For i = 1 To lastCol
If sh.Cells(1, i).Value = header Then
headerCol = i
End If
Next i
FindColumn = headerCol
End Function
It isn't clear on whether the Updated column header could be in row 1 or if it will always be in row 1, just not in the same location.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:DX")) Is Nothing Then
On Error GoTo bm_SafeExit
'TURN OFF EVENTS IF YOU ARE GOING TO WRITE A VALUE INTO THE WORKSHEET!
Application.EnableEvents = False
Dim uCol As Long, f As Range
If Application.CountIf(Rows(1), "updated") Then
uCol = Application.Match("updated", Rows(1), 0)
For Each f In Intersect(Target, Range("A:DX"))
If f.Row > 1 Then _
Cells(f.Row, uCol) = Now
Next f
Else
MsgBox "'Updated' header not found!"
End If
End If
bm_SafeExit:
Application.EnableEvents = True
End Sub
That should survive multiple updates (e.g. when pasting values). The problem I see is that is the Updated column is being shifted around, presumably through inserting columns or the like, then the change routine is going to run.

Cannot use named range when it is empty

I have a named range lstVendors that refers to: =OFFSET(Data!$W$2,0,0,COUNTA(Data!$W$2:$W$400),1). I want this range to be populated when the workbook opens. I have the following code for this:
Private Sub Workbook_Open()
Application.WindowState = xlMaximized
Dim rslt()
Dim i As Integer
Dim n As Integer
Dim startRng As Range
Dim DropDown1 As DropDown
ThisWorkbook.Sheets("Dashboard").Shapes("TextBox 6").Visible = False
' Range("lstVendors").Offset(0, 0).Value = "Please Select..."
' Set DropDown1 = ThisWorkbook.Sheets("Dashboard").DropDowns("Drop Down 1")
' DropDown1.Value = 1
On Error Resume Next
If Not IsError(Range("lstVendors")) Then
Range("lstVendors").ClearContents
End If
On Error GoTo 0
rslt = Application.Run("SQLite_Query", "path/to/my/sqlite", "SELECT PROGRAM_ID FROM VENDOR;")
Set startRng = Range("lstVendors")
i = 0
For n = 2 To UBound(rslt)
Range("lstVendors").Offset(i, 0).Value = rslt(n)(0)
i = i + 1
Next n
End Sub
It errors on the Set startRng = Range("lstVendors"). I know this is because there's nothing in the range when I'm trying to set it, because if I put one entry into the named range, the set works, however, I need it populated by the sqlite query on each open as the data changes.
Any suggestions much appreciated.
Try this. You have a dynamic range that doesn't evaluate after you clear the contents. To avoid this, there are probably several ways, but easy to simply hardcode the startRange variable so that it always points to Data!$W$2 address, which is (or rather, will become) the first cell in your lstVendors range.
Private Sub Workbook_Open()
Dim rslt()
Dim i As Integer
Dim n As Integer
Dim startRng As Range
Dim DropDown1 As DropDown
Dim rngList As Range
'// Define your startRange -- always will be the first cell in your named range "lstVendors"
' hardcode the address because the dynamic range may not evalaute.
Set startRange = Sheets("Data").Range("W2")
'// Empty th lstVendors range if it exists/filled
On Error Resume Next
Range("lstVendors").Clear
On Error GoTo 0
'// Run your SQL query
rslt = Application.Run("SQLite_Query", "path/to/my/sqlite", "SELECT PROGRAM_ID FROM VENDOR;")
i = 0
'// Print results to the Worksheet, beginning in the startRange cell
For n = 2 To UBound(rslt)
'Increment from the startRange cell
startRange.Offset(i, 0).Value = rslt(n)(0)
i = i + 1
'Verify that "lstVendors" is being populated
Debug.Print Range("lstVendors").Address
Next n
End Sub
Thanks for the suggestions. Here is what I ended up doing in order to get around my problem. It involves adding something I didn't specify would be ok in my original question, so David's answer is great if what I did isn't an option. I first populated the first two cells in my named range with "Please Select..." and "All". In Sub Workbook_Open() we do this:
Private Sub Workbook_Open()
Application.WindowState = xlMaximized
Dim rslt()
Dim i As Integer
Dim n As Integer
Dim startRng As Range
Dim DropDown1 As DropDown
' Disable our not found message
ThisWorkbook.Sheets("Dashboard").Shapes("TextBox 6").Visible = False
' Set our start range to our named range
Set startRng = Range("lstVendors")
' Grab all vendor names
rslt = Application.Run("SQLite_Query", "path/to/my/sqlite", "SELECT PROGRAM_ID FROM VENDOR;")
' Print result. Skip first two rows as constants "Please Select..." and "All" are populated there
i = 2
For n = 2 To UBound(rslt)
startRng.Offset(i, 0).Value = rslt(n)(0)
i = i + 1
Next n
End Sub
Then we will create Sub Workbook_BeforeClose:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
' Disable the save changes dialog. This workbook will be locked up for display only. No need to confuse the user.
Application.DisplayAlerts = False
' Clear everything below the "Please Select..." and "All" cells in the named range
On Error Resume Next
Range("lstVendors").Offset(2, 0).ClearContents
On Error GoTo 0
' Save the changes to the named range
ThisWorkbook.Save
Application.DisplayAlerts = True
End Sub
This information is going to populate a drop down, so having Please Select and All hardcoded into the named range is acceptable for me. If that stipulation doesn't work for someone else looking at this in the future, please use David's suggestion! Thanks again!