Excel+VBA: Userfrom string Deal(Order) Number parsing - vba

I am trying to make a full function report file which connects to old version access data base. I need to build a Userform where user can enter deal (order) number just like we can add pages in print dialog box.
So far I have found below question which has given me partial answer:
How to Parsing Full String and split into several String in Excel VBA?
I can use Split(string) method to get various deal numbers when separated by ",".
However, I also need From to TO deal number option as well.
so, if user enters on String1 = "10001 - 10050, 20111 , 20115"
then I need output as
Deal_Line.Deal_Number >= 10001 and Deal_Line.Deal_Number <= 10050
and Deal_Line.Deal_Number = 20111 and Deal_Line.Deal_Number = 20115.
I can refine SQL String for my requirement, I would like to know if there is a way to use two deliminator.

Expanding on #user3598756 answer, (which does exactly what you asked for) and interpreting your requirements I come up with the following:
Function ProcessString(strng1 As String, fieldname As String) As String
Dim sqlStrng As String
Dim strng As Variant, limits As Variant
For Each strng In Split(strng1, ",")
limits = Split(strng, "-")
If UBound(limits) = 0 Then
sqlStrng = sqlStrng & "[FIELD] = " & limits(0) & "|"
Else
sqlStrng = sqlStrng & "([FIELD] >= " & Trim(limits(0)) & " And [FIELD] <= " & Trim(limits(1)) & ")|"
End If
Next
ProcessString = Replace(WorksheetFunction.Trim(Join(Split(Left(sqlStrng, Len(sqlStrng) - 1), "|"), " Or ")), "[FIELD]", fieldname)
End Function
The test line would be:
MsgBox ProcessString("10001 - 10050, 20111 , 20115","Deal_Line.Deal_Number")
This will produce an output that I think is more likely to be what you actually want - using OR instead of AND for starters, adding in parentheses needed for the OR to work properly and allowing for multiple table/field names.
Then again, maybe I've misinterpreted your requirement - I just can't see what use having all AND would be one one field.

you could use this helper function:
Function ProcessString(strng1 As String) As String
Dim sqlStrng As String
Dim strng As Variant, limits As Variant
For Each strng In Split(strng1, ",")
limits = Split(strng, "-")
If UBound(limits) = 0 Then
sqlStrng = sqlStrng & "Deal_Line.Deal_Number = " & limits(0) & "|"
Else
sqlStrng = sqlStrng & "Deal_Line.Deal_Number >= " & limits(0) & " And Deal_Line.Deal_Number <= " & limits(1) & "|"
End If
Next
ProcessString = WorksheetFunction.Trim(Join(Split(Left(sqlStrng, Len(sqlStrng) - 1), "|"), " And "))
End Function
to be tested/expolited in your "main" code as:
Sub main()
MsgBox ProcessString("10001 - 10050, 20111 , 20115")
End Sub

Related

Modify matrix column spacing in equation through Word VBA

I would like to automate vertically stacked math problems (sums, products, etc.).
By using matrices I can align the numbers to the right so the digits align.
However, the column spacing default is too wide:
I can manually right click the matrix, select matrix spacing and set the minimum distance between columns to exactly 1, achieving my goal:
I cannot get the syntax of the matrix manipulation in VBA. The documentation seems very sparse (no examples). I tried recording a macro, but the right-click menu does not appear for the matrix in the equation when recording. I am not sure how to "set" the OMathMat object, since it is not a property of OMath.
I would settle for code that looped through all the equation objects in the document, all the matrix objects in those equations, and updated the OMathMat.ColSpacing property.
I tried something like:
For Each equation In ActiveDocument.OMaths
For Each Func In equation.Functions
Func.Mat.ColSpacing = 1
Next
Next
But the requested member (Mat) of the collection (Functions) did not exist. Also, there seems to be OMathFunction.Mat and OMathMat. I think I need the second option.
I agree that there isn't an obvious place to find documentation about accessing the OMath objects, so started trying to put something together.
But then you answered your own question - at this point it's probably more useful to publish what I already have, despite the fact that there are many unanswered questions.
So here are a few pieces of code that may help shed some light. They aren't well-tested.
I may well try to improve this question in future, but it will take time.
The first set of code should be able to deal with the situation where your Matrix objects could be anywhere in an OMath object.
The second set of code implements an "explorer" that reports the structure of the OMath objects in the main body of the document via Debug.Print statements.
Then there are a few more notes at the end.
Here's the code that should do what you needed, but slightly more generalised. You can copy the code into a single Module and run it.
' Keep some running totals
Dim OMathCount As Integer
Dim FunctionCount As Long
Dim MatCount As Long
Sub processOMaths()
Dim i As Long
FunctionCount = 0
MatCount = 0
' Just process the document body
With ActiveDocument
For i = 1 To .OMaths.Count
With .OMaths(i)
Call processOMathFunctions(.Functions)
End With
OMathCount = i
Next
End With
MsgBox "Processed " & CStr(OMathCount) & " Equation(s), " & _
CStr(FunctionCount) & " Function(s), " & _
CStr(MatCount) & " Matrix object(s)"
End Sub
Sub processOMathFunctions(oFunctions As OMathFunctions)
' There does not seem to be a way to return the entire collection of Functions
' in an OMath object. So it looks as if we have to recurse. But because the
' Object names for different Functions are different, we can't easily drill down
' to the next level using exactly the same code for multiple object types...
Dim i As Integer
For i = 1 To oFunctions.Count
Call processSingleOMathFunction(oFunctions, i)
Next
End Sub
Sub processSingleOMathFunction(oFunctions As OMathFunctions, index As Integer)
' ...so unless someone has a better idea, we'll just use a Select Case
' statement and deal with all the possible Function types
FunctionCount = FunctionCount + 1
With oFunctions(index)
Select Case .Type
Case WdOMathFunctionType.wdOMathFunctionAcc
Call processOMathFunctions(.Acc.E.Functions)
Case WdOMathFunctionType.wdOMathFunctionBar
Call processOMathFunctions(.Bar.E.Functions)
Case WdOMathFunctionType.wdOMathFunctionBorderBox
Call processOMathFunctions(.BorderBox.E.Functions)
Case WdOMathFunctionType.wdOMathFunctionBox
Call processOMathFunctions(.Box.E.Functions)
Case WdOMathFunctionType.wdOMathFunctionDelim
Dim delimCount As Integer
For delimCount = 1 To .Delim.E.Count
Call processOMathFunctions(.Delim.E(1).Functions)
Next
Case WdOMathFunctionType.wdOMathFunctionEqArray
Dim eqCount As Integer
For eqCount = 1 To .EqArray.E.Count
Call processOMathFunctions(.EqArray.E(eqCount).Functions)
Next
Case WdOMathFunctionType.wdOMathFunctionFrac
Call processOMathFunctions(.Frac.Num.Functions)
Call processOMathFunctions(.Frac.Den.Functions)
Case WdOMathFunctionType.wdOMathFunctionFunc
Call processOMathFunctions(.Func.E.Functions)
Call processOMathFunctions(.Func.FName.Functions)
Case WdOMathFunctionType.wdOMathFunctionGroupChar
Call processOMathFunctions(.GroupChar.E.Functions)
Case WdOMathFunctionType.wdOMathFunctionLimLow
Call processOMathFunctions(.LimLow.E.Functions)
Call processOMathFunctions(.LimLow.Lim.Functions)
Case WdOMathFunctionType.wdOMathFunctionLimUpp
Call processOMathFunctions(.LimUpp.E.Functions)
Call processOMathFunctions(.LimUpp.Lim.Functions)
Case WdOMathFunctionType.wdOMathFunctionLiteralText
' as far as I know, this cannot contain further Functions
' Do nothing.
Case WdOMathFunctionType.wdOMathFunctionMat
MatCount = MatCount + 1
Dim i As Integer
.Mat.ColGapRule = wdOMathSpacingExactly
' Hardcode this bit
.Mat.ColGap = 1 ' I think these are Twips, i.e. 1/20 pt
' We could iterate the columns and rows, but
' we'll iterate the Args instead.
For i = 1 To .Args.Count
Call processOMathFunctions(.Args(i).Functions)
Next
Case WdOMathFunctionType.wdOMathFunctionNary
Call processOMathFunctions(.Nary.Sub.Functions)
Call processOMathFunctions(.Nary.Sup.Functions)
Call processOMathFunctions(.Nary.E.Functions)
Case WdOMathFunctionType.wdOMathFunctionNormalText
' Used for 'Non-Math text'
' Do nothing
Case WdOMathFunctionType.wdOMathFunctionPhantom
Call processOMathFunctions(.Phantom.E.Functions)
Case WdOMathFunctionType.wdOMathFunctionRad
Call processOMathFunctions(.Rad.Deg.Functions)
Call processOMathFunctions(.Rad.E.Functions)
Case WdOMathFunctionType.wdOMathFunctionScrPre
Call processOMathFunctions(.ScrPre.Sub.Functions)
Call processOMathFunctions(.ScrPre.Sup.Functions)
Call processOMathFunctions(.ScrPre.E.Functions)
Case WdOMathFunctionType.wdOMathFunctionScrSub
Call processOMathFunctions(.ScrSub.E.Functions)
Call processOMathFunctions(.ScrSub.Sub.Functions)
Case WdOMathFunctionType.wdOMathFunctionScrSubSup
Call processOMathFunctions(.ScrSubSup.E.Functions)
Call processOMathFunctions(.ScrSubSup.Sub.Functions)
Call processOMathFunctions(.ScrSubSup.Sup.Functions)
Case WdOMathFunctionType.wdOMathFunctionScrSup
Call processOMathFunctions(.ScrSup.E.Functions)
Call processOMathFunctions(.ScrSup.Sup.Functions)
Case WdOMathFunctionType.wdOMathFunctionText
' Text - do nothing
Case Else
MsgBox "OMath Function type " & CStr(.Type) & " not recognized. Ignoring."
End Select
End With
End Sub
The second lot of code is the Explorer. It's incomplete, in various ways. All the code could go in a single Module but as it stands it is divided into three Modules:
One module contains the main Explorer code, which is structured in a similar way to the code I posted above. I haven't completed the code for all the function types so you will see some TBD (To Be Done) comments.
' indentation increment for each level of oMath object nesting
Const incindent As String = " "
Sub exploremath()
' This code explores the structure of 'modern' equations in Word
' i.e. the sort that have neen in Word since around Word 2007, not the older
' types inserted using an ActiveX object or an EQ field.
' Note to English speakers: some places use "Math" to refer to Mathematics
' e.g. the US. Others, e.g. the UK, use "Maths". This can cause a bit of confusion
' for UK English speakers but the trick is to realise that the oMaths object
' is just a collection of oMath objects. i.e. the naming convention is exactly the same as
' e.g. Paragraphs/Paragraph and so on.
' The overview is that
' - each Equation is represented by an OMath object
' - an oMath object contains an oMathFunctions collection
' with 0 (?1) or more oMathFunction objects
' - an oMathFunction object can represent several different
' types of structure, not just those with familiar function names
' such as Sin, Cos etc. but structures such as Matrices,
' Equation Arrays and so on.
Dim eqn As oMath
Dim fn As OMathFunction
Dim i As Long
Dim j As Long
Dim indent As String
With ActiveDocument
For i = 1 To .OMaths.Count
With .OMaths(i)
Debug.Print "Equation " & CStr(i) & ":-"
indent = ""
Call documentOMathFunctions(.Functions, indent)
End With
Debug.Print
Next
End With
End Sub
Sub documentOMathFunctions(fns As OMathFunctions, currentindent As String)
Dim i As Integer
Dim indent As String
indent = currentindent & incindent
Debug.Print indent & "Function count: " & CStr(fns.Count)
For i = 1 To fns.Count
Call documentOMathFunction(fns, i, indent)
Debug.Print
Next
End Sub
Sub documentOMathFunction(fns As OMathFunctions, index As Integer, currentindent As String)
Dim indent As String
indent = currentindent & incindent
With fns(index)
Debug.Print indent & "Function " & CStr(index) & ", Type: " & OMathFunctionTypeName(.Type) & " :-"
Select Case .Type
Case WdOMathFunctionType.wdOMathFunctionAcc
' Accented object
Debug.Print indent & "Accent: " & debugPrintString(ChrW(.Acc.Char))
Call documentOMathFunctions(.Acc.E.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionBar
' object with an overbar
Debug.Print indent & "Bar " & AB(.Bar.BarTop) & ":-"
Call documentOMathFunctions(.Bar.E.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionBorderBox
' TBD
Case WdOMathFunctionType.wdOMathFunctionBox
Debug.Print indent & "Box: IsDifferential? " & YN(.Box.Diff) & _
", Breaks Allowed? " & YN(Not .Box.NoBreak) & _
", TreatAsSingleOp? " & YN(.Box.OpEmu)
Call documentOMathFunctions(.Box.E.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionDelim
' Brackets etc.
Debug.Print indent & "Delim: BeginningChar: " & _
debugPrintString(ChrW(.Delim.BegChar)) & _
", EndChar: " & debugPrintString(ChrW(.Delim.EndChar)) & _
", SeparatorChar: " & debugPrintString(ChrW(.Delim.SepChar))
Debug.Print indent & incindent & "Grow? " & _
YN(.Delim.Grow) & ", LeftChar Hidden? " & _
YN(.Delim.NoLeftChar) & ", RightChar Hidden? " & _
YN(.Delim.NoRightChar) & ", Appearance: " & OMathShapeTypeName(.Delim.Shape)
Dim delimCount As Integer
For delimCount = 1 To .Delim.E.Count
Debug.Print indent & "Part " & CStr(delimCount) & ":-"
Call documentOMathFunctions(.Delim.E(1).Functions, indent)
Next
Case WdOMathFunctionType.wdOMathFunctionEqArray
' Array of aligned equations
Debug.Print indent & "Equation Array: Vertical Alignment : " & OMathVertAlignTypeName(.EqArray.Align) & _
", Expand to page column width? " & YN(.EqArray.MaxDist)
Debug.Print "Expand to object width? " & YN(.EqArray.ObjDist) & _
", Row Spacing Rule: " & oMathSpacingRuleName(.EqArray.RowSpacingRule);
If .EqArray.RowSpacingRule = WdOMathSpacingRule.wdOMathSpacingExactly Then
Debug.Print ", Row Spacing: " & CStr(.EqArray.RowSpacing) & " twips"
ElseIf .EqArray.RowSpacingRule = WdOMathSpacingRule.wdOMathSpacingMultiple Then
' Don't know what the .rowspacing Unit is in this case
Debug.Print ", Row Spacing: " & CStr(.EqArray.RowSpacing) & " half-lines";
End If
Debug.Print
Dim eqCount As Integer
For eqCount = 1 To .EqArray.E.Count
Debug.Print indent & "Equation " & CStr(eqCount) & ":-"
Call documentOMathFunctions(.EqArray.E(eqCount).Functions, indent)
Next
Case WdOMathFunctionType.wdOMathFunctionFrac
' Fraction
Debug.Print indent & "Fraction numerator:-"
Call documentOMathFunctions(.Frac.Num.Functions, indent)
Debug.Print indent & "Fraction denominator:-"
Call documentOMathFunctions(.Frac.Den.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionFunc
' Function (not sure yet whether a 'Func' can
' only have a single argument (possibly blank))
Debug.Print indent & "Func name: " & debugPrintString(.Func.FName.Range.Text)
Call documentOMathFunctions(.Func.E.Functions, indent)
Call documentOMathFunctions(.Func.FName.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionGroupChar
' A character such as a brace over or under another Function.
Debug.Print indent & "Group Char: " & UHex(.GroupChar.Char) & ", Position: " & AB(.GroupChar.CharTop); ""
Call documentOMathFunctions(.GroupChar.E.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionLimLow
' A Limit with the small text under the 'Lim word'
Debug.Print indent & "'LimLow':-"
Debug.Print indent & "Base:-"
Call documentOMathFunctions(.LimLow.E.Functions, indent)
Debug.Print indent & "Lim:-"
Call documentOMathFunctions(.LimLow.Lim.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionLimUpp
' A limit with the small text over the 'Lim word'
Debug.Print indent & "'LimUpp':-"
Debug.Print indent & "Base:-"
Call documentOMathFunctions(.LimUpp.E.Functions, indent)
Debug.Print indent & "Lim:-"
Call documentOMathFunctions(.LimUpp.Lim.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionLiteralText
' 'Literal Text' at first sight seems to be followed by
' a wdOMathFunctionText function with a Range
' containing the actual text.
' To be explored further
' But for now, do nothing.
Case WdOMathFunctionType.wdOMathFunctionMat
' A Matrix. AFAIK they have to be rectangular
Dim i As Integer
Debug.Print indent & ", Column count: " & CStr(.Mat.Cols.Count) & _
", Column gap rule: " & oMathSpacingRuleName(.Mat.ColGapRule);
If .Mat.ColGapRule = WdOMathSpacingRule.wdOMathSpacingExactly Then
Debug.Print ", Spacing: " & CStr(.Mat.ColGap) & " twips";
ElseIf .Mat.ColGapRule = WdOMathSpacingRule.wdOMathSpacingMultiple Then
Debug.Print ", Spacing: " & CStr(.Mat.ColGap);
End If
Debug.Print
Debug.Print indent & "Row count: " & CStr(.Mat.Rows.Count) & _
", Row gap rule: " & oMathSpacingRuleName(.Mat.RowSpacingRule);
If .Mat.RowSpacingRule = WdOMathSpacingRule.wdOMathSpacingExactly Then
Debug.Print ", Spacing: " & CStr(.Mat.RowSpacing) & " twips";
End If
Debug.Print
Debug.Print indent & "Args count: " & CStr(.Args.Count)
For i = 1 To .Args.Count
Debug.Print indent & " Arg " & CStr(i) & ":-"
Call documentOMathFunctions(.Args(i).Functions, indent)
Next
Case WdOMathFunctionType.wdOMathFunctionNary
' An N-Ary function, such as a summation operator, product operator
' various types of integral operator and so on.
' AFAICS all current N-Ary operators are in effect 3-Ary, i.e.
' The lower limit is the Sub, the upper limit is the Sup, and the
' thing being summed/integrated etc. is the 'Base'
' ignore .SubSupLim for now
.Nary.Char = &H2AFF
Debug.Print indent & "N-ary function, Type character: " & _
oMathNaryOpName(.Nary.Char) & ", Grow? " & YN(.Nary.Grow) & ":-"
Debug.Print indent & "N-ary Lower limit:- Hidden? " & YN(.Nary.HideSub)
Call documentOMathFunctions(.Nary.Sub.Functions, indent)
Debug.Print indent & "N-ary Upper limit:- Hidden? " & YN(.Nary.HideSup)
Call documentOMathFunctions(.Nary.Sup.Functions, indent)
Debug.Print indent & "N-ary body:-"
Call documentOMathFunctions(.Nary.E.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionNormalText
'Used for 'Non-Math text'
Debug.Print indent & "Literal Text: " & debugPrintString(.Range.Text)
Case WdOMathFunctionType.wdOMathFunctionPhantom
' TBD
Case WdOMathFunctionType.wdOMathFunctionRad
Debug.Print indent & "Degree:- (Hidden? " & YN(.Rad.HideDeg)
Call documentOMathFunctions(.Rad.Deg.Functions, indent)
Debug.Print indent & "Radical:-"
Call documentOMathFunctions(.Rad.E.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionScrPre
' base object with a superscript/subscript *before* the base
' Think this means an obect that has *both* (although one or both
' could be left blank)
' (TBR: Can OMath be used right-to-left, and if so, how
' are properties named/documented as 'to the left of',
' 'to the right of', 'before', 'after' to be interpreted?
' Or are math formulas etc. always expressed as LTR worldwide
' these days (I would guess so!)
Debug.Print indent & "ScrPre Subscript:-"
Call documentOMathFunctions(.ScrPre.Sub.Functions, indent)
Debug.Print indent & "ScrPre Superscript:-"
Call documentOMathFunctions(.ScrPre.Sup.Functions, indent)
Debug.Print indent & "ScrPre Base-"
Call documentOMathFunctions(.ScrPre.E.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionScrSub
' base object with a subscript after the base
Debug.Print indent & "Base:-"
Call documentOMathFunctions(.ScrSub.E.Functions, indent)
Debug.Print indent & "Superscript:-"
Call documentOMathFunctions(.ScrSub.Sub.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionScrSubSup
' base object with subscript and supersript after the base
Debug.Print indent & "ScrSubSup Base-"
Call documentOMathFunctions(.ScrSubSup.E.Functions, indent)
Debug.Print indent & "ScrSubSup Subscript:-"
Call documentOMathFunctions(.ScrSubSup.Sub.Functions, indent)
Debug.Print indent & "ScrSubSup Superscript:-"
Call documentOMathFunctions(.ScrSubSup.Sup.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionScrSup
' base object with supersript after the base
Debug.Print indent & "Base:-"
Call documentOMathFunctions(.ScrSup.E.Functions, indent)
Debug.Print indent & "Superscript:-"
Call documentOMathFunctions(.ScrSup.Sup.Functions, indent)
Case WdOMathFunctionType.wdOMathFunctionText
Debug.Print indent & "Text: " & debugPrintString(getRunTextFromXML(.Range))
Case Else
' we already printed an unknown type message before the select statement.
End Select
End With
End Sub
In Module "Common" there are some Helper routines
usly had Cstr
Function UHex(codepoint As Long) As String
' Form a 4-digit Unicode Hex string
' fix : had 'CStr' instead of 'Hex'
UHex = "U+" & Right("0000" & Hex(codepoint), 4)
End Function
Function debugPrintString(s As String) As String
' Form a string where 1-byte characters are output as is,
' others are output as Unicode Hex strings
' NB, at the moment we do not try to change stuff such as "&" to "&"
Dim i As Long
Dim t As String
t = ""
For i = 1 To Len(s)
If AscW(Mid(s, i, 1)) < 256 Then
t = t & Mid(s, i, 1)
Else
t = t & " " & UHex(AscW(Mid(s, i, 1))) & " "
End If
Next
debugPrintString = t
End Function
Function YN(b As Boolean) As String
If b Then YN = "Y" Else YN = "N"
End Function
Function AB(b As Boolean) As String
If b Then AB = "Above" Else AB = "Below"
End Function
Function getRunTextFromXML(r As Word.Range) As String
' We need a function like this to retrieve text in the Math Font, (e.g. Cambria Math Font,
' which appears to be encoded as ASCII rather than Unicode.
' So if the equation contains a Cambria Math "A", the .Range.Text is returned as "??"
' For later: if the text is *not* in Cambria Math, we probably *don't* want to do this!
' (Could end up inspecting character by character).
' For the moment, use a kludge to get the first run of text in the range.
Dim x As String
Dim i1 As Long
Dim i2 As Long
x = r.WordOpenXML
' FOr an oMath text, we look for m:t rather than w:t
i1 = InStr(1, x, "<m:t>")
i2 = InStr(i1, x, "</m:t>")
getRunTextFromXML = Mid(x, i1 + 5, i2 - i1 - 5)
End Function
In module Enums, there are some more Helper routines to return things such as Enum names as text (if only VBA had better facilities for Reflection!)
Function oMathIsAllowedNaryOp(codepoint As Long) As Boolean
' Perhaps can look up the unicode database rather than hardcode this list
Select Case codepoint
Case &H2140, &H220F To &H2211, &H222B To &H2233, &H22C0 To &H22C3, &H2A00 To &H2A06, &H2A09, &H2AFF
oMathIsAllowedNaryOp = True
Case Else
oMathIsAllowedNaryOp = False
End Select
End Function
Function oMathNaryOpName(codepoint As Long) As String
' Perhaps can look up the unicode database rather than hardcode this list
' and the standard Unicode character names
Select Case codepoint
Case &H2104
oMathNaryOpName = "Double-Struck N-Ary Summation"
Case &H220F
oMathNaryOpName = "N-Ary Product"
Case &H2210
oMathNaryOpName = "N-Ary Coproduct"
Case &H2211
oMathNaryOpName = "N-Ary Summation"
Case &H22C0
oMathNaryOpName = "N-Ary Logical And"
Case &H22C1
oMathNaryOpName = "N-Ary Logical Or"
Case &H22C2
oMathNaryOpName = "N-Ary Intersection"
Case &H22C3
oMathNaryOpName = "N-Ary Union"
Case &H22A0
oMathNaryOpName = "N-Ary Circled Dot Operator"
Case &H22A1
oMathNaryOpName = "N-Ary Circled Plus Operator"
Case &H22A2
oMathNaryOpName = "N-Ary Circled Times Operator"
Case &H22A3
oMathNaryOpName = "N-Ary Union Operator With Dot"
Case &H22A4
oMathNaryOpName = "N-Ary Union Operator With Plus"
Case &H22A5
oMathNaryOpName = "N-Ary Square Intersection Operator"
Case &H22A6
oMathNaryOpName = "N-Ary Square Union Operator"
Case &H22A9
oMathNaryOpName = "N-Ary Times Operator"
Case &H2AFF
oMathNaryOpName = "N-Ary White Vertical Bar"
Case Else
oMathNaryOpName = "(Possibly invalid N-ary opcode: " & UHex(codepoint) & ")"
End Select
End Function
Function OMathShapeTypeName(OMathShapeType As Integer) As String
Select Case OMathShapeType
Case WdOMathShapeType.wdOMathShapeCentered
OMathShapeTypeName = "wdOMathShapeCentered"
Case WdOMathShapeType.wdOMathShapeMatch
OMathShapeTypeName = "wdOMathShapeMatch"
Case Else
OMathShapeTypeName = "(Math Shape Type unknown: " & CStr(OMathShapeType) & ")"
End Select
End Function
Function oMathSpacingRuleName(oMathSpacingRule As Long) As String
Select Case oMathSpacingRule
Case WdOMathSpacingRule.wdOMathSpacing1pt5
oMathSpacingRuleName = "wdOMathSpacing1pt5"
Case WdOMathSpacingRule.wdOMathSpacingDouble
oMathSpacingRuleName = "wdOMathSpacingDouble"
Case WdOMathSpacingRule.wdOMathSpacingExactly
oMathSpacingRuleName = "wdOMathSpacingExactly"
Case WdOMathSpacingRule.wdOMathSpacingMultiple
oMathSpacingRuleName = "wdOMathSpacingMultiple"
Case WdOMathSpacingRule.wdOMathSpacingSingle
oMathSpacingRuleName = "wdOMathSpacingSingle"
Case Else
oMathSpacingRuleName = "(Math Spacing Rule unknown: " & CStr(oMathSpacingRule) & ")"
End Select
End Function
Function OMathVertAlignTypeName(OMathVertAlignType As Integer) As String
Select Case OMathVertAlignType
Case WdOMathVertAlignType.wdOMathVertAlignBottom
OMathVertAlignTypeName = "wdOMathVertAlignBottom"
Case WdOMathVertAlignType.wdOMathVertAlignCenter
OMathVertAlignTypeName = "wdOMathVertAlignCenter"
Case WdOMathVertAlignType.wdOMathVertAlignTop
OMathVertAlignTypeName = "wdOMathVertAlignTop"
Case Else
OMathVertAlignTypeName = "(Math Vertical Alignment Type unknown: " & CStr(OMathVertAlignType) & ")"
End Select
End Function
Notes.
AFAIK the author/designer of the OMath objects and User Interface
(and indeed other aspect of layout in Word) is Murray Sargent III.
His paper on UnicodeMath Describes how the system as a whole is
intended to use Build-Up. But take care, because not everything
mentioned in there is necessarily implemented in all versions of
OMath (which is used across a number of MS Office products). His
Math-in-Office blog can be quite enlightening too.
There are at least two versions of the OMath object documentation -
one for "VBA" and one for .NET. There are some differences (e.g. some
Properties and at least one Function Type enumeration name is missing
from the VBA version. The .NET version is near here and the VBA
version is near here.
At the moment, none of the code I've posted provides anything that would help you modify the Function structure of an Equation, e.g. insert a new function. That's mainly because I haven't got to grips with it yet. Even writing code to insert a piece of Text throws up a number of problems, not least the question of why Math Font text is not encoded as Unicode and what that means when it comes to modifying it. It may in fact be easier to work with the Linear ("not built up" text version rather than the Object model. TBD!
I solved my problem, looping through the equations (OMaths collection), and then, using the WdOMathFunctionType enumeration to find fraction type functions that contained a matrix in their numerator, I could properly set the matrix properties:
For Each eq In ActiveDocument.OMaths
For Each Func In eq.Functions
If Func.Type = 7 Then 'a fraction function
If Func.Args(1).Functions(1).Type = 12 Then 'a matrix function in the numerator
With Func.Args(1).Functions(1).Mat
.ColGapRule = wdOMathSpacingExactly
.ColGap = 1
.PlcHoldHidden = True
End With
End If
End If
Next
Next
I knew the type of structured equations my document contained, so I didn't include many check conditions. (There probably a more elegant and robust way to search all 'child functions' of equations until the last node is reached.) Hopefully this can serve as a template for anyone trying to expose specific OMath Function properties.

Compile Error At IfError in VBA Code of Nested WorksheetFunctions

So, I'm quite new to VBA and I'm trying to learn. I have several tables with fastener data that I'm trying to pull information from based on what's in the main input table. I have a formula that works, but as I add different fastener tables, the nested IF formula is getting unruly. I decided to try to convert the formula to VBA and I'm getting an error: "Compile error: Wrong number of arguments or invalid property assignment." It shows up at the IfError. I may also have called the table columns incorrectly. The formula that I am trying to convert is included below. I haven't yet determined how best to set up the If statement so that if the user selected IFF, it would switch to using the IFF table, but that's another post.
Dim tbl_Solid As ListObject
Dim Rep_Fast_Type
Dim Incoming_Dia
Dim BP_Max_Dia
Dim Test_Dia
Dim Rep_Fast
Dim Test_Value
Dim i
Set tbl_Solid = ThisWorkbook.Sheets("Fastener DB").ListObjects("tbl_Solid")
i = 1
Incoming_Dia = [tbl_Input].Cells(i, 2)
BP_Max_Dia = [tbl_Input].Cells(i, 3)
Rep_Fast_Type = [tbl_Input].Cells(i, 4)
If Incoming_Dia > BP_Max_Dia Then
Test_Dia = Incoming_Dia
Else
Test_Dia = BP_Max_Dia
End If
'Compile error at IfError in third line.
If Rep_Fast_Type = "Solid" Then
Rep_Fast = WorksheetFunction.Index([tbl_Solid].Range("Fastener"), _
WorksheetFunction.Aggregate(15, 6, WorksheetFunction.IfError( _
Rows([tbl_Solid].Range("Fastener")) / ((Test_Dia >= [tbl_Solid].Range("min")) * (Test_Dia <= [tbl_Solid].Range("max"))), _
Rows([tbl_Solid].Range("Fastener")) / (Test_Dia <= [tbl_Solid].Range("min")), 1)))
End If
'To test values
MsgBox "Incoming diameter is " & Incoming_Dia & vbCrLf & "B/P max diameter is " & BP_Max_Dia & vbCrLf & "Test diameter is " & Test_Dia & vbCrLf & "Repair fastener type is " & Rep_Fast_Type & vbCrLf & "Repair Fastener is " & Rep_Fast
Old Excel Formula:
=IF([#[rep type]]="Solid",INDEX(tbl_Solid[[#All],[Fastener]],AGGREGATE(15,6,IFERROR(ROW(tbl_Solid[Fastener])/(([#diameter]>=tbl_Solid[min])*([#diameter]<=tbl_Solid[max])),ROW(tbl_Solid[Fastener])/([#diameter]<=tbl_Solid[min])),1)),IF([#[rep type]]="IFF",INDEX(tbl_IFF[[#All],[Fastener]],AGGREGATE(15,6,IFERROR(ROW(tbl_IFF[Fastener])/(([#diameter]>=tbl_IFF[min])*([#diameter]<=tbl_IFF[max])),ROW(tbl_IFF[Fastener])/([#diameter]<=tbl_IFF[min])),1))))
Screenshot of Data
It was just a parenthesis wrongly placed:
replace this line:
Rows([tbl_Solid].Range("Fastener")) / (Test_Dia <= [tbl_Solid].Range("min")), 1)))
with for this:
Rows([tbl_Solid].Range("Fastener")) / (Test_Dia <= [tbl_Solid].Range("min"))), 1))
Nevertheless, I would suggest to use the With statement to wrap the formula, i.e.:
With WorksheetFunction
Rep_Fast = .Index([tbl_Solid].Range("Fastener"), _
.Aggregate(15, 6, .IfError( _
Rows([tbl_Solid].Range("Fastener")) / _
((Test_Dia >= [tbl_Solid].Range("min")) * (Test_Dia <= [tbl_Solid].Range("max"))), _
Rows([tbl_Solid].Range("Fastener")) / (Test_Dia <= [tbl_Solid].Range("min"))), 1))
End With
Note that this answer refers only to the syntax that gives the compile error, no test have been performed to the formula output.

How do I allow wildcard * in Excel VBA function that finds words in string?

I have the following function that finds words in a string, for instance searching for don will find Don and not don't which is what I want:
"I don't know Don, what do you think?"
I however also find I need to look for words like race, races, racing. I would like to be able to search for rac* to cover all such variants rather than searching for each.
Is it possible to update the code to do this? Or does someone have any code that can solved this problem?
Function InStrExact(Start As Long, SourceText As String, WordToFind As String, _
Optional CaseSensitive As Boolean = False)
Dim x As Long, Str1 As String, Str2 As String, Pattern As String
If CaseSensitive Then
Str1 = SourceText
Str2 = WordToFind
Pattern = "[!A-Za-z0-9]"
Else
Str1 = UCase(SourceText)
Str2 = UCase(WordToFind)
Pattern = "[!A-Z0-9]"
End If
For x = Start To Len(Str1) - Len(Str2) + 1
If Mid(" " & Str1 & " ", x, Len(Str2) + 2) Like Pattern & Str2 & Pattern _
And Not Mid(Str1, x) Like Str2 & "'[" & Mid(Pattern, 3) & "*" Then
InStrExact = x
Exit Function
End If
Next
End Function
A simple modification is to add a wildcard to the end of your search string and match against all remaining characters in the original string. The change is to replace this line:
If Mid(" " & Str1 & " ", x, Len(Str2) + 2) Like Pattern & Str2 & Pattern _
with this:
If Mid(" " & Str1 & " ", x) Like Pattern & Str2 & Pattern & "*" _
This simply removes the restriction on the number of characters to be matched. If a wildcard is added to the end of the search word, it comes before the trailing pattern and so allows any number of additional characters. If there is no wildcard in the search word, then the trailing pattern still needs to come immediately after the search word and hence still requires an exact match.
Note that there will be an issue if the word you're searching for is the last word AND you add a wildcard. The length of Str2 then causes the function to stop searching too soon. So the complete solution is to also replace this line:
For x = Start To Len(Str1) - Len(Str2) + 1
with this:
For x = Start To Len(Str1)
There's no need to stop checking any earlier.
I'd go like follows:
Function InStrExact(startPos As Long, sourceText As String, wordToFind As String, _
Optional CaseSensitive As Boolean = False) As Long
Dim x As Long
Dim actualSourceText As String, actualWordToFind As String, Pattern As String
Dim word As Variant
actualSourceText = Replace(Mid(sourceText, startPos), ",", "")
If CaseSensitive Then
Pattern = "[A-za-z]"
Else
actualSourceText = UCase(actualSourceText)
actualWordToFind = UCase(wordToFind)
Pattern = "[A-Z]"
End If
For Each word In Split(actualSourceText, " ")
If CStr(word) Like actualWordToFind & Pattern Or CStr(word) = actualWordToFind Then
InStrExact2 = x + 1
Exit Function
End If
x = x + Len(word) + 1
Next
InStrExact = -1 '<--| return -1 if no match
End Function

VBA ACCESS Comparing String as they are integer

I am trying to prompt the user to input a range and display all the instruments that are within that range in a subform.
Problem: The upper and lower range is a text field (because some of the range cannot be expressed in integer). As seen in the screenshot, the comparison only compare the first character of the field.
User's input: 5 - 3
On the subform: 36 - 4
It compares 5 and 3 instead of 36
I know vba is doing what it has been told but how can I achieve the result I want?
Here is my code for requering the subform:
Dim Up As Integer
Dim Low As Integer
If Me.Text_L = "" Or IsNull(Me.Text_L) Or Me.Text_U = "" Or IsNull(Me.Text_U) Then
MsgBox ("Please choose a valid range!")
Else
Up = Me.Text_U
Low = Me.Text_L
SQL = SQL_Origin & " WHERE [qry_View_Search].[Upper_Range] <= '" & Up & "' " _
& "AND [qry_View_Search].[Lower_Range] >= '" & Low & "';"
subform_View_Search.Form.RecordSource = SQL
subform_View_Search.Form.Requery
End If
so what i did is made a new column in the query for
IIf(IsNumeric([Upper]), Val([Upper]), Null)
to get all the numeric result.
Then in the vba, I re query the subform as below
SQL = SQL_Origin & " WHERE [qry_View_Search].[Upper] <= cint(Forms![frm_View_Search]![Text_U]) " _
& "AND [qry_View_Search].[Lower] >= cint(Forms![frm_View_Search]![Text_L]);"
Thanks #HansUp !
I have successfully for those cases used Val only:
Value: Val([FieldName])
or:
Value: Val(Nz([FieldName]))

If a listbox contains an item LIKE

Is there anywhere to check whether a listbox contains an item which is similar to a string?
I have tried to use a Like statement but this doesn't work.
I have tried:
For i As Integer = 0 To TopicListBox.Items.Count - 1
If (TopicListBox.Items(i).ToString.Contains(Record.Item("Keyphrase2"))) Then
Dim Item As String = TopicListBox.Items(i).ToString
If Item Like "Questions related to:" & Record.Item("Keyphrase2") & ": |*| Qs" Then
Dim ItemSplit() As String = Item.Split(New Char() {"|"c})
Dim AmountOfQuestionsAfter As Integer = AmountOfQuestions + CInt(ItemSplit(1))
Item = ItemSplit(0) & AmountOfQuestionsAfter & ItemSplit(1)
Else
TopicListBox.Items.Add("Questions related to:" & Record.Item("Keyphrase2") & ": |" & AmountOfQuestions & "| Qs")
Exit For
End If
End If
Next
I don't really understand what you are trying to accomplish, but here is a LINQ function to return all the items that contain a certain string.
Dim lb As New ListBox
lb.Items.Add("asdf")
lb.Items.Add("asdfasdf")
lb.Items.Add("aifisasdf")
lb.Items.Add("adf")
'' find all items in the ListBox that contain the string "asdf"
Dim found = lb.Items.Cast(Of String).Where(Function(f) f.Contains("asdf"))
For Each s In found
'' do something with those items
Next
Can't you just do
If Item.Contains("Questions related to:" & Record.Item("Keyphrase2")) Then
...
End If
or
If Item.StartsWith("Questions related to:" & Record.Item("Keyphrase2")) Then
...
End If
?
Dim Found = (From Result In lb.Items Where Result.ToString = "Value" Select Result)
If (Found.Count > 0) Then
' your code
End If