Shape Names In Excel - vba

I have a VBA sub to create a few shapes, these shapes are then renamed to a cell value (B5:B15) and add text (C5:C15).
The shapes gets created, renamed and the text gets added but when I try to connect them I get the "Object Required".
Can some one please help me.
Thanks in advance.
Sub Button1_Click()
Dim s, conn As Shape, i As Integer
Set w = ActiveSheet
For i = 5 To 7
Set s = w.Shapes.AddShape(1, 800, i * 120 - 599, 100, 100)
s.Name = Range("B" & i)
s.TextFrame.Characters.Text = Range("C" & i)
s.Fill.ForeColor.RGB = RGB(0, 0, 213)
s.TextFrame.Characters.Font.ColorIndex = 19
Next i
Set conn = w.Shapes.AddConnector(1, 1, 1, 1, 1)
conn.ConnectorFormat.BeginConnect A001, 1
conn.ConnectorFormat.EndConnect A002, 1
End Sub

Something that would work:
Option Explicit
Sub Button1_Click()
Dim s As Shape, conn As Shape, i As Long
Dim w As Worksheet
Set w = ActiveSheet
Dim arr As Variant
ReDim arr(5 To 7)
For i = 5 To 7
Set s = w.Shapes.AddShape(1, 800, i * 120 - 599, 100, 100)
s.Name = Range("B" & i)
s.TextFrame.Characters.Text = Range("C" & i)
s.Fill.ForeColor.RGB = RGB(0, 0, 213)
s.TextFrame.Characters.Font.ColorIndex = 19
Set arr(i) = s
Next i
Set conn = w.Shapes.AddConnector(1, 1, 1, 1, 1)
conn.ConnectorFormat.BeginConnect arr(5), 1
conn.ConnectorFormat.EndConnect arr(6), 1
End Sub
What is the difference?
declaration of all variables - s is a Shape, i is a Long, w is a Worksheet;
the declaration is forced by Option Explicit;
a new variable arr is introduced, which keeps all the newly created forms. Thus the first form is kept under arr(5) and the last form is arr(7);
the BeginConnect and EndConnect need a variable which is a form. This is where we use the arr(5) to arr(7);
You can also refer to the shape, by its name and the Shapes() collection. Thus, the last 3 lines should look like this:
Set conn = w.Shapes.AddConnector(1, 1, 1, 1, 1)
conn.ConnectorFormat.BeginConnect w.Shapes("A001"), 1
conn.ConnectorFormat.EndConnect w.Shapes("A002"), 1

Related

Find string in table VBA Powerpoint

After 2 days trying to find a solution to my problem, I need your help please.
I'm working on powerpoint VBA script, and I've got a Table (3,3). In the row 1, I've already input some string in cells.
I want to know why my script doesn't want to write NOK in cells when the string does'nt match "comp" for example
Here is my VBA script:
Public Sub CreateTable1()
' déclaration of variables
Dim objSld As Slide
Dim objShp As Shape
Dim foundText1 As Object
Dim FindWhat As String
Dim I As Integer
Dim j As Integer
Set objSld = ActivePresentation.Slides(1)
Set objShp = objSld.Shapes.AddTable(3, 3, 15, 150, 700, 500)
' Give a name to table
objShp.Name = "Table1"
' Define size of cells
With objSld.Shapes("Table1").Table
.Columns(1).Width = 115
.Columns(2).Width = 115
.Columns(3).Width = 115
.Rows(1).Height = 120
.Rows(2).Height = 120
.Rows(3).Height = 120
'Write in cells
With .Cell(1, 1).Shape.TextFrame
.TextRange.Text = "Composition"
End With
With .Cell(2, 1).Shape.TextFrame
.TextRange.Text = "Material"
End With
With .Cell(3, 1).Shape.TextFrame
.TextRange.Text = "Method"
End With
' Define text position
For I = 1 To 3
For j = 1 To 3
With .Cell(j, I).Shape.TextFrame
.VerticalAnchor = msoAnchorMiddle
.HorizontalAnchor = msoAnchorCenter
.TextRange.Font.Size = 18
End With
Next j
Next I
'Command find
'Browse row 1 from line 1 to 3
For x = 1 To 3
Set foundText1 = objSld.Shapes("Table1").Table.Cell(x, 1).Shape.TextFrame.TextRange.Find(FindWhat:="Comp")
If foundText1 = "Comp" Then
'MsgBox foundText1 & x
'Will write in cell (x,2) OK and x
objSld.Shapes("Table1").Table.Cell(x, 2).Shape.TextFrame.TextRange.Text = "OK " & x
Else
'Will write in cell (x,2) NOK and x
'Doesn't works !! Why??
objSld.Shapes("Table1").Table.Cell(x, 2).Shape.TextFrame.TextRange.Text = "NOK " & x
End If
Next x
End With
End Sub
I Would like to know if you see where is the mistake. The function Else seems not working..
I found the solution !!
For those who're lost with this same problem, here is my code:
Public Sub CreateTable1()
' déclaration of variables
Dim objSld As Slide
Dim objShp As Shape
Dim foundText1 As Object
Dim TextRng As TextRange
Dim FindWhat As String
Dim I As Integer
Dim j As Integer
Set objSld = ActivePresentation.Slides(8)
Set objShp = objSld.Shapes.AddTable(3, 3, 15, 150, 700, 500)
' Give a name to table
objShp.Name = "Table1"
' Define size of cells
With objSld.Shapes("Table1").Table
.Columns(1).Width = 115
.Columns(2).Width = 115
.Columns(3).Width = 115
.Rows(1).Height = 120
.Rows(2).Height = 120
.Rows(3).Height = 120
'Write in cells
With .Cell(1, 1).Shape.TextFrame
.TextRange.Text = "Composition"
End With
With .Cell(2, 1).Shape.TextFrame
.TextRange.Text = "Material"
End With
With .Cell(3, 1).Shape.TextFrame
.TextRange.Text = "Method"
End With
' Define text position
For I = 1 To 3
For j = 1 To 3
With .Cell(j, I).Shape.TextFrame
.VerticalAnchor = msoAnchorMiddle
.HorizontalAnchor = msoAnchorCenter
.TextRange.Font.Size = 18
End With
Next j
Next I
'Command find
'Browse row 1 from line 1 to 3
End With
End Sub
Creation of a second sub to understand where does script failed
Sub yolo()
Dim objSld As Slide
Dim oTbl As Table
Dim lRow As Long
Dim lCol As Long
Dim foundText1 As Object
Set objSld = ActivePresentation.Slides(8)
Set oTbl = objSld.Shapes("Table1").Table
With oTbl
For lRow = 1 To .Rows.Count
With .Cell(lRow, 1).Shape
'Do something with each cell's text
'Does this shape has text?
If .HasTextFrame Then
Set TextRng = oTbl.Cell(lRow, 1).Shape.TextFrame.TextRange
Set foundText1 = TextRng.Find(FindWhat:="Comp")
Do While Not (foundText1 Is Nothing)
With foundText1
oTbl.Cell(lRow, 2).Shape.TextFrame.TextRange.Text = "OK"
Set foundText1 = TextRng.Find(FindWhat:="Comp", After:=.Start + .Length - 1)
End With
Loop
End If
End With
Next lRow
End With
End Sub

Excel VBA code for Process Mapping

I have to create process maps in excel often and am trying to cut down on the excessive time it takes. instead of creating each shape and entering or pasting the text into the shape, with often 100 + steps. I am trying to write a code that will create a shape with the text from column D and will continue to do so until there is no text in the cell in column A. I have been able to create code to create the shapes but I cannot seem to have the text populate correctly in the separate blocks. Below is the code I have so far:
Sub addshapewithtext()
Dim BlankFound As Boolean
Dim x As Long
Dim w As Worksheet
Dim s As Shape
Dim t As Variant
Do While BlankFound = False
x = x + 1
Set t = Cells(x, "d")
Set w = ActiveSheet
Set s = w.Shapes.addshape(msoShapeFlowchartProcess, 200 + x * 200, 100, 100, 100)
'format shape
s.Fill.ForeColor.RGB = RGB(300, 300, 300)
s.Line.ForeColor.RGB = RGB(0, 0, 0)
s.Line.Weight = 3
'transparency
s.Fill.Transparency = 0
'add text from list
s.OLEFormat.Object.Formula = t
If Cells(x, "A").Value = "" Then
BlankFound = True
End If
Loop
End Sub
thank you for your help
Change s.OLEFormat.Object.Formula = t to s.OLEFormat.Object.Text = t
You may want to add in font size and color for the text in your shape as well
REVISED TO ADD
I went ahead and tweaked the procedure slightly and tested it:
Sub addshapewithtext()
Dim BlankFound As Boolean
Dim x As Long
Dim w As Worksheet
Dim s As Shape
Dim t As Variant
x = 1
Do Until Cells(x, "A").Value = ""
Set t = Cells(x, "d")
Set w = ActiveSheet
Set s = w.Shapes.AddShape(msoShapeFlowchartProcess, 200 + x * 200, 100, 100, 100)
'format shape
s.Fill.ForeColor.RGB = RGB(300, 300, 300)
s.Line.ForeColor.RGB = RGB(0, 0, 0)
s.Line.Weight = 3
'transparency
s.Fill.Transparency = 0
'add text from list
s.OLEFormat.Object.Text = t
If Cells(x, "A").Value = "" Then BlankFound = True
x = x + 1
Loop
End Sub
The code from the answer before me works great, but the text does not show because it's white.
To show the text in the process blocks you can give them a color with:
s.TextFrame2.TextRange.Font.Fill.ForeColor.RGB = RGB(0, 0, 0)
Code:
Sub addshapewithtext()
Dim BlankFound As Boolean
Dim x As Long
Dim w As Worksheet
Dim s As Shape
Dim t As Variant
x = 1
Do Until Cells(x, "A").Value = ""
Set t = Cells(x, "d")
Set w = ActiveSheet
Set s = w.Shapes.AddShape(msoShapeFlowchartProcess, 200 + x * 200, 100, 100, 100)
'format shape
s.Fill.ForeColor.RGB = RGB(300, 300, 300)
s.Line.ForeColor.RGB = RGB(0, 0, 0)
s.Line.Weight = 3
'Add color to text in process block
s.TextFrame2.TextRange.Font.Fill.ForeColor.RGB = RGB(0, 0, 0)
'transparency
s.Fill.Transparency = 0
'add text from list
s.OLEFormat.Object.Text = t
If Cells(x, "A").Value = "" Then BlankFound = True
x = x + 1
Loop
End Sub

Excel VBA moving Shapes to a column

I am having a problem with a program for my Excel VBA course. I have written a program to add 5 each of lines, rectangles, ovals and triangles to a worksheet this is the btnAddShapes click event. In the cmdAlignRectangles click event I am trying to take only the rectangles that were added and align them all in the C column. I have used a For Each loop to select all the shapes on the sheet, the For Each loop structure is required for the assignment. Then I used an If/Then statement to select the shape Type msoShapeRectangle. I used the name that I assigned in when creating the rectangles such as "Box1" using the counter I to iterate through each rectangle, it is this statement that is giving me an error saying that the item with that name was not found. I must use the Left property of the Range and Shape objects to move the rectangles.? Any help or guidance would be greatly appreciated.
Private Sub btnAddShapes_Click()
Randomize
For I = 1 To 5
ActiveSheet.Shapes.AddShape(msoShapeRectangle, 50, 100, 100, 65).Select
With Selection
.Name = "Box" & I
.Left = Int(422 * Rnd)
.Top = Int(422 * Rnd)
End With
ActiveSheet.Shapes.AddLine(10 + I * (Rnd * 133), 50 + I * (Rnd * 133), 125 + I * (Rnd * 133), 250 + I * (Rnd * 133)).Select
With Selection
.Name = "Line" & I
End With
ActiveSheet.Shapes.AddShape(msoShapeOval, 275, 240, 108, 44).Select
With Selection
.Name = "Oval" & I
.Left = Int(444 * Rnd)
.Top = Int(444 * Rnd)
End With
ActiveSheet.Shapes.AddShape(msoShapeIsoscelesTriangle, 514, 220, 93, 71).Select
With Selection
.Name = "Triangle" & I
.Left = Int(377 * Rnd)
.Top = Int(377 * Rnd)
End With
Next I
End Sub
Private Sub btnRemoveShapes_Click()
Dim sh As Shape
For Each sh In ActiveSheet.Shapes
If Not (sh.Type = msoOLEControlObject Or sh.Type = msoFormControl Or sh.Type = msoTextBox) Then sh.Delete
Next sh
End Sub
Private Sub cmdAlignRectangles_Click()
Dim allRectangles As Shapes
Dim sh As Shape
Dim I As Integer
Set allRectangles = ActiveSheet.Shapes
I = 1
For Each sh In allRectangles
If sh.Type = msoShapeRectangle Then
ActiveSheet.Shapes("Box" & I).Left = Cells(I, 3).Left
End If
I = I + 1
Next
End Sub
The error is that in the creation loop you create 4 shapes for each 1, I going from 1 to 5. On the other hand, in the alignment loop you iterate one I for each shape. Therefore, when I reaches 6 (with the 6th shape), the object named "Box6" does not exist.
A simpler way to achieve this would be to modify our test by examining the name of the shape, like this, for example:
If sh.Type = msoShapeRectangle And InStr(sh.Name, "Box") = 1 Then
sh.Left = Cells(I, 3).Left
End If
p.s. you can also drop the first part of the test

VBA check if next 10 rows and 10 columns in all 4 sides of a Excel Table is empty

In VBA Excel, if I have a table. How do I check the cells outside the table in all 4 sides of it, for 10 rows and 10 columns, as empty or not?
Thanks
Jeevan
You could use this function:
Option Explicit
Function NonBlankCellsOutside(rng As Range, rowsOutside As Long, colsOutside As Long)
Dim outside As Range
Dim rowsBefore As Long
Dim colsBefore As Long
rowsBefore = IIf(rng.Row <= rowsOutside, rng.Row - 1, rng.Row - rowsOutside)
colsBefore = IIf(rng.Column <= colsOutside, rng.Column - 1, rng.Column - colsOutside)
Set outside = rng.Offset(-rowsBefore, -colsBefore) _
.Resize(rng.Rows.Count + rowsBefore + rowsOutside, _
rng.Columns.Count + colsBefore + colsOutside)
NonBlankCellsOutside = WorksheetFunction.CountA(outside) _
- WorksheetFunction.CountA(rng)
End Function
Example use with a normal range:
Dim ok As Boolean
ok = NonBlankCellsOutside(Worksheets(1).Range("C20:F50"), 10, 10) = 0
If Not ok Then MsgBox "There are non-blank cells in the neighbourhood"
Another example with a named table:
Dim num As Long
num = NonBlankCellsOutside(ActiveSheet.ListObjects("Table1").Range, 5, 5)
MsgBox "There are " & num & " non-blank cells around the table"
You can do this with in-cell formulae.
Given a table named Table1 whose top-left corner is no closer to the top or to the left than K11, and the following formulae, the value in A5 will give you your answer:
A B C
1
2 Range start =ROW(Table1)-10 =COLUMN(Table1)-10
3 Range end =ROW(Table1)+ROWS(Table1)+9 =COLUMN(Table1)+COLUMNS(Table1)+9
4
5 =AND(B2>0, B3>0, COUNTA(INDIRECT("r"&B2&"c"&C2&":r"&B3&"c"&C3, FALSE))=COUNTA(Table1[#All]))
Here I have something that works for any named table, as long as its first cell is no closer to the edges than K11.
Sub checkSurroundings()
Dim tws As Worksheet
Dim tb1 As ListObject
Dim tb1_address As String
Dim c() As String 'Table range, first and last cell
Dim rngL, rngR, rngU, rngD As Range
Dim tmpRange As Range
Dim cnt As Integer
Set tws = ThisWorkbook.Worksheets("Sheet1")
Set tb1 = tws.ListObjects("Table1")
tb1_address = tb1.Range.Address
'Debug.Print tb1_address
c() = Split(tb1_address, ":", -1, vbTextCompare)
'Debug.Print c(0)
'Debug.Print c(1)
cnt = 0
With tws
'Range Left
Set rngL = Range(.Range(c(0)).Offset(-10, -10), .Cells(.Range(c(1)).Row + 10, .Range(c(0)).Column - 1))
'Range Right
Set rngR = Range(.Cells(.Range(c(0)).Row - 10, .Range(c(1)).Column + 1), .Range(c(1)).Offset(10, 10))
'Range Up
Set rngU = Range(.Range(c(0)).Offset(-10, 0), .Cells(.Range(c(0)).Row - 1, .Range(c(1)).Column))
'Range Down
Set rngD = Range(.Cells(.Range(c(1)).Row + 1, .Range(c(0)).Column), .Range(c(1)).Offset(10, 0))
End With
For i = 1 To 4
Select Case i
Case 1
Set tmpRng = rngL
Case 2
Set tmpRng = rngR
Case 3
Set tmpRng = rngU
Case 4
Set tmpRng = rngD
End Select
For Each cell In tmpRng
If Not IsEmpty(cell) Then
cnt = cnt + 1
End If
Next cell
Next i
If cnt > 0 Then
MsgBox ("The area around Table1 (+-10) is not empty. There are " & cnt & " non-empty cells.")
Else
MsgBox ("The area around Table1 (+-10) is empty.")
End If
End Sub

VBA Index function size limit

In cells A1:A66000 I have the numbers 1, 2, ... 66000.
Sub addData()
Application.ScreenUpdating = False
Cells(1, 1) = 1
Cells(2, 1).Formula = "=A1+1"
Range(Range("A66000"), Range("A66000").End(xlUp)).Select
Selection.FillDown
Application.ScreenUpdating = True
End Sub
The following code loads the data into an array and finds the index of the number 2. It returns the correct result, 2.
Sub test()
Dim arr As Variant
arr = ArrayFromRange(Range("A1:A65536"))
MsgBox Application.WorksheetFunction.Match(2, Application.Index(arr, 0, 1), 0)
End Sub
However, changing the array size causes a Type Mismatch error due to the Index function.
Sub test()
Dim arr As Variant
arr = ArrayFromRange(Range("A1:A65537"))
MsgBox Application.WorksheetFunction.Match(2, Application.Index(arr, 0, 1), 0)
End Sub
How can I get around this? I'm using Excel 2007.
EDIT: I forgot to include this handy function that I'm calling
Function ArrayFromRange(rg As Range) As Variant()
'==============================================================================================
'Returns an array from a given range
' BG Feb 2013
'==============================================================================================
If (rg.Cells.Count = 1) Then
Dim arr(1 To 1, 1 To 1) As Variant
arr(1, 1) = rg.Value
ArrayFromRange = arr
Else
ArrayFromRange = rg ' Arr is now an allocated array
End If
End Function
Since there's a hard limit on the size of an array you can pass to WorksheetFunction.xxxx in VBA, you can instead leave the data on the sheet, and query it directly. This has the advantage of being much faster...
Sub test()
Dim arr As Variant, v, t, i As Long
Dim rng As Range
Set rng = ActiveSheet.Range("A1:A65536")
arr = rng.Value
'array-based approach
t = Timer
For i = 1 To 100
v = Application.WorksheetFunction.Match(i, Application.Index(arr, 0, 1), 0)
If i <= 5 Then Debug.Print v
Next i
Debug.Print Timer - t '>> 1.55 sec
'query worksheet directly
t = Timer
For i = 1 To 100
v = rng.Parent.Evaluate("MATCH(" & i & ", INDEX(" & rng.Address() & ", 0, 1), 0)")
If i < 5 Then Debug.Print v
Next i
Debug.Print Timer - t '>> 0.008 sec
End Sub