VBA loop in change event error - vba

I have this macro that creates a textbox in worksheet 2 when I write something in cell A1 of worksheet 1, and when I delete that value it deletes the textbox.
I want to do that for several cells, but it just is working. If Cell A1 has a value a textbox with that value should appear, if the A2 has a value a textbox with that value should appear, but if I delete A1 it should delete the texbox that refers to A1, not all of the textboxes
Sub RemoveShapes()
Dim shp As Shape
For Each shp In Worksheets(2).Shapes
If shp.Type = msoTextBox Then shp.Delete
Next shp
End Sub
Sub criarcaixastexto()
Dim wsActive As Worksheet
Dim box As Shape
Set wsActive = Worksheets(2)
Set box = wsActive.Shapes.AddTextbox(msoTextOrientationHorizontal, 20, 20, 100, 50)
box.TextFrame.Characters.Text = Range("Folha1!A1").value
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$A$1" Then
Call criarcaixastexto
End If
End Sub
I tried this but it doesn't work
'macro para apagar
Sub removercaixas()
Dim shp As Shape
For Each shp In Worksheets(2).Shapes
If shp.Type = msoTextBox Then shp.Delete
Next shp
End Sub
'macro para criar caixas de texto
Sub criarcaixastexto()
Dim wsActive As Worksheet
Dim box As Shape
Set wsActive = Worksheets(2)
Set box = wsActive.Shapes.AddTextbox(msoTextOrientationHorizontal, 20, 20, 100, 50)
box.TextFrame.Characters.Text = Worksheets(1).Cells(i, 1).Value
End Sub
' macro corre ao escrever texto numa célula
Private Sub Worksheet_Change(ByVal Target As Range)
For i = 1 To 3
If Target.Count > 1 Then Exit Sub
If Not Intersect(Target, Range("A&i")) Is Nothing Then
removercaixas
If Len(Target) > 1 Then criarcaixastexto
End If
Next
End Sub

You are removing all textboxes on the sheet any time you call removercaixas. You need to somehow link the textbox with the cell it was generated by.
Why not name the textbox with the cell address? Copy/Paste this:
Sub removercaixas(strName As String)
Dim shp As Shape
For Each shp In Worksheets(2).Shapes
If shp.Type = msoTextBox AND shp.Name = strName Then shp.Delete
Next shp
End Sub
And
Sub criarcaixastexto(strName As String)
Dim wsActive As Worksheet
Dim box As Shape
Set wsActive = Worksheets(2)
Set box = wsActive.Shapes.AddTextbox(msoTextOrientationHorizontal, 20, 20, 100, 50)
box.TextFrame.Characters.Text = Worksheets(1).Range(strName).Value
box.Name = strName
End Sub
And
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Count > 1 Then Exit Sub
Select Case Target.Address
Case "$A$1", "$A$2", "$A$3"
removercaixas (Target.Address)
Case Else
Exit Sub
End Select
If Len(Target) > 1 Then criarcaixastexto (Target.Address)
End Sub
Textboxes are created in worksheet 2 all on top of each other. They are deleted appropriately. No textbox is created when the value entered in $A$1:$A$3 has a length of 1 or less. Not sure what the logic is there, but if you want single digit values to create a textbox just change the Len(Target) > 1 to Len(Target) > 0.

Related

refresh a textbox VBA

I want a macro that creates a textbox in Worksheet2 when I write something in Worksheet1!A1. The problem is that I want it to refresh whenever I refresh the data.
I made one but is runs the macro again, so I am left with several textbox, one on top of the others. Also I want to delete the textbox if the cell is empty.
I would appreciate any help. Thanks. Here is my code:
Sub criarcaixastexto()
Dim wsActive As Worksheet
Dim box As Shape
Set wsActive = Worksheets(2)
Set box = wsActive.Shapes.AddTextbox(msoTextOrientationHorizontal, 20, 20, 100, 50)
box.TextFrame.Characters.Text = Range("Folha1!A1").value
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$A$1" Then
Call criarcaixastexto
End If
End Sub
To ignore empty values change the event to this one:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Count > 1 Then Exit Sub 'to avoid multiple selection.
If Target.Address = "$A$1" Then
RemoveShapes
If Len(Target) > 1 then Criarcaixastexto
End If
End Sub
This will remove the shapes, before writing new ones.
Sub RemoveShapes()
Dim shp As Shape
For Each shp In Worksheets(2).Shapes
If shp.Type = msoTextBox Then shp.Delete
Next shp
End Sub

How to remain a code active after all macros have been executed

I have written multiple macros that will be executed with a command button however I want my last macro to remain active after all before macros have been executed. I want Macro15 to remain active after. And for for Macro15 I want if any cell changes I want to highlight that cell with colorindex 3
Sub RunallMacros()
macro1
macro2
macro3
macro5
Macro12
Macro13
Macro14
Macro15
End Sub
Sub macro1()
ThisWorkbook.Sheets("Main").Activate
End Sub
Sub macro2()
Dim myvalue As Variant
myvalue = InputBox("Enter Safety Stock Days")
Range("R5").value = myvalue
End Sub
Sub macro5()
Dim answer As Integer
answer = MsgBox("Are There Any ICF Forms?", vbYesNo + vbQuestion, "Other Sales")
If answer = vbYes Then ICFUserForm.Show
End Sub
Sub macro3()
Dim MyAnswer1 As Variant
Dim MyAnswer2 As Variant
Dim MyAnswer3 As Variant
Dim MyAnswer4 As Variant
Dim MyAnswer5 As Variant
MyAnswer1 = InputBox("Enter Growth Current Month")
Range("m3").value = MyAnswer1
MyAnswer2 = InputBox("Enter Growth Current Month+1")
Range("n3").value = MyAnswer2
MyAnswer3 = InputBox("Enter Growth Current Month+2")
Range("o3").value = MyAnswer3
MyAnswer4 = InputBox("Enter Growth Current Month+3")
Range("p3").value = MyAnswer4
MyAnswer5 = InputBox("Enter Growth Current Month+4")
Range("q3").value = MyAnswer5
End Sub
Sub Macro12()
ActiveCell.FormulaR1C1 = "='raw data'!R[-5]C"
Range("A7").Select
Selection.AutoFill Destination:=Range("A7:A500"), Type:=xlFillDefault
End Sub
Sub Macro13()
Range("C7").Select
Selection.ClearContents
End Sub
Sub Macro14()
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> "Raw" And ws.Name <> "Main" And ws.Name <> "Calendar" Then
For Each c In ws.Range("A50:A300")
ws.Rows(c.Row).Hidden = c.value = 0
Next
End If
Next
End Sub
Sub Macro15()
If Not Intersect(Target, Range("A7:AH500")) Is Nothing Or _
Not Intersect(Target, Range("A7:AH500")) Is Nothing Then
Target.Interior.ColorIndex = 3
End If
End Sub
A macro that is "active" is doing something, i.e. it is executing code. While it is executing code, the user can't do anything. So either the macro is active or the user is active.
What you want is to respond to an event, in this case the Worksheet.Change event:
Private Sub Worksheet_Change(ByVal Target as Range)
Target.Interior.ColorIndex = 3
End Sub
See https://msdn.microsoft.com/en-us/library/office/ff839775.aspx
Great answer from Paul - try this to get it working;
Private Sub Worksheet_Change(ByVal Sh As Object, ByVal Target as Range)
Target.Interior.ColorIndex = 3
End Sub

Clear cascading ComboBox on reselection

I am in the process of creating a user form in excel that uses several ComboBox'. The first ComboBox lists values from column 1 of a table and the following ComboBox' lists values from the following columns. ComboBox 2 onwards also only lists values depending on the preceding box. All ComboBox' show unique values only.
Here is the current code I am using:
Option Explicit
Private Sub ComboBox1_Change()
Call cValues(ComboBox1.Value, ComboBox2, 2)
End Sub
Private Sub ComboBox2_Change()
Call cValues(ComboBox2.Value, ComboBox3, 3)
End Sub
Private Sub ComboBox3_Change()
Call cValues(ComboBox3.Value, ComboBox4, 4)
End Sub
Private Sub ComboBox4_Change()
Call cValues(ComboBox4.Value, ComboBox5, 5)
End Sub
Private Sub ComboBox5_Change()
Call cValues(ComboBox5.Value, ComboBox6, 6)
End Sub
Private Sub UserForm_Initialize()
Dim Rng As Range
Dim Dn As Range
Dim Dic As Object
With Sheets("Listuni")
Set Rng = .Range(.Range("A2"), .Range("A" & Rows.Count).End(xlUp))
End With
Set Dic = CreateObject("scripting.dictionary")
Dic.CompareMode = vbTextCompare
For Each Dn In Rng: Dic(Dn.Value) = Empty: Next
Me.ComboBox1.List = Application.Transpose(Dic.keys)
End Sub
Sub cValues(txt As String, Obj As Object, col As Integer)
Dim Dn As Range
Dim Rng As Range
Dim Dic As Object
With Sheets("Listuni")
Set Rng = .Range(.Cells(2, col), .Cells(Rows.Count, col).End(xlUp))
End With
Set Dic = CreateObject("Scripting.Dictionary")
Dic.CompareMode = 1
For Each Dn In Rng
If Dn.Offset(, -1).Value = txt Then
If Not Dic.exists(Dn.Value) Then
Dic(Dn.Value) = Empty
End If
End If
Next Dn
Obj.List = Application.Transpose(Dic.keys)
End Sub
The problem I am having occurs when a user makes a reselection of a preceding ComboBox. Instead of clearing the subsequent boxes, all existing selections remain.
I am looking for a way to clear/default the values of subsequent ComboBox every time a reselection of a preceding ComboBox is made. For example if I make a selection in ComboBox 1 and 2 but then change my selection at ComboBox 1, I want ComboBox 2 to clear rather than show my previous selection. Note that the default position for the user form on launch shows no values in any ComboBox.
I have tried using the .clear method on change however this always gets hung up at:
Obj.List = Application.Transpose(Dic.keys)
I suspect this is because a clear is technically a change and therefore it cannot transpose the list of values to other boxes based on a null value.
This clears all subsequent ComboBoxes - if Combo1 changes, Combo2, 3, 4, 5, and 6 are cleared
Option Explicit
Private ws As Worksheet
Private d As Object
Private Sub UserForm_Initialize()
Dim cel As Range, txt As String, rng As Range
Set ws = Worksheets("Listuni")
Set d = CreateObject("Scripting.Dictionary"): d.CompareMode = vbTextCompare
Set rng = ws.Range(ws.Cells(2, 1), ws.Cells(ws.Rows.Count, 1).End(xlUp))
For Each cel In rng: d(cel.Value) = Empty: Next
ComboBox1.List = Application.Transpose(d.keys)
End Sub
Private Function setList(ByVal txt As String, ByRef cmb As ComboBox) As Object
Dim xID As Long, rng As Range, cel As Range, x As Control
xID = Right(cmb.Name, 1)
For Each x In Me.Controls
If TypeName(x) = "ComboBox" Then If Val(Right(x.Name, 1)) > xID - 1 Then x.Clear
Next
Set rng = ws.Range(ws.Cells(2, xID), ws.Cells(ws.Rows.Count, xID).End(xlUp))
d.RemoveAll
For Each cel In rng
If cel.Offset(, -1) = txt Then
If Not d.exists(cel.Value) Then
d(cel.Value) = Empty
End If
End If
Next
If d.Count > 0 Then cmb.List = Application.Transpose(d.keys) Else cmb.Clear
End Function
Private Sub ComboBox1_Change()
setList ComboBox1.Value, ComboBox2
End Sub
Private Sub ComboBox2_Change()
setList ComboBox2.Value, ComboBox3
End Sub
Private Sub ComboBox3_Change()
setList ComboBox3.Value, ComboBox4
End Sub
Private Sub ComboBox4_Change()
setList ComboBox4.Value, ComboBox5
End Sub
Private Sub ComboBox5_Change()
setList ComboBox5.Value, ComboBox6
End Sub

Remove extra spaces from cells in column

I wrote the following code to do inventory scanning bar-codes but for some reason when I scan the bar-code it is adding extra spaces in the cells and the result are not showing up as expected.
How do I remove the extra spaces from the cells in column?
Option Explicit
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Target.Cells.Count > 1 Or IsEmpty(Target) Or Target.Column <> 1 Then Exit Sub
If Not SheetExists("WarehouseInventory") Then Exit Sub
Dim result As Variant
Set result = Sheets("WarehouseInventory").Cells.Range("E:E").Find(Target)
If result Is Nothing Then
Target.Worksheet.Cells(Target.Row, 2) = "Data Maybe Bin #?"
Else
Target.Worksheet.Cells(Target.Row, 2) = result.Worksheet.Cells(result.Row, 4)
Target.Worksheet.Cells(Target.Row, 3) = result.Worksheet.Cells(result.Row, 5)
Target.Worksheet.Cells(Target.Row, 4) = result.Worksheet.Cells(result.Row, 6)
Target.Worksheet.Cells(Target.Row, 5) = result.Worksheet.Cells(result.Row, 7)
End If
End Sub
Public Function SheetExists(SheetName As String) As Boolean
Dim ws As Worksheet
SheetExists = False
For Each ws In ThisWorkbook.Worksheets
If ws.Name = SheetName Then SheetExists = True
Next ws
End Function
Barcode will be scan on column A
when I scan the barcode it is add extra spaces in the cells and the result are not showing up as expected.
The idea is not to trim all the cells later but trim the bar code entry at the time of scanning. Is this what you want? Put this in the code area of the relevant sheet. I am assuming that the bar code will be scanned in Col B to E.
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Whoa
If Target.Cells.Count > 1 Then Exit Sub
Application.EnableEvents = False
'~~> Assuming that the bar code is scanned in B to E
'~~> If it is Just one column like B then change
'~~> The code below to
'~~> If Not Intersect(Target, Columns("B:B")) Is Nothing Then
If Not Intersect(Target, Columns("B:E")) Is Nothing Then
Target.Value = Trim(Target.Value)
End If
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
This is a code for trimming cells of extra space.
Dim cell As Range
For Each cell In ActiveSheet.UsedRange.SpecialCells(xlCellTypeConstants)
cell = WorksheetFunction.Trim(cell)
Next cell
The above code will Trim all the cells in the ActiveSheet.
Select appropriate cells which you want to trim, and apply the Trim(cell) on them.

Using textboxes within userform to define variables?

I currently run a macro to compare the most recent sheet of data to the report immediately prior and highlight changes. It works fine on its own. Now, however, we would like to be able to compare selected sheets from any time period. My idea was to pop up a simple userform with two textboxes that the user can use to specify which two reports he wants to compare. I am quite lost though with the idea of trying to declare public variables; what I've got atm is:
Option Explicit
Public shtNew As String, shtOld As String, _
TextBox1 As TextBox, TextBox2 As TextBox
Sub SComparison()
Const ID_COL As Integer = 31 'ID is in this column
Const NUM_COLS As Integer = 31 'how many columns are being compared?
Dim rwNew As Range, rwOld As Range, f As Range
Dim X As Integer, Id
shtNew = CSManager.TextBox1
shtOld = CSManager.TextBox2
'Row location of the first employee on "CurrentMaster" sheet
Set rwNew = shtNew.Rows(5)
Do While rwNew.Cells(ID_COL).Value <> ""
Id = rwNew.Cells(ID_COL).Value
Set f = shtOld.UsedRange.Columns(ID_COL).Find(Id, , xlValues, xlWhole)
If Not f Is Nothing Then
Set rwOld = f.EntireRow
For X = 1 To NUM_COLS
If rwNew.Cells(X).Value <> rwOld.Cells(X).Value Then
rwNew.Cells(X).Interior.Color = vbYellow
rwNew.Cells(33) = "UPDATE"
Else
rwNew.Cells(X).Interior.ColorIndex = xlNone
End If
Next X
End If
Set rwNew = rwNew.Offset(1, 0) 'next row to compare
Loop
Call SUpdates
End Sub
My Suggestion would be to use Comboboxes instead of TextBoxes. Create a userform with two command buttons and two comboboxes and populate the comboboxes in the UserForm_Initialize() event using this code.
Private Sub UserForm_Initialize()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Sheets
ComboBox1.AddItem ws.Name: ComboBox2.AddItem ws.Name
Next
End Sub
And then use this code in the OK button to do the comparison.
Private Sub CommandButton1_Click()
Dim shtNew As Worksheet, shtOld As Worksheet
If ComboBox1.ListIndex = -1 Then
MsgBox "Please select the first sheet"
Exit Sub
End If
If ComboBox2.ListIndex = -1 Then
MsgBox "Please select the Second sheet"
Exit Sub
End If
Set shtNew = Sheets(ComboBox1.Value)
Set shtOld = Sheets(ComboBox2.Value)
'~~> REST OF THE CODE HERE NOW TO WORK WITH THE ABOVE SHEETS
End Sub
Private Sub CommandButton2_Click()
Unload Me
End Sub
HTH
Sid
For an easy fix, couldn't you just colour (sorry, I'm English!) the worksheets that you want to refer to, then do something like:
Sub ListSheets()
'lists only non-coloured sheets in immediate window
'(could amend to add to combo boxes)
Dim w As Worksheet
'loop over worksheets in active workbook
For Each w In Worksheets
If w.Tab.Color Then
'if tab color is set, print
Debug.Print w.Name
End If
Next w
Let me know if this solves your problem.