Putting "Align at equals" into a macro - vba

I'm working on a Word Macro to streamline doing my University Mathematics and Statistics Coursework. Basically, selecting a group of equations and running it changes font size, line height and formats the paragraph in the way that I want to be common to all my maths/equations sections. It's great, but there's one little bit which I still have to do "manually", so to speak, which is right clicking and selecting "Align at equals".
Now the reason I'm asking this here and not on Super User is that I think I've exhausted all ways of doing this at the "record macro" stage. I found out how to access the right click menu without right clicking and accessed the "align at equals" option during record. Nothing was recorded.
Truth be told I'd prefer to code the lot anyway as it gives me more control. So, I'll post my code here and if anyone knows what line(s) of code I need to add to get it to replicate the "align at equals" command I would be extremely grateful.
Sub Equationiser()
'
' Equationiser Macro
'
'
With Selection.ParagraphFormat
.SpaceBefore = 12
.SpaceAfter = 12
.LineSpacingRule = wdLineSpace1pt5
End With
Selection.Font.Size = 20
End Sub
So, ideally just before the "With Selection.ParagraphFormat" section there would be some kind of "AlignAtEquals" command or whatever is needed so that, on one keypress, I would be able to align all the equals, set the line height to 1.5, place a 12 point space before and after the paragraph and change the font size to 20.
My absolute ideal would also be to programatically select all equation boxes that are in the same block, as "align at equals" is notoriously fussy and finicky as to when it will execute. That might also mean there may be a try and catch needed depending on whether trying to run "align at equals" when it wouldn't normally be available from the right click menu would do nothing or cause an error.
Any help on these two implementations would be gratefully appreciated but I'd happily settle for just the first.

I've built a solution that should address your needs, based on:
Looking through the equation to find the equal-sign.
Get the location of that equal-sign
Use that location to set the AlignPoint property of the OMath object
that is your equation
Use this MSDN reference if you want to explore more
Sub Equationiser()
'
' Equationiser Macro
'
'
Dim equationCounter As Long, charLoc As Long, FormattedTextLoc As Long
With Selection
For equationCounter = 1 To .OMaths.Count:
FormattedTextLoc = 0
For charLoc = 1 To Len(.OMaths(equationCounter).Range.FormattedText):
FormattedTextLoc = FormattedTextLoc + Len(.OMaths(equationCounter).Range.FormattedText.Characters(charLoc))
If .OMaths(equationCounter).Range.FormattedText.Characters(charLoc) = "=" Then
.OMaths(equationCounter).AlignPoint = (FormattedTextLoc - 1)
Exit For
End If
Next charLoc
Next equationCounter
End With
With Selection.ParagraphFormat
.SpaceBefore = 12
.SpaceAfter = 12
.LineSpacingRule = wdLineSpace1pt5
End With
Selection.Font.Size = 20
End Sub
I've done some brief testing that from what I can see this should be able to manage several code-blocks, i.e. when selecting 2 code-blocks it will align-at-equals all equations in the first block, then align-at-equals all equations in the second block (both blocks are not aligned with each oter) - is this your desired outcome to your request: My absolute ideal would also be to programatically select all equation boxes that are in the same block, as "align at equals" is notoriously fussy and finicky as to when it will execute.

Related

Redistribute Visio Diagram so Begin & End are in the extremes

I created a VBA macro that creates a Excel spreadsheet-based diagram in Visio. However, when I try to re-distribute the layout of the diagram, somehow the "End" label always ends up in the middle of the drawing, instead of being linear from "Start" to "End". This is the only problem: all connectors are OK, all shapes contain the required information.
Is there a way to have Visio ensure both Start & End are left in the corners of the diagram? I was thinking there might be a property in the "Start" & "End" that could drive this behavior.
Edit #1: Added what I have of code, although is not really fancy.
As of now, the only thing I'm using to distribute the diagram is the standard option:
Sub vsDiagramClosure()
'Layout Arrange
With vsPag
With .PageSheet
.CellsSRC(visSectionObject, visRowPageLayout, visPLOPlaceStyle).FormulaForceU = "1"
.CellsSRC(visSectionObject, visRowPageLayout, visPLORouteStyle).FormulaForceU = "5"
End With
.Layout
End With
End Sub
I adapted that from the macro recorder. vsPag was declared as Global vsPag As Visio.Page & it already has been assigned the proper page.
Thanks,

Word VBA Font.Hidden property becomes unreadable if selection is in table and selection is big

This is very easy to replicate....Please follow my steps with MS Word.
In a Word file, create a fairly large table (of about five columns and twenty rows). Populate all cells with some junk text.
Or, pick a "good sized" table in a file that you already have.
Select (or "Highlight") a few rows of the table, such as three rows.
Go to the Font section of the Word Ribbon. Notice that the Hidden
property of the selection is showing as "not checked" which means
visible. This is what you expect to see (for now).
Optionally, also go to the Immediate Window of the VB Editor and
type "? Selection.Range.Font.Hidden". Word will respond "0", meaning
the .Hidden property is false. This is another way of verifying what
you saw in Step 2.
Now select an additional few rows of the table. Go to Step 2 and repeat.
By the time your Selection gets up to about 12 rows (your results may vary somewhat), the Hidden checkbox shows a solid block to indicate "mixed hidden-ness", and "? Selection.Range.Font.Hidden" shows 9999999 to indicate 'undefined" state.
WHY does the .Hidden property become unreliable/unreadable when the selection is in a table and the selection is relatively many rows? This does not happen when the Selection in a non-table. This is messing up a program of mine that is otherwise very good.
Thanks to all who puzzled over this bug in MS Word. My workaround is as follows. I make the assumption that the hidden-ness (or other property of the selection like .Font or .Underline) of the the selection is "uniform" ... that I can try to read the property of the first 10 characters rather than the entire selection, and still get the right answer. If you do not know that your entire selection has the same properties as the first 10 characters of your selection, this solution won't help you.
Function DigDeeperInto(fullSelection As Selection) As String
' This returns the truth of the hidden-ness of the *front end* of a Selection.
' This is used because Word sometimes mis-reads the hidden-ness of a whole Selection.
Dim shortRange As Range ' will hold the just the front end of the full Selection
Dim theTruth As String
Set shortRange = fullSelection.Range
shortRange.SetRange Start:=shortRange.Start + 1, End:=shortRange.Start + 10
If shortRange.Font.Hidden = wdUndefined Then theTruth = "UnDef" ' this Function failed
If shortRange.Font.Hidden = 0 Then theTruth = "Visible"
If shortRange.Font.Hidden = -1 Then theTruth = "Hidden"
DigDeeperInto = theTruth
End Function
After selecting a Selection, and assuming that Selection.Font.Hidden will not be reported correctly, I call the DigDeeperInto(Selection) function. It looks at only the first 10 characters and returns a string "Visible", "Hidden", or "UnDef". If it still returns "UnDef" (which does not happen for me, luckily), that would indicate DigDeeperInto() failed to solve the problem.
For what it's worth this is not specific to tables; the exact same thing happens in a document that has 51 blank (but otherwise identical) paragraphs. If you select 50 paragraphs you can get real results for:
?selection.Range.Paragraphs.Count ' just to confirm the selection
?selection.Font.name
?selection.Range.Font.Hidden
BUT as soon as you select the 51st paragraph things lose definition, so to speak.
I imagine that looping over your table row by row (as suggested by #jsotola) would be a good workaround.
To answer the question as to WHY this happens, well the best anyone outside of MS can do (I think) is speculate. If I were to speculate I would suggest that there is some sort of internal limit or limitation when it comes to processing or storing detailed information for so many paragraphs. Is it a bug, I think so (but that is just IMHO).
Function to replace the buggy built-in function
Example of a function that will return True, False or wdUndefined (9999999) depending on whether font is Hidden, Visible or a mixture of Hidden & Visible (i.e. undefined), respectively.
Note that it is entirely possible for wdUndefined to be a valid result as an otherwise visible paragraph may have a single character hidden by font and therefore that paragraph's font would neither be entirely visible, not hidden - so it is 'undefined' - hence wdUndefined is very valid.
Example usage and output:
?selection.Paragraphs.Count
2016
?fontIsHidden '(automatically operates on the active selection)
False
The function:
' Put this code into a STANDARD module
Function fontIsHidden() As Variant ' Variant to return True, False, wdUndefined
Dim paraSelected As Paragraph
Dim NotFirstPara As Boolean
Dim result As String
For Each paraSelected In Selection.Range.Paragraphs
' This could be done with Table.Rows instead of Paragraps
' (but would error if there was no table)
If NotFirstPara Then
If paraSelected.Previous.Range.Font.Hidden <> paraSelected.Range.Font.Hidden Then
result = wdUndefined ' this para is different to the previous paras
Exit For
End If
Else
' Setup for the first paragraph
Select Case paraSelected.Range.Font.Hidden
Case False ' 0
result = False
Case True ' -1
result = True
Case wdUndefined
result = "Undefined" ' some text in the para is visible and some is hidden
End Select
NotFirstPara = True
End If
Next paraSelected
fontIsHidden = result
End Function

Excel VBA: Updating userform position/size leaves trail

I am fairly familiar with userforms, so I decided to try and get a little fancy and attempted to make a userform grow/shrink and reposition to align center based on a checkbox change event.
The code is working as intended, but there is one issue. When shrinking the userform, the userform leaves a trail of all size increments set during the loop. This makes the program look less "smooth". I've included a screenshot of the issue as well as the relevant code.
I've tried including variations of DoEvents and Application.ScreenUpdating. ScreenUpdating seemed to do nothing, while DoEvents halved the number of trails, but also made the text inside the userform go a bit crazy during execution.
Note: The checkbox is named "MyCheckBox" and the userform is named "ColumnSelect"
Perhaps I'm trying to do too much with excels memory or w/e.. Any help is appreciated though. Thank you!
Private Sub MyCheckBox_Change()
Dim w As Integer
Application.ScreenUpdating = False
If MyCheckBox.Value = True Then
For w = 425 To 838 Step 7
ColumnSelect.Width = ColumnSelect.Width + 7
ColumnSelect.Left = ColumnSelect.Left - 3.5
Next w
Else
'DoEvents
For w = 838 To 425 Step -7
ColumnSelect.Width = ColumnSelect.Width - 7
ColumnSelect.Left = ColumnSelect.Left + 3.5
Next w
End If
Application.ScreenUpdating = True
End Sub
I have tested the file on my home (gaming) of, which is much more powerful than the laptop/monitor setup I used when posing the question. There was no visible trail when resizing the user form, so it seems user A.S.H was correct in his assumption that the issue involved the PC/monitor rather than the code.
If you want credit for the answer I will be happy to give it to you, just submit your own. Otherwise I'll mark this as answerws. Thanks for your help

Execute specific line vba

I couldn't find this asked anywhere. In Visual Basic (excel), I can hit F8 and cycle through each line. But lets say I want to begin the sub procedure, and then after executing the first 2 lines, I'd like to skip to line 200. Until now, I've always just dragged the yellow arrow to the desired line. This is really time consuming and I was wondering if there's any command to simply say "run current line where selected" or something.
Additionally, even if I could begin to run through line by line, and quickly move the yellow selected arrow to the desired line, that would also work.
If you have a 200-liner procedure that does so many things you'd like to skip most of it, it looks like you need to refactor a bit.
Extract "things the procedure is doing" into their own Sub procedures and Function scopes. If you have banner-like comments that say things like '*** do something *** then that's a chunk to extract into its own procedure already.
Stepping through that procedure could then involve stepping over (Shift+F8) the smaller procedures that do one thing, or break and skip the call altogether.
Right click on the line you want to jump to. Hit the Set Next Statement option in the menu. That's equivalent to dragging the arrow to that line. (Ctrl-F9 is the hotkey for this action.)
If you want it to quickly execute every line up to a certain line, set a breakpoint then hit run instead of stepping through the code line by line. Do this by clicking on the gray bar to the left side where the yellow arrow appears. A dark red dot should appear and the line should be highlighted in dark red. This tells visual basic to stop when it hits that line.
You can also comment lines out by starting them with an apostrophy.
Finally, you can break code into subroutines and execute them independently of eachother.
Sub Subroutine1()
'This is a commented out line. It does nothing.
MsgBox "Do stuff here"
End Sub
Sub Subroutine2()
Subroutine1 'This will run all the code in subroutine 1
MsgBox "Do more stuff here"
End Sub
In the above example, if you run Subroutine1 you'll get one message box popping up. If you run Subroutine2 you'll get two message boxes.
Unfortunately it is not possible to do what you ask directly.
However, you may comment out the lines of code above the code you want to be executed for example:
Sub Workbook_Open()
'Application.DisplayFullScreen = True
'Application.DisplayFormulaBar = False
'ActiveWindow.DisplayWorkbookTabs = False
''ActiveWindow.DisplayHeadings = False
Application.EnableEvents = True
Password = "1234"
ActiveWorkbook.Protect
ThisWorkbook.Protect (Password = "1234")
End Sub
You may use GoTos, but however this is not considered good practice and may actively harm your code:
Sub Workbook_Open()
GoTo ExecuteCode
Application.DisplayFullScreen = True
Application.DisplayFormulaBar = False
ActiveWindow.DisplayWorkbookTabs = False
ActiveWindow.DisplayHeadings = False
Application.EnableEvents = True
ExecuteCode:
Password = "1234"
ActiveWorkbook.Protect
ThisWorkbook.Protect (Password = "1234")
End Sub
This is how I do it - basically if I know that my code up to line 200 is working properly but I'm pretty sure there's an error between 200-300 then before compiling - scroll down to line 200 and mark it (to the left of the code). Then compile it - click F5 and it will execute everything up to line 200 - then you can step through each line thereafter individually.
I normally comment out lines of code that I don't want to run with apostrophes. Alternatively, you can break up your code into smaller procedures so that you can easily pick and choose what you want to test/run.
I found adding a module for testing and copy pasting snippets of code in it as a best way for troubleshooting.

GetCrossReferenceItems in msword and VBA showing only limited content

I want to make a special list of figures with use of VBA and here I am using the function
myFigures = ActiveDocument.GetCrossReferenceItems(Referencetype:="Figure")
In my word document there are 20 figures, but myFigures only contains the first 10 figures (see my code below.).
I search the internet and found that others had the same problem, but I have not found any solutions.
My word is 2003 version
Please help me ....
Sub List()
Dim i As Long
Dim LowerValFig, UpperValFig As Integer
Dim myTables, myFigures as Variant
If ActiveDocument.Bookmarks.Count >= 1 Then
myFigures = ActiveDocument.GetCrossReferenceItems(Referencetype:="Figure")
' Test size...
LowerValFig = LBound(myFigures) 'Get the lower boundry number.
UpperValFig = UBound(myFigures) 'Get the upper boundry number
' Do something ....
For i = LBound(myFigures) To UBound(myFigures) ‘ should be 1…20, but is onlu 1…10
'Do something ....
Next i
End If
MsgBox ("Done ....")
End Sub*
Definitely something flaky with that. If I run the following code on a document that contains 32 Figure captions, the message boxes both display 32. However, if I uncomment the For Next loop, they only display 12 and the iteration ceases after the 12th item.
Dim i As Long
Dim myFigures As Variant
myFigures = ActiveDocument.GetCrossReferenceItems("Figure")
MsgBox myFigures(UBound(myFigures))
MsgBox UBound(myFigures)
'For i = 1 To UBound(myFigures)
' MsgBox myFigures(i)
'Next i
I had the same problem with my custom cross-refference dialog and solved it by invoking the dialog after each command ActiveDocument.GetCrossReferenceItems(YourCaptionName).
So you type:
varRefItemsFigure1 = ActiveDocument.GetCrossReferenceItems(g_strCaptionLabelFigure1)
For k = 1 To UBound(varRefItemsFigure1)
frmBwtRefDialog.ListBoxFigures.AddItem varRefItemsFigure1(k)
Next
and then:
frmBwtRefDialog.Show vbModeless
Thus the dialog invoked several times instead of one, but it works fast and don't do any trouble. I used this for one year and didn't see any errors.
Enjoy!
Frankly I feel bad about calling this an "answer", but here's what I did in the same situation. It would appear that entering the debugger and stepping through the GetCrossReferenceItems always returns the correct value. Inspired by this I tried various ways of giving control back to Word (DoEvents; running next segment using Application.OnTime) but to no avail. Eventually the only thing I found that worked was to invoke the debugger between assignments, so I have:
availRefs =
ActiveDocument.GetCrossReferenceItems(wdRefTypeNumberedItem):Stop
availTables =
ActiveDocument.GetCrossReferenceItems(wdCaptionTable):Stop
availFigures = ActiveDocument.GetCrossReferenceItems(wdCaptionFigure)
It's not pretty but, as I'm the only person who'll be running this, it kind of works for my purposes.