Use a cell value (text) as a part of a formula in Excel VBA - vba

I have a problem while doing a macro in Excel VBA which looks simple but I was not able to find an answer.
What I want is to change a formula depending on a value of a concrete cell; the cell is situated in C7 and can have the text OR or the text AND. The part of the formula is (being CritEUs and CritSKUs String variables):
If CritEUs = "NO" (OR/AND) CritSKUs = "NO" Then .... (whatever)
So I want to change the OR/AND depending on the value in C7, I tried to use INDIRECT but I think it works only with numbers, and also the following (being W the Worksheet variable):
Dim Pattern As String
Pattern = W.Range("C7").Value
If CritEUs = "NO" " & Pattern & " CritSKUs = "NO" Then
But the Excel don't accept me this option.
Could it be that this is not possible?
I would really appreciate any help!

I'd look to handle this in another if statement and then nest the next if statement within like so:
Sub Example()
Dim Pattern As String
Pattern = W.Range("C7").Value
If Pattern = "AND" Then
If CritEUs = "NO" And CritSKUs = "NO" Then
'Do Something'
End If
ElseIf Pattern = "OR" Then
If CritEUs = "NO" Or CritSKUs = "NO" Then
'Do Something'
End If
End If
End Sub

Even if I strongly prefer Gareth's solution, there is a trick for doing what you want (i.e. for evaluating the condition) through the usage of the Application.Evaluate() method. It would be:
If Application.Evaluate(Pattern & "(" & Chr(34) & CritEUs & Chr(34) & "=" & Chr(34) & "NO" & Chr(34) & "," & Chr(34) & CritSKUs & Chr(34) & "=" & Chr(34) & "NO" & Chr(34) & ")") Then
... where the string being an expression such as =AND(whatever = "NO", whateverelse = "NO") or =OR(whatever = "NO", whateverelse = "NO") (depending on the value of the variable Pattern) that can be evaluated by the MS Excel application no matter what the system language is.
But as I said, I would personally prefer a nested if block as Gareth suggested because it's clearer what you are doing and it cannot crash if the user inserts an invalid logic operator or makes just a spelling mistake; you should consider this option if you don't want / cannot slightly re-design your code.
FORMULA EXPLANATION - required from the asker
The Evaluate() is a method of the Application object, which means of the object MS Excel. This method is very straightforward:
input: string
output: evaluation of the string
It is in fact used to "evaluate" a string inserted by the user exactly as it does when you type a formula into a cell. If you type into a cell "=3+4", you are basically typing Application.Evaluate("3+4"). This will return you 7, because it's the result of the string evaluation you provided.
This built-in is very very powerful, because it uses a very consolidated system (the one of MS Excel) to parse and evaluate any string that Excel can evaluate. Moreover, the evaluation is always in English (you can use the English function IF but not the Italian SE, nor the German WENN or the French SI because the method evaluates as if your Excel was in English to be system independent.
On the other hand, the Chr(34) is just returning the character ". This character is hard to use in VBA because it's usually need to separate strings (e.g. a = "first" & "second". However, you need this character inside the string to be evaluated so I'm just calling it with Chr(34) to avoid confusion of the compiler.
SUMMARY:
The string is being built up like this:
Pattern & "(" & Chr(34) & CritEUs & Chr(34) & "=" & Chr(34) & "NO" & Chr(34) & "," & Chr(34) & CritSKUs & Chr(34) & "=" & Chr(34) & "NO" & Chr(34) & ")"
Being...
Pattern = AND or OR
Chr(34) = "
... the string that we are building will be of this kind (just a possible outcome):
"AND("NO"="NO","YES"="NO")"
So, once we have built-up this string, we pass it into the Evaluate method: it's like if we were writing =AND("NO"="NO","YES"="NO") into an Excel cell. What would the outcome be? Clearly it depends on your variable, but in this case it would be FALSE, so the If - Then block will not be entered because the return value is false. Otherwise, it would be entered.
This is not a "wrong" method, but as I was saying it has only two possible downsides:
1) It needs data validation, because if you pass crap into the variable Pattern the Evaluate method will fail; in Gareth's solution, instead, only AND and OR will be evaluated, otherwise the code will skip --> more stability;
2) It's not 100% intuitive: while Gareth's solution could be explained to a 10 year old child (because it's very very straight-forward to understand), this one needs (as we just did) a deeper analysis to understand properly what it does. I.E.: you need one line of code to write it, but you/someone else that will have to work on it in the future will need 5-10 minutes and a cup of coffee to understand what the statement wants to check.

Related

dynamic search box filters using LIKE

I have a dynamic search box filtering a subform based on user input. I also have a few filter buttons that filter the same subform. I set up the search box to incorporate preexisting filters applied by those buttons.
All that works fine. The problem I have is:
The dynamic filter using the LIKE statement only seems to work correctly when the length of the filter text is >= 3. Go below that and it applies some wonky filtering. Maybe I am using the statement wrong. I thought it would look if the field's value contains the search string somewhere in it. But it seemingly accepts values like "Natronlauge 50% techn. EN 896" for a search string of "Hä", which seems weird to me. It works once I add a third character though. Lines 11 and 13:
Me.Form.Filter = "[bezeichnung] LIKE '*" & SearchBoxStoffe & "*' AND [kategorie] = '" & Forms![HUB]![FilterAlleLink] & "'"
Would be nice if someone has some ideas how to go about these issues.
Here the full code for my searchbox:
Private Sub SearchBoxStoffe_KeyUp(KeyCode As Integer, Shift As Integer)
On Error GoTo errHandler
Dim filterText As String
'Apply dynamic filter for current filter category.
If Len(SearchBoxStoffe.Text) > 0 Then
filterText = SearchBoxStoffe.Text
If Forms![HUB]![FilterAlleLink] = "" Then
Me.Form.Filter = "[bezeichnung] LIKE '*" & SearchBoxStoffe & "*'"
Else
Me.Form.Filter = "[bezeichnung] LIKE '*" & SearchBoxStoffe & "*' AND [kategorie] = '" & Forms![HUB]![FilterAlleLink] & "'"
End If
Me.FilterOn = True
'Retain filter text in search box after refreshing.
SearchBoxStoffe.Text = filterText
SearchBoxStoffe.SelStart = Len(SearchBoxStoffe.Text)
Else
'Revert to current main filter category.
If Forms![HUB]![FilterAlleLink] <> "" Then
Call FilterStoffe("[kategorie] = '" & Forms![HUB]![FilterAlleLink] & "'")
Else
If Forms![HUB]![FilterAlleLink] = "" Then
Me.Filter = ""
Me.FilterOn = False
End If
End If
End If
'Set focus back to search box
SearchBoxStoffe.SetFocus
Exit Sub
errHandler:
MsgBox Err.Number & " - " & Err.Description, vbOKOnly, "Error ..."
End Sub
The dynamic filter using the LIKE statement only seems to work
correctly when the length of the filter text is >= 3. Go below that
and it applies some wonky filtering.
I don't really know why this is happening, but try the below. It makes use of the Change() event and covers 4 scenarios:
Both filters applicable.
Search Box only.
Main category only.
Nothing (clear the filter).
Also, I don't know what the FilterStoffe() method does, but I assume it just applies the main filter only.
Private Sub SearchBoxStoffe_Change()
On Error GoTo Trap
Select Case True
'both filters
Case Len(SearchBoxStoffe.Text) > 0 And Not IsNull(Forms.HUB.FilterAlleLink.Value):
Form.Filter = "[bezeichnung] LIKE '*" & SearchBoxStoffe.Text & "*' AND [kategorie] = '" & Forms.HUB.FilterAlleLink.Value & "'"
FilterOn = True
'SearchBox only
Case Len(SearchBoxStoffe.Text) > 0:
Form.Filter = "[bezeichnung] LIKE '*" & SearchBoxStoffe.Text & "*'"
FilterOn = True
'FilterAlleLink only
Case Not IsNull(Forms.HUB.FilterAlleLink.Value):
Form.Filter = "[kategorie] = '" & Forms.HUB.FilterAlleLink.Value & "'"
FilterOn = True
'Nothing
Case Else:
FilterOn = False
Filter = vbNullString
End Select
Leave:
On Error Resume Next
SearchBoxStoffe.SetFocus
Exit Sub
Trap:
MsgBox Err.Number & " - " & Err.Description, vbOKOnly, "Error ..."
Resume Leave
End Sub
Keep in mind, within the Change() event, the Text property gets updated with every keystroke and when the control loses the focus, it gets copied to the Value property.
However, the Value is the default property when you just reference the control.
So this
Me.SearchBoxStoffe
is the same as this:
Me.SearchBoxStoffe.Value
There were 2 issues that prevented the searchbox from running as intended:
Object references in the project were created with 2 different language versions of access. Objects would call other objects using the formulation of one language, which in turn called objects using referencing in another language etc. In cases where fields and/or queries would return empty, this would cause some of the references to no longer function as intended. The result was the program running out of stack space, empty controls on subforms that returned empty queries, objects not being found and more.
The searchbox filter was lagging behind the text in the searchbox by one event. If entering a new search string, the applied filter would always be missing the last character when using SearchBoxStoffe in the filter statement. Entering "Wood" would cause the filter to apply "Woo" etc.
The solutions are the following:
Fix all the references in the file manually to either language version and do not mix them up going forward.
The Value of the search box SearchBoxStoffe is not yet updated on either the KeyUp or the Change event when entering a new character. This can be fixed by substituting the Text value instead, which is updated already. Simply change line 11 to Me.Form.Filter = "[bezeichnung] LIKE '*" & SearchBoxStoffe.Text & "*'" and line 13 to Me.Form.Filter = "[bezeichnung] LIKE '*" & SearchBoxStoffe.Text & "*' AND [kategorie] = '" & Forms![HUB]![FilterAlleLink] & "'". The info originally came from #KostasK in his solution:
Keep in mind, within the Change() event, the Text property gets updated with every keystroke and when the control loses the focus, it gets copied to the Value property.
Which works too btw, just wasn't able to be verified since issue 1 prevented the code from running correctly. Answer by Kostas K.

Copying and Modyfing the Connection with VBA

I'm currently building a solution using VBA to copy my existing query (API query) and change its parameters depending on the values in columns from the original query.
I'm using the p1 variable that contains a looped column values to be used in API URL
The result should be a multiple sheets with one query each, vba code should loop through a certain column in my first query and then pass this values into each new query.
I encountered a problem. I don't know if its regional formatting issue or not, but i cannot really paste the query into Queries.Add method. I was pretty sure i formatted the "" correctly but:
AS you can see editor flashed this part of code in red (in Notepadd++ with VBA formatting its fine)
Here is my code:
mFormula =
"let" & Chr(13) & "" & Chr(10) & " Source = Json.Document(Web.Contents(""https://rejestr.io/api/v1/krs/"" & p1 & ""/relations"", [Headers=[Authorization=""xxxxxxx""]]))," & Chr(13) & "" & Chr(10) &" #""Converted to Table"" = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error), " & Chr(13) & "" & Chr(10) & " #""Expanded Column1"" = Table.ExpandRecordColumn(#""Con" & _
"verted to Table"", ""Column1"", {""address"", ""business_insert_date"", ""ceo"", ""current_relations_count"", ""data_fetched_at"", ""first_entry_date"", ""historical_relations_count"", ""id"", ""is_opp"", ""is_removed"", ""krs"", ""last_entry_date"", ""last_entry_no"", ""last_state_entry_date"", ""last_state_entry_no"", ""legal_form"", ""name"", ""name_short"", ""nip"", ""regon"", ""type"", ""w_likwidacji"", ""w_upadlo" & _
"sci"", ""w_zawieszeniu"", ""relations"", ""birthday"", ""first_name"", ""krs_person_id"", ""last_name"", ""organizations_count"", ""second_names"", ""sex""}, {""Column1.address"", ""Column1.business_insert_date"", ""Column1.ceo"", ""Column1.current_relations_count"", ""Column1.data_fetched_at"", ""Column1.first_entry_date"", ""Column1.historical_relations_count"", ""Column1.id"", ""Column1.is_opp"", ""Column1.is_rem" & _
"oved"", ""Column1.krs"", ""Column1.last_entry_date"", ""Column1.last_entry_no"", ""Column1.last_state_entry_date"", ""Column1.last_state_entry_no"", ""Column1.legal_form"", ""Column1.name"", ""Column1.name_short"", ""Column1.nip"", ""Column1.regon"", ""Column1.type"", ""Column1.w_likwidacji"", ""Column1.w_upadlosci"", ""Column1.w_zawieszeniu"", ""Column1.relations"", ""Column1.birthday"", ""Column1.first_name"", ""Column1.krs_person_id"", ""Column1.last_name"", ""Column1.organizations_count"", ""Column1.second_names"", ""Column1.sex""})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & "" & Chr(10) & " #""Renamed Columns3"""
Also, is it entirely possible to dynamically pass multiple variables from first query to create multiple queries to API service? Maybe someone already did something similar?
Many thanks
Alex
You need a line continuation marker (_) in the mFormula = line:
mFormula = _

Global Variable Lookup

Hopefully, I'll make this question as precise and understandable as possible - but you'll tell me if i don't ! Thanks in advance.
Firstly, a little background and what I've found that DOES work, then on to a small change that I cannot get to work. Rather than use the whole code, I've used snippets that should give enough information for you to understand that I have initiated things correctly first.
I use a menu system (a turbo-charged version of the original MS one) that has additional fields to store information needed to make changes depending upon what wording the user wants to use, so I may name a field Product, whereas a user may want to call it Goods or Items or Stuff or whatever he desires! So I store the user preferences in a separate table (we'll call that tblWORDS). When the menu is populated (remember it operates in a similar fashion to the standard SwitchBoard) with the data from fields: ItemText, Command & Argument the menu normally displays the text from ItemText, which I use. But, I have added a NEW field called CAPTION in SWITCHBOARD table because the VBA code does not allow for formatting the labels as I want them. So, when the VBA code reads ItemText for the label, from the recordset, and it encounters a | (pipe), my added VBA code then looks to the field Caption for a string. Hopefully, enough background info!!??
[SwitchBoard].[Caption] originally contained the following: (EVERYTHING INCLUDED)
"" & dlookup("fldProduct","tblWORDS") & ""
This worked perfectly!! But...
Instead of performing a lookup every time I need the WORD, I decided to create GLOBAL VARIABLES, so I have a Global Variable of glProduct, which obtains the word from the tblWORDS table correctly and retains that just fine. This is then available throughout the session anywhere.
I have substituted the string above to read the Global Variable instead of performing a lookup each time, to: (again, EVERYTHING INCLUDED)
"" & glProduct & ""
So, my code is as follows:
While (Not (rs.EOF))
Me("Option" & rs![ItemNumber]).Visible = True
'MY PIPE DEVIATION
If Left(Trim(rs![ItemText] & ""), 1) = "|" Then
'THIS LINE WORKS JUST FINE
'DISPLAYS CORRECTLY (rs!Caption = "" & dlookup("fldProduct","tblWORDS") & "")
szTemp = DLookup(rs![Caption], "tblWORDS")
'AS DOES THIS (but, I'm explicitly naming the variable in code! Only entered this line to show that the variable is working!)
szTemp = "" & glbProduct & ""
'THIS DOES TOO (Just a BYREF function for testing)
szTemp = fnGetValue(DLookup(rs![Caption], "tblWORDS"))
Me("Option" & rs![ItemNumber]).Caption = szTemp
Else
If rs![ItemNumber] = 0 Then
Me("OptionLabel" & rs![ItemNumber]).Caption = VBA.Trim(rs![ItemText] & " (" & rs![SwitchboardID] & ")")
Me("OptionLabel" & rs![ItemNumber]).Visible = True
Me("Option" & rs![ItemNumber]).Visible = False
Else
Me("Option" & rs![ItemNumber]).Caption = VBA.Trim(rs![ItemText] & "")
End If
End If
rs.MoveNext
Wend
REVISED TO:
While (Not (rs.EOF))
Me("Option" & rs![ItemNumber]).Visible = True
'MY PIPE DEVIATION
If Left(Trim(rs![ItemText] & ""), 1) = "|" Then
'THIS WORKS (but, I'm explicitly naming the variable in code! Only entered this line to show that the variable is working!)
szTemp = "" & glbProduct & ""
'THIS DOES NOT (FYI: rs!Caption = "" & glbProduct & "")
szTemp = rs!Caption
'NOR DOES THIS (Just a BYREF function for testing)
szTemp = fnGetValue(rs!Caption)
Me("Option" & rs![ItemNumber]).Caption = szTemp
Else
If rs![ItemNumber] = 0 Then
Me("OptionLabel" & rs![ItemNumber]).Caption = VBA.Trim(rs![ItemText] & " (" & rs![SwitchboardID] & ")")
Me("OptionLabel" & rs![ItemNumber]).Visible = True
Me("Option" & rs![ItemNumber]).Visible = False
Else
Me("Option" & rs![ItemNumber]).Caption = VBA.Trim(rs![ItemText] & "")
End If
End If
rs.MoveNext
Wend
What IS displayed is the literal string as entered ("" _glProduct "") not Product as it was prior to my revision!
So... the first problem is that you're confusing everybody with what you're doing it and where it's happening - where did rs![Caption] come from???
The second problem is you can't access global variables in your form or control properties.
You need to set the caption via code. I'm going to make some big guesses as to what you're using and what each element is, so let's start here:
First
Replace the Caption property with something else - just "Caption" for example.
Second
Add this code after your loop - I think that will work - hard to really know because you don't show where this code is and when it runs
[SwitchBoard].Caption = "" & glProduct & ""

vbmsgBox prompt is a mix of RTL(Persian) and LTR(English) but text is displayed in wrong order

I am designing a vba userform for excel 2013, in which I use English and Persian text, the problem is when I use a mix of both languages in a msgBox the bits of text come out in the wrong order, the number is supposed to be displayed at the end. here is the code:
Private Sub CommandButton1_Click()
MsgBox "نام" & " - " & "نام" & " - " & "VF" & " - " & 52 & " ." _
, 1 + 1048576 + 524288, "نام برگه"
End Sub
The parts in double quotes and the number are supposed to come from listBoxes.(I just replaced them here with examples, the code behaves the same)
I tried spacing the bits out(works in windows), rearranging the bits and changing msgBox's alignment and reading order, but the result was the same. How to fix this thing?
Could you put all the persian into a string. Have the message box output the string and then have the message box output the numbers?
So it would be something like
aString = ListBox1.value & "-" & listbox2.Value ....
msgbox aString & 52 etc...

Quotation marks in VBA

In my current VBA code, I have a query in which I am using Chr(34) to put quotation marks between some of my variables.
I was wondering what alternatives exist to this. This is a simple question, i know, but I haven't had any success with repeating quotation marks like this
" & variable string here & "
My code is messy for one and not understandable for people who are not familiar with VBA:
comboService = Chr(34) & Me.Combo8.Value & Chr(34)
Also, this hasn't worked:
comboService = """" & Me.Combo8.Value & """"
Can you perhaps tell me why?
Thanks in advance.
This:
comboService = """ & Me.Combo8.Value & """
is what you posted, but you need to add an extra quotation mark in order to add a literal quotation mark:
comboService = """" & Me.Combo8.Value & """"
Double-quotes within a string are what you are looking for.
aVar = "This: "" is a literal quotation mark"
I took a page from MS (the old vbCRLF) a while back, and just define any "tricky" characters I'll need as a string at the top of my code ...
Dim vbDblQuote As String
vbDblQuote = Chr(34)
Now you can just use that pseudo-constant as you build strings ...
strMyString = "Just another string " & vbDblQuote & "with quotes" & vbDblQuote & "!"
This makes code more readable, and also helps avoid "miscounted quote errors"
I have found the same behavior if I output to a file using the Write command. Any double quotes get repeated.
However, if you construct the string and then output to file using the Print command, this does not happen and all works as expected.