VBA-delete shapes - vba

I have this code that creates shapes in page 2 when I write something in A1:A3 and places the textbox according to what I write in B1:B3, the problem is that when I delete the value of A1 I want the textbox to be deleted, but it doesn't delete the textbox. I also tried : Call getCaixas(Worksheets(2), Target.Address).Delete after dim box as shape. In this option it did erase the textbox but then all the textboxes were created on the top of the page.
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim box As Shape
If Target.Address = "Delete" Then getCaixas(Worksheets(2), Target.Address).Delete
If Target.Count > 1 Or Not Sh.Index = 1 Or Len(Target) = 0 Then Exit Sub
If Not Intersect(Target, Range("B1:B3")) Is Nothing Then
Set box = getCaixas(Worksheets(2), Target.Offset(0, -1).Address)
Select Case Target.Value
Case Is = "financeiro"
box.Top = 20
Case Is = "cliente"
box.Top = 150
Case Is = "processos internos"
box.Top = 250
End Select
End If
If Not Intersect(Target, Range("A1:A3")) Is Nothing Then
Set box = getCaixas(Worksheets(2), Target.Address)
Select Case Target.Address
Case Is = "$A$1"
box.Left = 50
Case Is = "$A$2"
box.Left = 200
Case Is = "$A$3"
box.Left = 350
End Select
box.TextFrame.Characters.Text = Target.Value
End If
End Sub
Function getCaixas(ws As Worksheet, CaixasName As String) As Shape
Dim box As Shape
On Error Resume Next
Set box = ws.Shapes(CaixasName)
If Err.Number <> 0 Then
Set box = ws.Shapes.AddTextbox(msoTextOrientationHorizontal, 0, 0, 100, 50)
box.Name = CaixasName
End If
On Error GoTo 0
Set getCaixas = box
End Function

When you have to delete shapes in a given area, the easiest way to do it, is to loop over the shapes and to see the outliers.
The shapes in a given sheet are a collection. Thus, looping through them is easy.
Each shape has two important properties - TopLeftCell and BottomRightCell. These properties are of type range - thus they have row and column property.
Long story short - if you have a case like this:
and you want to delete every shape outside the range("A1:C3") (in yellow) then you can loop through every shape and check its TopLeftCell.Row and BottomRightCell.Column for being more than 3. If both are true, then delete it. Like this:
Sub KillShapes()
Dim sh As Shape
For Each sh In ActiveSheet.Shapes
Debug.Print sh.Name
Debug.Print sh.TopLeftCell.Address
Debug.Print sh.BottomRightCell.Address
If sh.TopLeftCell.Row > 3 And sh.BottomRightCell.Column > 3 Then
Debug.Print sh.Name; " is deleted!"
sh.Delete
End If
Next
End Sub

This looks wrong:
If Target.Address = "Delete" Then
The Address property of a Range object will return a range address like "$A$1". If are looking for a cell value of "Delete" then it should be
If Target.Value= "Delete" Then
If you are looking for the Name of a named range, then
If Target.Name.Name = "Delete" Then

Related

VBA-shape with realtive positioning

I have this code but it's not working as I want it.
This is what I want:
-if I write something in cell A1,A2 or A3 (in worksheet1) a textbox is created in worksheet(2). This works but now I want the place of the textbox to change when I right in cell B1,B2,B3.
I tried to do that with the code below, but I think there might be a problem with the way I defined the Range("B" & CStr(i)) because when I use just B1 it works.
I need to change the code two do two things differently:
1- If I write in B1 "cliente" I want the texbox with the text from A1 to be created in toppos=150 and if I change it to "financeiro" I want the texbox to be created in toppos=20.
2- If B1 and B2 have "fianceiro" written I want the textboxes related to A1 and A2 to be next to each other.
Can someone help me?
Thank you
So this is what I want:
-Textboxes created with the content of cells A1 to A3 on worksheet 2;
-If I change the content the content of the textbox should be updated, if I erase the value then the textbox should be deleted;
-the position of the textboxes should change with the options I choose in column B. I want the worksheet(2) to have 4 "slices", the first is for the option "financeiro", so all the textboxes related to that slice of page should be in a specific place in the worksheet, for example, in position 20, if on the other hand that textbox is from the option "cliente", the textbox should be in the slice related to "cliente", position 150.
-also each option in column B might have more then one textbox so I want the textboxes from the same option to appear side by side.
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
Sub criarcaixastexto(strName As String)
Dim wsActive As Worksheet
Dim box As Shape
Set wsActive = Worksheets(2)
Dim leftpos As Long
Dim toppos As Long
Dim i As Long
For i = 1 To 3
If Worksheets(1).Range("B" & CStr(i)).Value = "financeiro" Then
toppos = 20
ElseIf Worksheets(1).Range("B" & CStr(i)).Value = "cliente" Then
toppos = 150
ElseIf Worksheets(1).Range("B" & CStr(i)).Value = "processos internos" Then
toppos = 250
Else:
toppos = 350
End If
Next i
Select Case strName
Case Is = "$A$1"
leftpos = 50
Case Is = "$A$2"
leftpos = 200
Case Is = "$A$3"
leftpos = 350
End Select
Set box = wsActive.Shapes.AddTextbox(msoTextOrientationHorizontal, leftpos, toppos, 100, 50)
box.TextFrame.Characters.Text = Worksheets(1).Range(strName).Value
box.Name = strName
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, 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)
If Len(Target) > 0 Then criarcaixastexto (Target.Address)
Case Else
Exit Sub
End Select
End Sub
I'm not sure of some of the OP's logic or exactly what he wants to accomplish. Instead of adding and removing textboxes, I would create a Function that would create the textbox, if needed, and return a reference to it.
Private Sub Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
If Target.Count > 1 Or Not Sh.Index = 1 Or Len(Target) = 0 Then Exit Sub
Dim box As Shape
If Not Intersect(Target, Range("B1:B3")) Is Nothing Then
Set box = getCaixas(Worksheets(2), Target.Offset(0, -1).Address)
Select Case Target.Value
Case Is = "financeiro"
box.Top = 20
Case Is = "cliente"
box.Top = 150
Case Is = "processos internos"
box.Top = 250
End Select
End If
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim box As Shape
If Target.Count > 1 Or Not Sh.Index = 1 Or Len(Target) = 0 Then Exit Sub
If Not Intersect(Target, Range("A1:A3")) Is Nothing Then
Set box = getCaixas(Worksheets(2), Target.Address)
Select Case Target.Address
Case Is = "$A$1"
box.Left = 50
Case Is = "$A$2"
box.Left = 200
Case Is = "$A$3"
box.Left = 350
End Select
box.TextFrame.Characters.Text = Target.Value
End If
End Sub
Function getCaixas(ws As Worksheet, CaixasName As String) As Shape
Dim box As Shape
On Error Resume Next
Set box = ws.Shapes(CaixasName)
If Err.Number <> 0 Then
Set box = ws.Shapes.AddTextbox(msoTextOrientationHorizontal, 0, 0, 100, 50)
box.Name = CaixasName
End If
On Error GoTo 0
Set getCaixas = box
End Function

Capture Cell Value on left mouse single click in Excel VBA

I need help in capturing the cell value on left mouse click. So when a user has done a single left mouse click in a particular cell then I need to capture that particular cell value (Value written in that cell) in the VBA code.
This value will then be passed on to the VBA code and the output will be different for click in different cells. I hope I was able to explain the purpose.
I have total of 10 cells where the left mouse click value is to be captured.
This code will check to see if multiple cells are selected at once then it checks to see if the cell is empty. If it is empty then it exits otherwise it stores the value is cell N1. You can change which cell the value gets stores at. If N1 has a value it goes to the next empty cell in column N.
Dim oval
Dim N As Long
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Cells.Count < 2 Then
If Target = Empty Then
Exit Sub
End If
oval = Target.Value
MsgBox "The value saved is " + oval + "."
If Range("N1").Value = "" Then
N = 1
Else
N = Cells(Rows.Count, "N").End(xlUp).Row + 1
End If
Cells(N, "N").Value = oval
End If
End Sub
If you would settle for a double mouse click, you could try something like
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
MsgBox Target.Value
Cancel = True
End Sub
How about this? Just wrote it, it will not register a mouse click if you click where you could have reached with the keyboard
Enter Under Worksheet Code:
XXXXXXX
Option Explicit
Private prevTarget As Range
Private Sub Worksheet_Activate()
Set prevTarget = Selection
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim wasMouseClick As Boolean
Dim ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8, ch9 As String
On Error Resume Next
ch1 = prevTarget.Offset(1, 0).Address
ch2 = prevTarget.Offset(-1, 0).Address
ch3 = prevTarget.Offset(0, 1).Address
ch4 = prevTarget.Offset(0, -1).Address
ch5 = prevTarget.End(xlDown).Address
ch6 = prevTarget.End(xlToLeft).Address
ch7 = prevTarget.End(xlToRight).Address
ch8 = prevTarget.End(xlUp).Address
On Error GoTo error_noPreTarget
If Not (Target.Address = ch1 Or _
Target.Address = ch2 Or _
Target.Address = ch3 Or _
Target.Address = ch4 Or _
Target.Address = ch5 Or _
Target.Address = ch6 Or _
Target.Address = ch7 Or _
Target.Address = ch8) Then
wasMouseClick = True
End If
Set prevTarget = ActiveCell
If wasMouseClick Then
Debug.Print wasMouseClick ' replace with what you want when wasMouseClick = True
End If
Exit Sub
error_noPreTarget:
Set prevTarget = Selection
End Sub

Displaying a cell value in a text box and moving around in excel using vba

I have an Excel sheet which contains some values in the cells A1 to A9. I have a text box and command button 1 (renamed to "previous") and command button 2 (renamed to "Next").
When I click the Next button the values in the cells from A1 to A9 should be displayed in the text box which I have in an order from A1 to A9 and when the Previous button is clicked it should behave in the reverse.
edited: added Solution "B" for Form controls
Solution A for ActiveX controls
1) double click "Previous" button and VBA gets you in the sheet code pane with
Private Sub CommandButton1_Click()'<~~ maybe your "Previous" button was not the 1st ActiveX button you inserted in the sheet so the sub title has a different number in it: don't bother and just keep it as you find it
End Sub
that you fill like follows:
Private Sub CommandButton1_Click() '<~~ remember: keep the number you already have there in the sub name
UpdateTextBox 1
End Sub
2) double click "Next" button and and VBA gets you in the sheet code pane with
Private Sub CommandButton2_Click() '<~~ maybe your "Next" button was not the 2d ActiveX button you inserted in the sheet so the sub title has a different number in it: don't bother and just keep it as you find it
End Sub
that you fill like follows:
Private Sub CommandButton2_Click()'<~~ remember: keep the number you already have there in the sub name
UpdateTextBox -1
End Sub
3) place this code in any module code pane
Option Explicit
Sub UpdateTextBox(shift As Long)
Dim found As Range, myRange As Range
Dim s As OLEObject
Dim index As Long
With ActiveSheet
Set s = .OLEObjects("TextBox1") '<~~ set the name of the ActiveX TextBox control
Set myRange = .Range("A1:A9") '<~~ set the range you want to scroll up and down
End With
index = 1 '<~~ default index position should textbox be empty or filled with non valid value
With myRange
If s.Object.Value <> "" Then '<~~ get current textbox value index in range
Set found = .Find(what:=s.Object.Value, LookIn:=xlValues, lookat:=xlWhole) '<~~ search for the current text current textbox value index in range
If Not found Is Nothing Then index = found.Row - .Rows(1).Row + 1
End If
index = index + shift '<~~ make the shift
Select Case index
Case Is > .Rows.Count
index = .Rows.Count '<~~ limit max index to range last row
Case Is < 1
index = 1 '<~~ limt min index to range first row
End Select
s.Object.Value = .Rows(index) '<~~ update textbox value
End With
End Sub
Solution B for Form controls
1) add this in any module code pane
Option Explicit
Sub SkipToNext()
UpdateTextBox2 1
End Sub
Sub SkipToPrevious()
UpdateTextBox2 -1
End Sub
Sub UpdateTextBox2(shift As Long)
Dim s As Shape
Dim found As Range, myRange As Range
Dim index As Long
With ActiveSheet
Set s = .Shapes("TextBox 1") '<~~ set the name of the Form TextBox control
Set myRange = .Range("A1:A9") '<~~ set the range you want to scroll up and down
End With
index = 1 '<~~ default index position should textbox be empty or filled with non valid value
With myRange
If s.TextFrame.Characters.Text <> "" Then '<~~ get current textbox value index in range
Set found = .Find(what:=s.TextFrame.Characters.Text, LookIn:=xlValues, lookat:=xlWhole) '<~~ search for the current text current textbox value index in range
If Not found Is Nothing Then index = found.Row - .Rows(1).Row + 1
End If
index = index + shift '<~~ make the shift
Select Case index
Case Is > .Rows.Count
index = .Rows.Count '<~~ limit max index to range last row
Case Is < 1
index = 1 '<~~ limt min index to range first row
End Select
s.TextFrame.Characters.Text = .Rows(index) '<~~ update textbox value
End With
End Sub
2) assign SkipToNext() to the "Next" button and SkipToPrevious() to the "Previous" button
Starting with a TextBox and two buttons from AutoShapes. Enter the following in a standard module:
Public WhereAmI As Long
Sub Nextt()
Dim s As Shape
Set s = ActiveSheet.Shapes("TextBox 1")
If CStr(WhereAmI) = "" Then
WhereAmI = 1
s.TextFrame.Characters.Text = Range("A1").Text
Else
If WhereAmI = 9 Then Exit Sub
WhereAmI = WhereAmI + 1
s.TextFrame.Characters.Text = Cells(WhereAmI, 1).Text
End If
End Sub
Sub Prevv()
Dim s As Shape
Set s = ActiveSheet.Shapes("TextBox 1")
If CStr(WhereAmI) = "" Then
WhereAmI = 2
s.TextFrame.Characters.Text = Range("A2").Text
Else
If WhereAmI = 1 Then Exit Sub
WhereAmI = WhereAmI - 1
s.TextFrame.Characters.Text = Cells(WhereAmI, 1).Text
End If
End Sub
Then assign Nextt() to the "Next" button and assign Prevv() to the "Previous" button:
From the above, if you click Next, gamma will be in the box. If you click Previous, alpha will be in the box.
EDIT#1:
I use the Public variable to keep track of which of the items is currently in the TextBox............that way the subs can get to the next or previous value.
All three Shapes (textbox and two buttons) are easily available from AutoShapes:
In my version of Excel, that menu is in the Insert tab. When you first start out, there will be nothing in the TextBox, that is the reason for the CStr() test.
EDIT#2:
To handle the case of having the initial value of WhereAmI being 0, use this version of Prevv():
Sub Prevv()
Dim s As Shape
Set s = ActiveSheet.Shapes("TextBox 1")
If CStr(WhereAmI) = "" Then
WhereAmI = 2
s.TextFrame.Characters.Text = Range("A2").Text
Else
If WhereAmI = 1 Then Exit Sub
If WhereAmI = 0 Then WhereAmI = 2
WhereAmI = WhereAmI - 1
s.TextFrame.Characters.Text = Cells(WhereAmI, 1).Text
End If
End Sub

Excel VBA, How to Loop a Msgbox when text in cell changes to "News" to answer of Msgbox in next column

I'm trying to create a MsgBox that automatically pops up with a prompt of "Yes or No" when a cell in a column changes from blank to "News", and to put the answer into the next column.
I will be continuing to add to rows over time so it has to automatically pop up when the cell changes from blank to "news" and input the answer into the newly added cell to the right.
I'm pretty sure I need the For each loop, but honestly I'm a little lost and get a mismatch error during debug at the If Intersect line.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myRange As Range
Set myRange = Range("G2:G1000")
If Intersect(myRange, Target) Then
If Range("G2").Value = "News" Then Answer = MsgBox("Good?", vbYesNo)
Answer = ActiveCell.Offset(0, 1) = 1 'not sure if this is right, or is it Range.Offset?
Dim cel As Range
For Each cel In Range("G2:G1000")
If cel.Value = "News" Then Answer = MsgBox("Good?", vbYesNo)
Answer = ActiveCell.Offset(0, 1) = 1 'not sure if this is right, or is it Range.Offset?
Exit For
Next
End If
End Sub
Here you go:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If Target.Column = 7 Then
If Target.Count = 1 Then
If LCase$(Target) = "news" Then
Application.EnableEvents = False
Target(, 2) = Array("Yes", "No")(MsgBox("Good?", vbYesNo) - 6)
End If
End If
End If
Application.EnableEvents = True
End Sub

Delete row button deleting other row instead

I've created a couple of macros, one that creates a shape in a determined row with a macro assigned and the macro assigned that deletes the row when the shape is clicked on. The macro that adds the shape is activated by another macro that populates the last empty row of my table with relevant data and the shape to delete the row in question, but I'll leave that one out of it.
So the macros should add the shape to the row being populated and, once the shape gets clicked, it gets the shape's row and delete it.
Here are the macros:
--The one that creates the shape:
Sub addDelBt(ByVal Target As Range)
Dim rw As Long
rw = Target.Row
Dim shp As Object
Set shp = Plan1.Shapes.AddShape(msoShapeMathMultiply, Target.Left + 2.5, Target.Top + 2.5, Target.RowHeight - 2, Target.RowHeight - 2)
'shp.Width = 11
'shp.Height = 11
shp.Fill.ForeColor.RGB = RGB(192, 0, 0)
shp.Fill.BackColor.RGB = RGB(170, 170, 170)
shp.Line.Visible = msoFalse
With shp.Shadow
.ForeColor.RGB = RGB(0, 0, 128)
.OffsetX = 0.5
.OffsetY = 2
.Transparency = 0.5
.Visible = True
End With
With shp.ThreeD
.BevelTopType = msoBevelCircle
.BevelTopInset = 15
.BevelTopDepth = 3
.PresetLighting = msoLightRigBalanced
.LightAngle = 145
.Visible = True
End With
shp.Name = "btnDel" & rw
shp.OnAction = "delRow"
End Sub
--The action of the shape:
Sub delRow()
Plan1.Unprotect ("password")
Dim shp As Object
Set shp = Plan1.Shapes(Application.Caller)
Dim rw As Long
rw = shp.TopLeftCell.Row
Dim doc As String
doc = Plan1.Cells(rw, 2).Value
Dim msgResult As VbMsgBoxResult
msgResult = MsgBox("VocĂȘ deseja deletar o documento " + doc + "?", vbYesNo)
If msgResult = vbYes Then
Plan1.Rows(rw).EntireRow.Delete
End If
Plan1.Protect ("password")
End Sub
The problem is that some times (I haven't found a pattern yet) the button from one row will delete another upper row. I can't find out why, can you see it?
Cannot see why this would happen. Everything looks alright with the code.
Once I also wanted to make very similar functionality and realized that using many dynamically created buttons is not the best option (at least not for me).
I have abandoned the idea of shapes and make similar functionality with Worksheet_SelectionChange event. With some nice formating you can make the cells in the column looks like some Delete Buttons. The Worksheet_SelectionChange event (for cells) works like OnClick event (for buttons / OnAction for shapes).
Example:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
On Error GoTo ErrHandler
Application.EnableEvents = False
If Target.Column = 5 Then 'If the clicked cell is in the column 5
Dim doc As String
doc = Cells(Target.row, 2).Value
Dim msgResult As VbMsgBoxResult
msgResult = MsgBox("Voce^ deseja deletar o documento " + doc + "?", vbYesNo)
If msgResult = vbYes Then
Plan1.Rows(Target.Row).EntireRow.Delete
End If
End If
ErrHandler:
Application.EnableEvents = True
End Sub
The ErrorHandler with Disabling events is important to prevent the row.delete event to trigger another Worksheet_SelectionChange event.