VBA-shape with realtive positioning - vba

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

Related

Autocomplete code for a worksheet in Excel not working in other worksheets using VB

My goal was to make autocomplete active for dropdowns and I have achieved it for a single worksheet but duplicating the code to other worksheets is not working.
I started by creating a combo Box on the initial worksheet containing the drop downs and then made the following changes-
Changed the name to TempCombo in the Name field
Selected 1-fmMatchEntryComplete in the MatchEntry field;
I then inserted the following code for that worksheet:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'Update by Extendoffice: 2017/8/15
Dim xCombox As OLEObject
Dim xStr As String
Dim xWs As Worksheet
Set xWs = Application.ActiveSheet
On Error Resume Next
Set xCombox = xWs.OLEObjects("TempCombo")
With xCombox
.ListFillRange = ""
.LinkedCell = ""
.Visible = False
End With
If Target.Validation.Type = 3 Then
Target.Validation.InCellDropdown = False
Cancel = True
xStr = Target.Validation.Formula1
xStr = Right(xStr, Len(xStr) - 1)
If xStr = "" Then Exit Sub
With xCombox
.Visible = True
.Left = Target.Left
.Top = Target.Top
.Width = Target.Width + 5
.Height = Target.Height + 5
.ListFillRange = xStr
.LinkedCell = Target.Address
End With
xCombox.Activate
Me.TempCombo.DropDown
End If
End Sub
Private Sub TempCombo_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal
`Shift As Integer)`
Select Case KeyCode
Case 9
Application.ActiveCell.Offset(0, 1).Activate
Case 13
Application.ActiveCell.Offset(1, 0).Activate
End Select
End Sub
This implementation works perfectly for that sheet but when i attempt to use the same code on another worksheet on the same file the autocomplete function doesn't work.
I attempted modification of the combobox name on sheet2 to TempCombo2 and changed the following line:
Set xCombox = xWs.OLEObjects("TempCombo")
to
Set xCombox = xWs.OLEObjects("TempCombo2")
The autocomplete function fails to work on sheet 2 even though no error is thrown.
This is a pretty interesting idea, I like it.
I was able to get this to work on multiple sheets with the following modifications:
Removed Cancel = True, this line was throwing an error and Cancel is not an argument in Worksheet_SelectionChange; I don't think this is doing anything.
Copied the code to the second sheet's code module (it has to be in each sheet module that you want it to run on)
updated Set xCombox = xWs.OLEObjects("TempCombo") to Set xCombox = xWs.OLEObjects("TempCombo2")
Me.TempCombo.DropDown updated to Me.TempCombo2.DropDown since that is what I named the combo box on the second sheet
Also, not a change as much as an assumption, it seems it only works with the ActiveX controls, so I assume that's what you are using when you add the new box.
As a follow up I was able to get it to work using the workbook module as long as the combo box is named "TempCombo" on all sheets (you have to add a combobox named "TempCombo" to each sheet). Going this route, you only need the code once, on the workbook module , and it uses the combo box that is local to each sheet.
TO TEST - In a new workbook: add list validation to a range using a range reference, put some values in the list range, add an ActiveX combobox to the sheet and name it "TempCombo", put the following code in the workbook module, then click anywhere in the range that has the list validation enabled.
One other note, make sure you aren't still in design mode on the developer tab!
Option Explicit
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
'Update by Extendoffice: 2017/8/15
Dim xCombox As OLEObject
Dim xStr As String
Dim xWs As Worksheet
Set xWs = Sh
On Error Resume Next
Set xCombox = xWs.OLEObjects("TempCombo")
With xCombox
.ListFillRange = ""
.LinkedCell = ""
.Visible = False
End With
If Target.Validation.Type = 3 Then
Target.Validation.InCellDropdown = False
'Cancel = True
xStr = Target.Validation.Formula1
xStr = Right(xStr, Len(xStr) - 1)
If xStr = "" Then Exit Sub
With xCombox
.Visible = True
.Left = Target.Left
.Top = Target.Top
.Width = Target.Width + 5
.Height = Target.Height + 5
.ListFillRange = xStr
.LinkedCell = Target.Address
End With
xCombox.Activate
Sh.TempCombo.DropDown
End If
End Sub
Private Sub TempCombo_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
Select Case KeyCode
Case 9
Application.ActiveCell.Offset(0, 1).Activate
Case 13
Application.ActiveCell.Offset(1, 0).Activate
End Select
End Sub

VBA-delete shapes

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

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

Excel: Selecting single cell vs. whole column in VBA

This is a newb question:
I have two sheets. Sheet 1 is where there is a form to enter data. When you double click on any cell in column A, a user form pop up comes up. You enter a few keys from any entry that is in the A column of sheet 2 and it autocompletes.
The problem I am having is: I only want to enter data on a specific cell, for instance A1 .. not the whole column of A. A second thing I wanted was that instead of a double click, I wanted it to work with a single click. Can anyone please help.
Here is the VBA code for Sheet 1 where you enter the data
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim uiChosen As String
Dim MyList As Range
Dim myPrompt As String
If Target.Column <> 1 Then Exit Sub
Set MyList = Sheet2.Range("Cariler")
myPrompt = "Lütfen Bir Cari Seçin"
uiChosen = UserForm1.ChooseFromList(MyList, myPrompt, Default:=Target.Value, xlFilterStyle:=xlContains)
If StrPtr(uiChosen) <> 0 Then
Target.Value = uiChosen
Cancel = True
End If
End Sub
Here is the code for the user form:
Option Explicit
' in userform's code module
Dim FullList As Variant
Dim FilterStyle As XlContainsOperator
Dim DisableMyEvents As Boolean
Dim AbortOne As Boolean
Const xlNoFilter As Long = xlNone
Private Sub butCancel_Click()
Unload Me
End Sub
Private Sub butOK_Click()
Me.Tag = "OK"
Me.Hide
End Sub
Private Sub ComboBox1_Change()
Dim oneItem As Variant
Dim FilteredItems() As String
Dim NotFlag As Boolean
Dim Pointer As Long, i As Long
If DisableMyEvents Then Exit Sub
If AbortOne Then AbortOne = False: Exit Sub
If TypeName(FullList) Like "*()" Then
ReDim FilteredItems(1 To UBound(FullList))
DisableMyEvents = True
Pointer = 0
With Me.ComboBox1
Select Case FilterStyle
Case xlBeginsWith: .Tag = LCase(.Text) & "*"
Case xlContains: .Tag = "*" & LCase(.Text) & "*"
Case xlDoesNotContain: .Tag = "*" & LCase(.Text) & "*": NotFlag = True
Case xlEndsWith: .Tag = "*" & LCase(.Text)
Case xlNoFilter: .Tag = "*"
End Select
For Each oneItem In FullList
If (LCase(oneItem) Like .Tag) Xor NotFlag Then
Pointer = Pointer + 1
FilteredItems(Pointer) = oneItem
End If
Next oneItem
.List = FilteredItems
.DropDown
DisableMyEvents = False
If Pointer = 1 Then .ListIndex = 0
End With
End If
End Sub
Private Sub ComboBox1_Click()
butOK.SetFocus
End Sub
Private Sub ComboBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
Select Case KeyCode
Case vbKeyReturn: Call butOK_Click
Case vbKeyUp, vbKeyDown: AbortOne = True
End Select
End Sub
Private Sub Label1_Click()
End Sub
Private Sub UserForm_Activate()
ComboBox1.SetFocus
If ComboBox1.Text <> vbNullString Then
Call ComboBox1_Change
End If
End Sub
Private Sub UserForm_Initialize()
ComboBox1.MatchEntry = fmMatchEntryNone
End Sub
Public Function ChooseFromList(ListSource As Variant, Optional Prompt As String = "Choose one item", _
Optional Title As String = "Cari Arama Programı", Optional Default As String, _
Optional xlFilterStyle As XlContainsOperator = xlBeginsWith) As String
Dim Pointer As Long, oneItem As Variant
If TypeName(ListSource) = "Range" Then
With ListSource
Set ListSource = Application.Intersect(.Cells, .Parent.UsedRange)
End With
If ListSource Is Nothing Then Exit Function
If ListSource.Cells.Count = 1 Then
ReDim FullList(1 To 1): FullList(1) = ListSource.Value
ElseIf ListSource.Rows.Count = 1 Then
FullList = Application.Transpose(Application.Transpose(ListSource))
Else
FullList = Application.Transpose(ListSource)
End If
ElseIf TypeName(ListSource) Like "*()" Then
ReDim FullList(1 To 1)
For Each oneItem In ListSource
Pointer = Pointer + 1
If UBound(FullList) < Pointer Then ReDim Preserve FullList(1 To 2 * Pointer)
FullList(Pointer) = oneItem
Next oneItem
ReDim Preserve FullList(1 To Pointer)
ElseIf Not IsObject(ListSource) Then
ReDim FullList(1 To 1)
FullList(1) = CStr(ListSource)
Else
Err.Raise 1004
End If
Me.Caption = Title
Label1.Caption = Prompt
FilterStyle = xlFilterStyle
DisableMyEvents = True
ComboBox1.Text = Default
ComboBox1.List = FullList
DisableMyEvents = False
butOK.SetFocus
Me.Show
With UserForm1
If .Tag = "OK" Then ChooseFromList = .ComboBox1.Text
End With
End Function
There is no single click event. Use Intersect to test wherther or not the target cell is within a given range.
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Intersect(Target, Range("A1")) Is Nothing Then
Dim uiChosen As String
Dim MyList As Range
Dim myPrompt As String
If Target.Column <> 1 Then Exit Sub
Set MyList = Sheet2.Range("Cariler")
myPrompt = "Lütfen Bir Cari Seçin"
uiChosen = UserForm1.ChooseFromList(MyList, myPrompt, Default:=Target.Value, xlFilterStyle:=xlContains)
If StrPtr(uiChosen) <> 0 Then
Target.Value = uiChosen
Cancel = True
End If
End If
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.