I'm using the following code alot in my project:
txtVoornaam.Locked = False
txtVoornaam.BorderStyle = 4
txtVoornaam.BorderColor = RGB(255, 165, 0
txtAchternaam.Locked = False
txtAchternaam.BorderStyle = 4
txtAchternaam.BorderColor = RGB(255, 165, 0)
txtAfdeling.Locked = False
txtAfdeling.BorderStyle = 4
txtAfdeling.BorderColor = RGB(255, 165, 0)
I wonder if there is a way to not display this in my code or shorten this. The code gets very long if i use this a few times..
Whenever you need to repeat a set of instructions, instead of copy+pasta'ing code your first reaction should be to ask yourself "how can I avoid copying this chunk over and over?" - and the solution is pretty much always to extract a method and parameterize it.
So you take one of the repeated chunks:
txtAchternaam.Locked = False
txtAchternaam.BorderStyle = 4
txtAchternaam.BorderColor = RGB(255, 165, 0)
and then copy it one last time in a new scope:
Private Sub RenameMe()
txtAchternaam.Locked = False
txtAchternaam.BorderStyle = 4
txtAchternaam.BorderColor = RGB(255, 165, 0)
End Sub
Then you extract the parameters:
Private Sub RenameMe(ByVal target As Control)
target.Locked = False
target.BorderStyle = 4
target.BorderColor = RGB(255, 165, 0)
End Sub
Then you replace the repeated chunks with calls to that new procedure:
RenameMe txtVoornaam
RenameMe txtAchternaam
RenameMe txtAfdeling
Or if that's still tedious you can iterate controls and call that procedure in the loop body - whatever works best for you.
And if you need more flexibility, extract more parameters and make them Optional as needed:
Private Sub RenameMe(ByVal target As Control, Optional ByVal lockCtrl As Boolean = False, Optional ByVal brdrStyle As Long = 4, Optional ByVal brdrColor As Long = 42495)
target.Locked = lockCtrl
target.BorderStyle = brdrStyle
target.BorderColor = brdrColor
End Sub
Now the hard part is to give RenameMe a meaningful name that properly conveys what's going on here. I'd suggest FormatControl or something along these lines.
An option if you have several controls that you are creating through a form would be to do the following:
Dim names() As String
names = Split("txtVoornaam,txtAchternaam,txtAfdeling", ",")
Dim ctrl As Variant
Dim ctrlName As Variant
For Each ctrl In Me.Controls
For Each ctrlName In names
If StrComp(ctrlName, ctrl.Name) = 0 Then
ctrl.Locked = False
ctrl.BorderStyle = 4
ctrl.BorderColor = RGB(255, 165, 0)
End If
Next ctrlName
Next ctrl
This code iterates through each of the control names that fit your list.
However, this is much less efficient than Mat's Mug's answer because you are iterating through the entire list of controls in your form, but it does showcase how you might take a list of static names and iterate through them and a collection.
If you wanted to change all the text controls this would be the way to do it; simply remove the ctrlName check.
As Parfait has correctly pointed out, you could shorten the code to the following if you are confident in your control names:
Dim names() As String
names = Split("txtVoornaam,txtAchternaam,txtAfdeling", ",")
Dim ctrlName As Variant
For Each ctrlName In names
With Me.Controls(ctrlName)
.Locked = False
.BorderStyle = 4
.BorderColor = RGB(255, 165, 0)
End With
Next ctrlName
function Lockdown(strControl)
with me(strControl)
.locked = false
.borderstyle = 4
.bordercolor = RGB(255,165,0)
end with
use me or forms!formname depending on where you're calling from
if your controls are the same, obviously put them in a single sub/function that you can call from anywhere. i would not try to lock or change the format of textboxes, instead just enable/disable, and it will handle the format for you:
textbox.enabled = true/false
if you are doing this on multiple forms and really want a single sub/function to control enabling/disabling the textboxes on each form, then there are various ways of doing that as well, solution will depend on your needs, but certainly doable and some have already commented above.
for example, you can use the "tag" property of the textboxes to flag the textboxes on that form that you want to enable/disable. you can then have a single sub/function that would take in the form as reference, and then you can read the "tag" property of all textboxes on that form and if they are the ones you flagged, you would proceed to enable/disable them
Related
I'd like to disable text boxes if the SponName combo box contains certain company names. I thought I could try a for loop for this, but I'm not sure if I created the right variables or used the for loop properly. Please help!
I've tried cobbling together the following code from the threads below but can't quite adapt it to my problem. I'm still learning VBA and am not great with declaring variables. There's no error message, but the text fields just won't disable after updating SponName.
VBA code for if like multiple criteria
Private Sub SponName_AfterUpdate()
Dim sponcontains As Variant
sponcontains = SponName.Text
Criteria = Array("Company1*", "Company2*", "Company3*", "Company24*")
For i = LBound(Criteria) To UBound(Criteria)
If InStr(sponcontains, Criteria(i)) > 0 Then
cps_manufsite_name.Enabled = False
cps_manufsite_city.Enabled = False
cps_manufsite_st.Enabled = False
cps_manufsite_ctry.Enabled = False
Else
cps_manufsite_name.Enabled = True
cps_manufsite_city.Enabled = True
cps_manufsite_st.Enabled = True
cps_manufsite_ctry.Enabled = True
End If
Next i
End Sub
Use Text property only when control still has focus. You need Value property which is default for data controls so don't have to reference. Explicitly declare all variables. Should have Option Explicit at top of all modules. Set the VBA editor > Tools > Options > Require Variable Declaration to automatically add to new modules - will have to type into existing. Use of * wildcard not appropriate with InStr() function - it's just another literal character.
Like this - default value for Boolean variable is False:
Option Compare Database
Option Explicit
___________________________________________________________________________________
Private Sub SponName_AfterUpdate()
Dim sponcontains As Variant, criteria As Variant, booE As Boolean, i As Integer
sponcontains = Me.SponName
criteria = Array("Company1", "Company2", "Company3", "Company24")
For i = LBound(criteria) To UBound(criteria)
If InStr(sponcontains, criteria(i)) > 0 Then
booE = True
Exit For
End If
Next
cps_manufsite_name.Enabled = Not booE
cps_manufsite_city.Enabled = Not booE
cps_manufsite_st.Enabled = Not booE
cps_manufsite_ctry.Enabled = Not booE
End Sub
But your solution without loop is just as valid and most likely shorter. Again, use Boolean variable to set Enabled state instead of duplicating.
booE = InStr("Company1,Company2,Company3,Company24", Me.SponName) > 0
Consider what would happen if you had to modify this list. Would have to modify code and distribute new db version to users. Alternatively, use a table with an attribute (can be a yes/no field) for company status. Code can do a lookup to table. Better to use company ID value to search. Your combobox should have that as a column in its RowSource and usually that would be the combobox's value.
booE = DLookup("Status", "Companies", "CompanyID = " & Me.SponName)
Use of Conditional Formatting can often avoid VBA altogether.
I am trying to simplify this function which is similar from
Function Snake_makestep()
maincar.Location = locate(xpos, ypos)
car0.Location = locate(posx(0), posy(0))
car1.Location = locate(posx(1), posy(1))
If car2.Visible = True Then
car2.Location = locate(posx(2), posy(2))
End If
If car3.Visible = True Then
car3.Location = locate(posx(3), posy(3))
End If
If car4.Visible = True Then
car4.Location = locate(posx(4), posy(4))
End If
To
If car30.Visible = True Then
car30.Location = locate(posx(30), posy(30))
End If
End Function
I'm not sure If I can/how to use Controls.Find solution within this function. Any help?
To answer the question as asked:
For i = 2 To 30
Dim car = Controls("car" & i)
If car.Visible Then
car.Location = locate(posx(i), posy(i))
End If
Next
Visible and Location are both members of the Control class so it doesn't matter what type of control those car variables are.
Note that this assumes that all controls are on the form itself. If they are in a different parent container, you'd need to use the Controls collection of that container.
Also, I have started i at 2 there, so you'd still need the explicit code for car0 and car1. If they are always visible then you could start the loop at 0 and it would still work, saving you those two lines of code as well.
I have an Access tool/database with external database (ODBC) connections. It's purpose is to review call logs for issues and the user will decide the severity based on the contents of a message.
I have an idea, to assist the review, using VBA. I created an with about 50 strings, and compare that to a field (memo format) in a form (bound to a table column). I want the routine to ONLY highlight the matching portion of the string.
An example is:
If the array string contains "Repor", it will change the font size and color of only those letters within the memo field Like Reported, . with be larger font and different color
I can successfully do this in Excel VBA with this section of code below ("findar" is a pre-built array, rng1 is the designated range)
For i = LBound(findar) To UBound(findar)
For Each rngcell In rng1
startPos = 0
startPos = InStr(rngcell, findar(i))
If InStr(rngcell, findar(i)) <> 0 Then
rngcell.Characters(startPos, Len(findar(i))).Font.Color = vbBlue
rngcell.Characters(startPos, Len(findar(i))).Font.Size = 18
End If
Next rngcell
Next I
"Character", apparently doesn't exist in Access, so I'm trying this, triggered in the "Got Focus" event: It fails with RunTime error 13. I'm certain this is doable, but apparently not by me.....
Dim i As Integer
Dim startpos As Long
'findar is an array
'incident text is inside the form field
findar = Array("returned", "failed") 'real array is about 50 strings
inctext = Me.txtincidentdesc
lngred = RGB(255, 0, 0)
lngblack = RGB(0, 0, 0)
'reset to default
Me.txtincidentdesc.FontBold = False
Me.txtincidentdesc.ForeColor = lngblack
Me.txtincidentdesc.FontSize = 10
startpos = 0
For i = LBound(findar) To UBound(findar)
With Me.txtincidentdesc
If InStr(inctext, findar(i)) <> 0 Then
SelStart = InStr(inctext, findar(i))
SelLength = Len(findar(i))
txtincidentdesc(Mid(inctext, SelStart, SelLength)).ForeColor = lngred 'fails here RunTime error 13
' Me.txtincidentdesc.ForeColor = lngred ' this works fine
' Me.txtincidentdesc.FontSize = 20 'this works fine
End If
End With
Next
End Sub
I've also considered using a recordset and compare that against the memo field but that also failed. Thanks for any input or help on this. Maybe I'm just approaching it wrong
Mark
I want to ask for a letter color in an If conditional:
string="asdfghjkl"
for i=1 to len(string)
letter = mid(string, i, 1)
input_letter = inputbox("Write a letter")
if letter = input_letter 'and letter.Font.Color = RGB(31,78,120)
'my code here
endif
next
The and letter.Font.Color = RGB(31,78,120) is not working. It says i need an object.
Is there any similar way to ask this? This RGB color is blue, and I am using this code to transform the entire sentence to blue (with the record macro excel setting)
With Selection.Font
.ThemeColor = xlThemeColorAccent1
.TintAndShade = -0.499984740745262
End With
Thanks
Regarding your question's problem:
The .Font.Color is a property of the class Range, but in your line of code:
if letter = input_letter 'and letter.Font.Color = RGB(31,78,120)
... you're trying to access this property in the variable letter, which is a String (you don't explicitly declare it as such, but it gets automatically declared when you execute letter = mid(string, i, 1) just above).
That is why you get an Object required exception: you're trying to access the property .Font.Color on something that is not a Range object (actually, not an Object at all).
Regarding your real need:
I'm not sure to understand what you're trying to do. Are you trying to reach a multi-colored text into a single cell in Excel? If I've got it right, you'll have a string:
string="asdfghjkl"
(please note: you can't call your variable String, that's a reserved keyword for the code. Think of calling it something else, though I guess you already do that in your real code or you wouldn't be able to execute it at all).
... and, for each letter of that string,
for i=1 to len(string)
... you want the user to give you a color. In that case, you can't do it in Excel. If not that, could you please express better your real need?
The code below comes closest to your OP logic and comment using the .Characters property of a cell Range (B11) containing your string value:
Code
Option Explicit
Sub test()
Dim blue As Long: blue = RGB(31, 78, 120)
Dim s As String: s = "asdfgh"
Dim letter As String
Dim input_letter As String
Dim i As Integer
Dim rng As Range
Set rng = ThisWorkbook.Worksheets("MySheet").Range("B11")
With rng
.Value = s
' color whole string
.Characters(1, Len(s)).Font.Color = blue
For i = 1 To Len(s)
letter = Mid(s, i, 1)
input_letter = InputBox("Write a letter")
If letter = input_letter And .Characters(i, 1).Font.Color = blue Then
'color found character
.Characters(i, 1).Font.Color = vbWhite
ElseIf input_letter = "" Then Exit For
End If
Next
End With
End Sub
Notes
Always use Option Explicitin your modules declaration head. So you would see that String isn't allowed as variable name as it's a function.
The extra color check in the If condition seems redundant, as characters so long a r e blue.
You seem to prefer repeated InputBoxes within the For - Next loop, could be reduced to a single call.
I have a large window with a little under 200 controls/variables to worry about. Many of them are similar, so I am wondering if instead of repeatedly calling each one individually I can concatenate their names.
I'll make an example:
I have 5 pieces of data that I'm worried about: Red, Orange, Yellow, Green, Blue.
Each one of these will have a label that needs to be made visible, a textbox that needs to be made visible, and a string that contains the text in the textbox:
lblRed.Visible = True
txtRed.Visible = True
strRed = txtRed.Text
Instead of listing this for every one of those 5 pieces of data, is there a way that I could make some sort of array to loop through that could concatenate these variable names?
Dim list As New List(Of String)(New String() {"Red", "Orange", "Yellow", "Green", "Blue"})
Dim i As Integer = 0
Do while i < list.count
lbl + list(i) + .Visible = True
txt + list(i) + .Visible = True
str + list(i) = txt + list(i) + .Text
i = i+1
Loop
I know that the above code doesn't work, but I wanted to give you the basic idea of what I wanted to do. Does this look feasible?
http://msdn.microsoft.com/en-us/library/7e4daa9c(v=vs.71).aspx
Using the controls collection:
Dim i As Integer
i = 1
Me.Controls("Textbox" & i).Text = "TEST"
so
Me.controls("lbl" & list(i)).Visible = true
Keep in mind that when concatenating items, '+' will work on strings and not integers. You might want to always use '&' when concatenating
Another way is to use a select-case block for each type of control. Something like this:
Private Sub EnableControls()
For Each c As Control In Me.Controls
Select Case c.GetType
Case GetType(TextBox)
c.Visible = True
Select Case c.Name.Substring(3)
Case "Red"
strRed = c.Text
Case "Orange"
strOrange = c.Text
Case "Yellow"
'and so on
End Select
Case GetType(Label)
c.Visible = True
End Select
Next
End Sub
Check out the information here:
http://msdn.microsoft.com/en-us/library/axt1ctd9.aspx
I'm sure someone may find something slightly more eloquent than this, but this should achieve your desired result. You'll need the below import:
Imports System.Reflection
If you use properties to access your variables, you can use reflection to grab them by name:
'Define these with getters/setters/private vars
Private Property strRed as String
Private Property strOrange as String
Private Property strYellow as String
Private Property strGreen as String
Private Property strBlue as String
For each color as String in list
If Me.Controls.Count > 1 Then
'Should really check for existence here, but it's just an example.
Me.Controls("lbl" & color).Visible = True
Dim tbx as TextBox = Me.Controls("txt" & color)
tbx.Visible = True
Dim propInfo as PropertyInfo = Me.GetType.GetProperty("str" & color)
propInfo.SetValue(Me, tbx.Text, Nothing)
End If
Next