Is there a way to count number of rows of texts inside a Visio shape? Such as linecount?
I've tried Rowcount on a Visio shape and it didn't return anything reflecting the text lines inside the Visio Shape! Below is that sample code I created
Sub something()
Dim intRows
Dim vsoShape As Visio.Shape
Set vsoShape = ActiveWindow.Selection.PrimaryItem
intRows = vsoShape.RowCount(Visio.visSectionProp)
MsgBox intRows
End Sub
There is no built-in way to count the number of lines of text, that I'm aware of.
The RowCount is for counting the number of rows in a particular shapesheet section.
You can call the BoundingBox method on a shape and get back the height and width of the text area for the shape, but you'd have to guess at how many lines that is, maybe as a function of the font size.
If you are able to enforce a standard font and character size on shapes in the diagrams you're working with, you should be able to tell how many lines there are based on the height of the text box.
I'm assuming you're asking after the number of line wraps that Visio has done, not the number of line breaks in the text.
Related
I wrote some macro code in Word (Office 365) to set the color of a shape outline to one of the theme colors. The code for doing that to a shape looks like this:
shape.line.foreColor.ObjectThemeColor = wdThemeColorAccent2
By assigning a 'wdXX' color to the ObjectThemeColor field, the color of the line around the shape will automatically change when the document ColorTheme is changed.
My problem (or the first weirdness) is that when I assign Accent2 with the code above and then do: select the shape, Menu, Format, Shape Outline, and hover over the color box with a red outline (which marks the active line color), the tooltip says "Turquoise, Accent 1" not "Accent 2."
I would have expected the wdThemeColorAccent2 color to be called Accent 2 in the tooltip, but it is not.
The second problem is that there is apparently no way for me to assign the last color shown in the menu using macro code. Because of the offset (Accent 2 in code = Accent 1 in the menu), I would need to use wdThemeColorAccent7 in code to assign the last color shown in the menu (labeled Accent 6 in the tooltip).
I'm wondering if this is a bug in Word (it sure looks like it to me), or if I am doing something wrong. To reproduce the situation, I created a simple empty rectangle, selected it, and ran the line of code above to change the outline color of the shape. Here's a little subroutine that illustrates the problem (select your shape before running the subroutine).
Sub TestAccent()
Dim shp As Shape
Set shp = selection.ShapeRange(1)
shp.line.foreColor.ObjectThemeColor = wdThemeColorAccent4
shp.line.Weight = 0.5
shp.line.Visible = True
End Sub
I believe the colors in the "theme scale" (see image below) don't correspond to the names of the WdThemeColorIndex, but rather to the underlying numerical value. If you look in the VBA Editor's Object Browser (F2), and search wdThemeColorAccent you'll get the full list. Click on a member in the list and at the bottom you'll see the numerical value.
The value 0 is assigned to MainDark1 and isn't recognized by VBA. Values 1, 2 and 3 are assigned to MainLight1, MainDark2 and MainLight2 which are black, white and the first entry in the image (These colors repeat in the last four enumerations for background and text). Values 4 (wdThemeColorAccent4) through 9 (wdThemeColorAccent6) correspond to the remainder of the colors in the image below. (Note: more discussion after image!)
So, no, I don't think it's a bug, just your expectations don't match what the developers were thinking when they assigned the numerical enumeration to the enumeration names. Or maybe the people who designed the color schemes changed their minds after the VBA code was locked down... And I imagine the names you see in the tooltips are another step removed from the VBA. You might find the information in this article helpful.
If you use the values, rather than the names, things could be less confusing. Or, define your own Enum:
Public Enum ColorSchemeAccents
Accent1 = 3
Accent2 = 4
Accent3 = 5
Accent4 = 6
Accent5 = 7
Accent6 = 8
Accent7 = 9
Accent8 = 10
End Enum
Sub TestAccent()
Dim shp As Shape
Set shp = Selection.ShapeRange(1)
shp.Line.ForeColor.ObjectThemeColor = ColorSchemeAccents.Accent8
shp.Fill.ForeColor = RGB(250, 250, 250)
shp.Line.Weight = 2
shp.Line.Visible = True
End Sub
Although the ColorFormat object's .ObjectThemeColor is defined as a wdThemeColorIndex in fact the value depends on context.
If it is a Word object - such as text, then you should use the wdThemeColorIndex constants, but if it is an Office object - such as shape, then you have to use the msoThemeColorIndex constants. These are weirdly NOT the same - mostly the mso constants are one more than the wd constants, but not for the Background1&2 and Text1&2 cases - Text1&2 are the same in both cases, but for Background1&2 mso is two more than wd.
A side effect of this is that it appears impossible in VBA to set the Background2 colour, as its mso value is 16 and so out-of-range BUT if you use the native GUI to set it, it can be set to 16!
Looks really poor design/implementation that needs cleaning up!
I'm automatically generating a powerpoint slide through VBA, User Forms, and Excel. You run the VBA script in excel, fill out the data, the data goes into cells in excel, then the VBA script pulls the data and puts it into textbox shapes in the slide.
My problem is I want to use different font sizes at different times, so for example 28 pt font for one part and 14 pt for the rest. The problem is that any property changes I make to the textbox applies to all of the text within the shape.
My current workaround is sloppy and is to just generate another textbox over the original and insert spacing in the original so it looks like the larger text is "in" the textbox while it's actually just sitting over a few empty lines set aside.
You can format specific substrings within a string, but it's very cumbersome, for example assuming shp is an object variable representing your textbox:
Sub foo()
Dim shp As Shape
Set shp = ActivePresentation.Slides(1).Shapes("TextBox 3")
shp.TextFrame.TextRange.Text = "Hello, world!"
shp.TextFrame.TextRange.Characters.Font.Size = 14 'applies uniform font to entire shape
shp.TextFrame.TextRange.Characters(1, 5).Characters.Font.Size = 28
End Sub
Example output:
The difficulty of course is working with mixed formats, and I do not think there is any easy solution. It will be up to you to determine what formats you need to "capture", and what subsequently implement the appropriate conditional logic to transfer those formats to the PowerPoint shapes.
One possible alternative -- and this is the route that I would go if I were you -- would be to copy the cell from Excel, and use this method to paste in to PowerPoint. I believe this will create a table consisting of a single cell, in the PowerPoint slide. You will have to make adjustments for size/position, but this should be an order of magnitude easier than trying to capture every possible variation of font formatting:
Sub foo2()
Dim shp As Shape
Dim xl As Object
'Get Excel and copy a specific cell
Set xl = GetObject(, "Excel.Application")
xl.Workbooks("Book35").Sheets("Sheet2").Range("B4").Copy
'Paste that cell in to PowerPoint as a table, preserving formats:
ActivePresentation.Slides(1).Select
Application.CommandBars.ExecuteMso "PasteSourceFormatting"
End Sub
Example output, as copied from the Excel cell:
No need to change the font in excel to reflect in Word. You can do it directly. Just paste the below mentinoed line in Word VBA : -
Activedocument.Shapes("sam").TextFrame.TextRange.Words(1).Font.Size = 28
I need to know is selected shape word art or not.
Shape has property "Type" (returns enum MsoShapeType).
When I insert word art and check this property - it returns msoAutoShape instead of msoTextEffect (with AutoShapeType==msoShapeRectangle).
How can I check that spae is word art (not usual rectangle with textbox) ?
Thanks!
If you select either the overall smartart shape or click into text within the smartart shape or select one of the shapes within the smart art, ActiveWindow.Selection.ShapeRange(1) will return the smartart shape.
So
If ActiveWindow.Selection.ShapeRange(1).HasSmartArt Then
Debug.Print "It's smart art"
End if
[edited]
But as you've pointed out, this is for smartart, not word art. My error, sorry.
There isn't a WordArt shape as such; it's more like any shape that has had WordArt formatting applied to the shape as a whole or to text within the shape. That'd include formatting like glow, reflection, shadow and so on, or could be one of the WordArt presets, pre-selected combinations of these different effects. I've added an example that'll help identify shapes or ranges of text within shape that have these presets applied. I don't see any simple way of checking for user-applied WordArt formats other than looking at each run and each text box for each of the various properties (glow, reflection etc) that might be applied. Unfortunately, there's no WordArtFormat = None to tell us we can ignore it. It's either going to be one of the presets or -2, which can mean any of several things.
Sub WordArtist()
Dim oSh As Shape
Dim oRng As TextRange2
' Has word art formatting been applied to
' entire shape?
Set oSh = ActiveWindow.Selection.ShapeRange(1)
Debug.Print oSh.TextFrame2.WordArtFormat
' Has it been applied to individual chunks of
' text within the shape
For Each oRng In oSh.TextFrame2.TextRange.Runs
Debug.Print oRng.Font.WordArtFormat
Next
' Note:
' A result of -2 for the entire shape could mean
' - No text in the shape; not really word art
' - Mixed formatting
' - Text/shape has had glow/shadow/reflection etc applied
' rather than one of the preset WordArt selections
End Sub
I'm writing a code generation tool using VBA in Excel (don't ask why—long story). I need to be able to "parse" a flowchart.
The problem is that Excel allows shapes to contain text, with the exception of connectors: lines and arrows can't contain text. To label an arrow, you just put a text box on top of it—but the box isn't "attached" to the arrow in a way that VBA can easily capture.
For example, a user might draw something like this:
Within my VBA code, I can use ActiveSheet.Shapes to find that the flowchart contains seven shapes: there are five boxes (the two labels are just boxes with no border) and two arrows. Then Shape.TextFrame2 will tell me what's written inside each box, and Shape.ConnectorFormat will tell me which box goes at the start and end of each arrow.
What I need is code that can deduce:
Label A belongs to the arrow from Box 1 to Box 2
Label B belongs to the arrow from Box 1 to Box 3
I can think of three ways of doing this, none of them satisfactory.
Ask the user to group each label with its corresponding arrow.
Find out the coordinates of the endpoints of each arrow, then
calculate which arrows pass through which labels.
Find out the coordinates of the corners of each box, then calculate
which labels lie between which pairs of boxes.
Method 1 makes things easier for the programmer but harder for the user. It opens up a lot of potential for user error. I don't see this as an acceptable solution.
Method 2 would be reasonably easy to implement, except that I don't know how to find out the coordinates!
Method 3 is doable (Shape.Left etc will give the coordinates) but computationally quite messy. It also has potential for ambiguity (depending on placement, the same label may be associated with more than one arrow).
Note that methods 2 and 3 both involve trying to match every label with every arrow: the complexity is quadratic. Typical applications will have 10–50 arrows, so this approach is feasible, if somewhat inelegant.
Does anyone have a better idea? Ideally it would be something that doesn't involve coordinate geometry and complicated logic, and doesn't involve asking users to change the way they draw flowcharts.
Edited to add: example 2 in response to Tim Williams
Here's a label whose bounding box intersects the bounding box of both arrows, and whose midpoint isn't inside the bounding box of either arrow. Visually it's easy for a human to see that it belongs with the left arrow, but programmatically it's hard to deal with. If I can find out the coordinates of the arrows' endpoints, then I can calculate that one arrow passes through the label's box but the other doesn't. But if all I have is the bounding rectangles of the arrows, then it doesn't work.
Interesting problem. What if you considered the range covered by the arrow and the range covered by the textbox and matched them up based on the most overlap.
Sub ListShapes()
Dim shp As Shape
Dim shpArrow As Shape
Dim vaArrows As Variant
Dim i As Long
Dim rIntersect As Range
Dim aBestFit() As String
Dim lMax As Long
vaArrows = Split("Straight Arrow Connector 7,Straight Arrow Connector 9", ",")
ReDim aBestFit(LBound(vaArrows) To UBound(vaArrows))
For i = LBound(vaArrows) To UBound(vaArrows)
Set shpArrow = Sheet1.Shapes(vaArrows(i))
lMax = 0
For Each shp In Sheet1.Shapes
If shp.Name Like "Label*" Then
Set rIntersect = Intersect(Sheet1.Range(shp.TopLeftCell, shp.BottomRightCell), _
Sheet1.Range(shpArrow.TopLeftCell, shpArrow.BottomRightCell))
If Not rIntersect Is Nothing Then
If rIntersect.Count > lMax Then
lMax = rIntersect.Count
aBestFit(i) = shp.Name
End If
End If
End If
Next shp
Next i
For i = LBound(vaArrows) To UBound(vaArrows)
Debug.Print vaArrows(i), aBestFit(i)
Next i
End Sub
I tested this with the five box-two arrow setup and nothing more complicated. I put my two arrows in an array, but I assume you have ways to identify the arrows. I also named my untethered boxes "Label x" so I could identify them, but again I assume you have something more sophisticated.
The code loops through every arrow. Inside that loop, it loops through every shape. If it's a label, then it counts the cells in the intersection of the two ranges. Whichever has the most is stored in the best fit array.
It would be nice if you had a reasonable corpus of flow charts to test this to see where the pitfalls are. I don't think this is necessarily better than use the coordinates, just a different approach.
You can find the coordinates of the arrow's endpoints as follows.
First of all, the .Left, .Top, .Width and .Height properties describe the bounding rectangle of the arrow, as Tim Williams points out.
Next, check the .HorizontalFlip and .VerticalFlip properties. If both are false, then the arrow runs from top left to bottom right in its bounding rectangle. That is, the beginning of the arrow has coordinates (.Left,.Top) and the end has coordinates (.Left+.Width,.Top+.Height).
If either *.Flip is true, then the coordinates need to be swapped around as appropriate. E.g., if .HorizontalFlip is true but .VerticalFlip false, then the arrow runs from (.Left+.Width,.Top) to (.Left,.Top+.Height).
As far as I can tell, this is not documented anywhere on MSDN. Thanks to Andy Pope for mentioning it at excelforums.com.
Given this, method 2 seems like the best approach.
I am trying to create a new PowerPoint plug-in. I have run into a problem for a case where I want to determine if for a given shape on slide 1, whether the same shape also exists on the next slide.
Is there a way by which I can compare shapes from different slides and determine whether they are the same?
I can probably compare the type, dimensions, text and other similar properties, but this may not be the right way to solve this problem. Is there a better way to do this?
Something like this will return an "identical" shape from another slide if it meets your criteria. You could have it return True/False instead if you prefer:
Function SameShape(oThisShape As Shape, oOtherSlide As Slide) As Shape
Dim oSh As Shape
For Each oSh In oOtherSlide.Shapes
If oSh.Type = oThisShape.Type Then
If oSh.Height = oThisShape.Height Then
If oSh.Width = oThisShape.Width Then
' other conditions here as required
Set SameShape = oSh
Exit Function
End If
End If
End If
Next
End Function
One caveat: if the shape's .Type = msoPlaceholder, you'll also need to look to see whether .PlaceholderFormat.ContainedType is the same.
What is an "identical" Shape for you?
All shapes hafe different IDs so you cannot compare them, but you could compare the Size, the location (Shape.Width, Shape.Height etc.) and maybe the content (a chart, a table or text?).
If enough Properties are equal they might be considered as equal.