Access VBA-Loop through sequential textfields and do if not empty - vba

I need some help to optimize an access form for data entry. On my form I have many different fields amongst them are 7 text fields. I would like to loop through 6 of these which I have named sequentially and do something if they are not empty. I am using access VBA
Tried to show in pseudo code:
for i = 1 to 6
if hardness_measurement_i > 0 then
<do something>
else
<do nothing>
end if
next i
My problem is formulating this part of the loop: "hardness_measurement_i"
Is there a good way to loop through form fields named sequentially?

Try to get the contol by name using the Controls collection property of the form.
If Me.Controls("hardness_measurement_" & i).Value > 0 Then
'...
To check if the textbox is empty, you can call the IsNull() method passing the control's Value.
If IsNull(Me.Controls("hardness_measurement_" & i).Value) Then
'...

Related

VBA for Each loop

Hi I do have a problem with for each elements in the range. I would like to use for each loop and for each element of that collection certain action must be taken meaning another sub procedure, everything works fine until first time. For the second element macro lives the loop does not continue what is wrong here HOW SHOULD I AVOID THIS?
Maybe is some better way to do some tasks for each elements in particular range.
In my case I do have 8 such ranges each consists of 20 elements.
NOTE if Im not calling Hiding_and_Creating_Dump(Celly) inside loop everything works fine.
PLEASE ADVICE
For Each Items In ThisWorkbook.Sheets("BU_SELECTOR").Range("A47:H47")
Debug.Print Items
Select Case Items.Value
Case Is = "ASCButton"
If ASCButton = True Then
For Each Celly In Workbooks(XLSname).Sheets("lookups").Range(Range_ASC)
Debug.Print Celly
CommSheetName = Celly.Value
Call Hiding_and_Creating_Dump(Celly)
Next Celly
End If
Case Is = "ICOButton"
enter image description here

Saving Custom Document Properties in a Loop

I'm trying to save the values of data that have been input into my form. There are a total of about 50 different fields to save across 5 different agents, so I loaded the data into arrays.
I've tried saving the fields in a loop, but it doesn't seem to work in a loop, only if each field has a separate line, which is a lot of code and messy. The Ag1Name, Ag2Name and Ag3Name are the names of my textboxes that the user enters to populate the form.
Sub LoadAndSaveData()
NumberofAgents = 3
Dim AgentName(3) as String
AgentName(1) = Ag1Name.Value
AgentName(2) = Ag2Name.Value
AgentName(3) = Ag3Name.Value
For Count = 1 To NumberOfAgents
With ActiveDocument.CustomDocumentProperties
.Add Name:="AgentName" & Count, LinkToContent:=False, Value:=AgentName(Count), Type:=msoPropertyTypeString
End With
Next Count
End Sub
The data doesn't get saved to the Custom Document Properties when the code is set up in a loop like the above. Since there are so many values to save and all the data is already in arrays, I would much prefer to use a loop rather than write out a separate line of code for all ~50 of the values. It does seem to work when each field is saved in a separate line of code.
I think this would probably get what you want. You don't really need to count the document properties first, only increment with the ones you want to update. Hopefully the only document properties you want contain the name AgentName in it.
ReDim AgentName(0) As String
Dim P As Long
For Each c In ThisDocument.CustomDocumentProperties
If InStr(1, c.Name, "AgentName", vbTextCompare) > 0 Then
ReDim Preserve AgentName(P)
AgentName(P) = c.Value
P = P + 1
End If
Next c
As a guest I cannot post a comment here, but the code you gave works OK here.
However, there is a problem with creating legacy custom document properties programmatically, because doing that does not mark the document as "changed". When you close the document, Word does not necessarily save it and you lose the Properties and their values.
However, if you actually open up the Custom Document Property dialog, Word does then mark the document as "changed" and the Properties are saved.
So it is possible that the difference between your two scenarios is not the code, but that in one scenario you have actually opened the dialog box to check the values before closing the document and in the other you have not.
If that is the case, here, I was able to change this behaviour by adding the line
ActiveDocument.Saved = False
after setting the property values.
If you do not actually need the values to be Document Properties, it might be better either to use Document Variables, which are slightly easier to use since you can add them and modify them with exactly the same code, or perhaps by storing them in A Custom XML Part, which is harder work but can be useful if you need to extract the values somewhere where Word is not available.
You can make this even easier by looping the controls on the UserForm, testing whether the control name contains "Ag" and, if it does, create the Custom Document Property with the control's value - all in one step.
For example, the following code sample loops the controls in the UserForm. It tests whether the controls Name starts with "Ag". If it does, the CustomDocumentProperty is added with that control's value.
Sub LoadAndSaveData()
Dim ctl As MSForms.control
Dim controlName As String
For Each ctl In Me.Controls
controlName = ctl.Name
If Left(controlName, 2) = "Ag" Then
With ActiveDocument.CustomDocumentProperties
.Add Name:=controlName, LinkToContent:=False, value:=ctl.value, Type:=msoPropertyTypeString
End With
End If
Next
End Sub
I feel a little stupid... I just realized that the reason that the code wasn't working was that the variable NumberofAgents was not being calculated correctly elsewhere in my code. I've got it working now. Thanks for your thoughts!

Protect custom properties in word

We have several templates in Word2016 which uses custom variables, these variables should be updated with my macro, so that data gets pushed to the Database when the user changes them.
Unfortunately we have some users that deletes the variables in the documents(in the text not in the file properties) which means that the database does not get updated. Is there a setting to make custom properties not able to be deleted from the text?
Properties for one example document are listed below
This is how it should look like in the document
Then this is what happens sometimes, which should not be allowed
By doing so I do not need to loop through document to find variables, because I can simply loop through custom properties:
Public Sub initializeVariablesFromDB(doc As Document, dokID As String)
Dim docProp As Object
Dim rowNumber As Integer
'Get valid properties
If CPearson.IsArrayEmpty(Settings.validPropertiesArray) Then
Settings.validPropertiesArray = Post.helpRequest("xxxxxx?Dok2=1")
End If
'We create the docid just in case....
Call CustomProperties.createCustomDocumentProperty(doc, "_DocumentID", dokID)
'We loop through all custom properties
For Each docProp In doc.CustomDocumentProperties
rowNumber = CPearson.findRownumber(docProp.name, Settings.validPropertiesArray)
If rowNumber <> -1 Then
'we clear all SIGN properties...
If InStr(UCase(docProp.name), "SIGN") > 0 Then
docProp.value = ""
End If
'We check if we should use value from DB
If Settings.validPropertiesArray(rowNumber, 1) = "0" Or InStr(UCase(docProp.name), "DOCUMENTCREATEDDATE") > 0 _
Or InStr(UCase(docProp.name), "DOCUMENTCREATOR") > 0 Or InStr(UCase(docProp.name), "DOCUMENTNAME") > 0 Then
'Update from DB
Call Post.updateDBFromCustomProperties(docId:=dokID, PropertyName:=docProp.name, whoRules:="dBrules", doc:=doc)
End If
End If
Next docProp
End Sub
This will fail for the _DocumentSubject in the text, the new value to the property will be collected but is not reflected in the text anymore because the user deleted the variable in the text, can I prevent this?
You can put the field into a Content Control and lock the content control. Users who know enough about MS Word can still delete the content control but they would need to take some fairly deliberate steps to do so (and I'm guessing those people are not the problem).
Normal fields, including document properties, can be added to content controls (probably best to use rich text).
You can put the field into a Content Control, seems like you can not do that :( – skatun 10 hours ago
You can totally do it, follow these steps (Word 2010):
Add a custom property to a new blank (even unsaved) document _SlowLearner
Add a RichText content control from the DeveloperTab
Click inside the content control
Insert > Quick Parts > Field
Filter fields Document Information > select DocProperty
Select property: _SlowLearner > OK
Note: the field is protected when the Content Control is fully locked. This has the curious side effect that the Content Control does NOT allow the field to update normally... to get around this you need to add some more code to your VBA, like this:
Sub UpdateProtectedField()
With ActiveDocument.ContentControls(1)
.LockContents = False
.Range.Fields(1).Update
.LockContents = True
End With
End Sub
One possibility is to use continuous sections breaks and protect sections, not an ideal solution because then images can not be formatted and so on..

Is there a way I can assign every single textbox in a form to a single variable?

txtRound.Text
txtDaysWorked.Text
txtPerGallon.Text
txtGasCost.Text
txtMaint.Text
txtParking.Text
txtInsure.Text
I have all these textboxes in a vb form and instead of checking every single one to make sure they are all positive numbers I want a more efficient way of checking them with one variable. Once that variable is declared I'll just go like this
If (dblVariable > 0) Then
A lot easier than a long list of checking
Your form has a Controls properties which is a list of all controls on that form.
You can loop through each control and check it is a TextBox, and if so set it's Text property.
If LINQ is available you could simplify this to
For Each control In myForm.Controls.OfType<TextBox>()
control.Text = "My Value"
Next
Re-read the question, the title is a bit misleading, but I get what you're asking now.
Again, assuming LINQ is available you should use the .All extension.
If Me.Controls.OfType(Of TextBox)().All(Function(x) Convert.ToDouble(x.Text) > 0) Then
' Do something
End If
This is going to be a vague answer, because I'm working from memory, but here's my best attempt.
In short, I don't think so. If you want to check one variable you're going to have to generate that variable yourself. You could do this client-side.
Alternatively, if all of these controls are within a single form or something, you might be able to cycle through each control in a loop to check each one.
You could have a function which takes the list of TextBoxes:
Private Function CheckValues(txt As TextBox()) As Boolean
For Each member In txt
If not String.IsNullOrWhiteSpace(member.Text) Then
Dim number As Integer = CInt(member.Text)
If number < 0 Then
Return False
Next
Return true 'all positives
End Function
And then call it like this:
If CheckValues(new TextBox(){txtRound, txtDaysWorked}) Then
'case true
Else
'case false
End If

word vba macro - dynamically reference form elements

I have a userform that I have created that contains a number of TextBoxes. Each of these fields are named name1, name2, name3, etc.
This userform is called from a Word macro used to process data. Depending on the data in the file the macro is run on, I want content from an array to be displayed in each of those TextBoxes.
What I want to be able to do is dynamically reference the form TextBoxes so that I can populate them from within a loop, as opposed to having to do a separate if statement for each individual value/TextBox.
So for example, instead of having to do something like this:
For p = 1 To count
if p=1 then
dForm.name1.Text=myVar(p)
end if
if p=2 then
dForm.name2.Text=myVar(p)
end if
.
.
.
etc.
Next p
I want to be able to something much more simple and efficient like:
For p = 1 To count
tempString = "name" & p
dForm.tempString.Text = myVar(p)
Next p
Unforunately though, I cannot figure out how to do this.
Is this possible? I had been hoping that something similar to what can be done in Actionscript would work, but it didn't (in Actionscript I would simply just do dForm["name"+p].Text = myVar[p]).
Any ideas/suggestions? Nothing I've tried has worked, and I haven't been able to find anything online about this. I'm sure there has got to be some work around to avoid having to do an incredible number of if statements (the 'name' TextBoxes' is just one of many repeating TextBoxes that is part of my form, so having to do if statements for all of them would take forever) ...
UserForm1.Controls("name" & p).Value = myVar(p)
or...
Private Sub FillTextBoxes()
Dim ctl As Control
For Each ctl In UserForm1.Controls
If TypeName(ctl) = "TextBox" Then
If ctl.Name Like "name*" Then
ctl.Value = myVar(p)
End If
End If
Next
End Sub