Sub to add controls works when called from one sub but the other - vba

I am getting an error that I just can't figure out. I wrote a sub that opens a form in Design view, deletes all of the dynamic controls, then adds the requested number.
The sub gets called in two different ways. The user opening the form (via a Main Menu form) to fill out a new form (so the dynamic controls are deleted then recreated) OR after the form is created, the user can click a button on the form to add more rows of controls. Both the Main Menu and button form call the same sub, BUT when the button is clicked the code gets stuck and error 29054 'Microsoft Access can't add, rename, or delete the control(s) you requested.' is thrown. The button to debug is deactivated so I can't see what line is actually getting stuck, but when I step through the code this is the last line before the error pops up (the last indent block in the context below):
With Application.CreateControl("BOM5", acComboBox, acDetail, frm.Controls("Tabs").Pages(PageNum).Name, , ChangeTypeLeft, LastControlTop, ChangeTypeWidth, ControlHeight)
The rest of the code is as follows.
DoCmd.OpenForm "BOM5", acDesign
Set frm = Application.Forms("BOM5")
' Cycle through controls and set LastControlTop based on the last dynamic control, if any
For i = 0 To frm.Controls.Count - 1
Set ctl = frm.Controls(i)
Debug.Print ctl.Name
If ctl.Tag Like DYNAMIC_TAG & "*" Then
If ctl.ControlType = acComboBox Or ctl.ControlType = acTextBox Then
LastControlTop = ctl.Top + ControlHeight + ControlPadding
FormRowCount = FormRowCount + 1
End If
Else
FormRowCount = (FormRowCount / 6) + 1 ' Convert number of fields to number of rows then add one to start new batch of controls
Exit For
End If
Next
PageNum = frm.Controls("Tabs").Pages.Count - 1 ' .Pages has an index of 0. Getting PageNum to follow suit with the -1
' Add controls for inputting parts
For FormRowCount = FormRowCount To NewControlCount
With Application.CreateControl("BOM5", acComboBox, acDetail, frm.Controls("Tabs").Pages(PageNum).Name, , ChangeTypeLeft, LastControlTop, ChangeTypeWidth, ControlHeight)
.Name = "ChangeType" & FormRowCount
.Tag = DYNAMIC_TAG
.RowSourceType = "Table/Query"
.RowSource = "ChangeType"
End With
The last for loop has 5 other With Application.CreatControl statements, but I just showed the first one. The other 5 are similar except text boxes instead of combo.
I've had this error before, but I think I resolved it by moving the DoCmd.OpenForm statement to a different part of the code (like out of an if statement or for loop or somewhere that wasn't letting it get called) but I don't think that will resolve it. Besides, the first for loop iterates correctly since I see it grabbing the control height of the last dynamic control.

You can't do this. A form or report can only hold a certain amount of controls, deleted or not, so - eventually - it will not accept more controls. At that point, you must recreate the form from scratch.
So, don't delete the controls. Create those you may need and, at any time, hide those not needed and rename the rest if necessary.

Related

int array to returning to previous record

I have a standard form which displays information based on a partID. The form has a subform showing ancillaries of the part where on double click will take you to that part number (code snippet below). var_lastPartID is a global long variable which records the current ID so upon pressing a return button will take you back to the previous record. However, as this can only store 1 value at a time I imagine the best way would to be to store an array of long/int whereby upon the double click the current ID is stored. When you click return it will take the last value added and take you to that record then delete that record, so that the next time you click return it will take you to the next record. However, my experience with VBA is very limited and I have not used them before. Please could someone explain the syntax of how I could achieve this?
Private Sub childPart_DblClick(Cancel As Integer)
var_lastPartID = Forms![Part]![part_ID].Value
Forms("Part").Recordset.FindFirst ("part_ID = " & childPart)
End Sub
Ok, so you have a parent form, and then a sub form.
You click on a row in the sub form. You want the parent form to JUMP to this record. But as you may well do this several times, then you want on the main form some kind of “back” or “previous” button. When you click on this back button, then you want the previous ID that you jumped to occur.
Ok, the way to drive this and have a next/previous set of buttons that traverse the records that you looked at, worked on, and clicked on?
Ok, in the main form, at the code module level, create a pointer, and a collection, and a routine like this:
Option Compare Database
Option Explicit
Dim intListPosition As Integer
Dim colRecords As New Collection
Public Sub AddAndJump(MyID As Long)
' add this new ID to the list
intListPosition = intListPosition + 1
colRecords.Add MyID, CStr(intListPosition)
' now jump (move) to this record
Me.Recordset.FindFirst "ID = " & MyID
End Sub
Public Sub BackOne()
' call this code from your back buttion
' go to previous record
If intListPosition > 1 Then
intListPosition = intListPosition - 1
Me.Recordset.FindFirst "ID = " & colRecords.Item(CStr(intListPosition))
End If
End Sub
Public Sub ForwardOne()
'call this code from your forward buttion
If intListPosition < colRecords.Count Then
intListPosition = intListPosition + 1
Me.Recordset.FindFirst "ID = " & colRecords.Item(CStr(intListPosition))
End If
End Sub
Private Sub cmdBack_Click()
BackOne
End Sub
Private Sub cmdNext_Click()
ForwardOne
End Sub
So the above goes in the main form.
In my "sample" code, I used ID, you have to change that to part_id
Now, you button code in the sub form is going to be "similar" to what you have, but you can now use this:
Call Me.Parent.AddAndJump(Me.ID)
So, the above will now allow a next/back button on the top form, as you click away and select the items from the lower form (the sub form), then the list of "id" will be built up, and the buttons for next/prev will work.

Getting .value property when using a string and variable

I am creating a form in Access which will be used as an order sheet for classroom materials. I have the available resources listed and a text box next to the resource where the user inputs the quantity they desire.
My VBA code checks to see if any entries have been made by using the following. (I am using Nz() to allow for Null results):
QuantCheck = Nz(Box1.Value, 0) + Nz(Box2.Value, 0) + Nz(Box3.Value, 0)
Where "QuantCheck" is the variable I am using in the IF statement which begins the workflow:
If QuantCheck > 0 Then
I would like to clean this up by using some kind of loop statement, however I am not able to extract the .value from a string. I would love something like the following which I could incorporate into a loop:
"Box"&VariableNumber.Value
From what I can tell, I am not able to use a string (concatenated or otherwise) as the base for the .value call.
It is interesting that there is a way to accomplish this when using a SQL statement. I have this elsewhere in the code which works nicely:
SQLStr = "INSERT INTO OrderRequests VALUES (cbSchool, txtName, Title" & x & ".caption, Box" & x & ")"
Here I have a variable "x" which increases with each loop to change the Title line, and the Box line.
Any help is appreciated.
I suggest you use the Tag property of the controls. Put "QuantCheck" in the Tag property of any control you want to include. Then
Function QuantitiesExist(frm As Form) As Boolean
Dim Ctrl As Control
Const sQUANTCHK As String = "QuantCheck"
For Each Ctrl In frm.Controls
If Ctrl.Tag = sQUANTCHK Then
If Nz(Ctrl.Value) > 0 Then
QuantitiesExist = True
Exit For
End If
End If
Next Ctrl
End Function
Now you get self documenting code
If QuantitiesExist(Me) Then
And when you add/delete/change controls, you don't have to edit your code. Just set up new controls with the proper tags.
You could loop through the control on the for checking the names and then if it is the one you wanted take an action on it, is this what you was thinking of?
Dim Ctrl As Control
For Each Ctrl In Me.Controls
If Ctrl.Name = "TxtPath" Then ' "Box" & VariableNumber Then
MsgBox Ctrl.Value
End If
Next

Can not update List Box Selected Collection in code

I have a list box on a form being populated from a query, with items selected on basis of matching a delimited list. So sSystemString equals something like "A;B;C"
Then I load records A,B,C,D,E,F from the SQL Server DB and only A,B,C should be selected.
Is there a native way to do this in MS Access (2010). I'm using an ADP in this case.
I'm doing it via code but I can't the selected property does not reflect my changes, nor does the form.
Here's my code:
Dim rs As New ADODB.Recordset
Dim sSystemString As String
If Not IsNull(Me.OpenArgs) Then sSystemString = Me.OpenArgs
' Load this list box with SRC Systems
Call rs.Open("SELECT DISTINCT System FROM dbo.System WHERE System IS NOT NULL", _
CurrentProject.Connection, adOpenForwardOnly, adLockReadOnly)
Do Until rs.EOF
lstSrcSystems.AddItem (rs.Fields(0))
If InStr(sSystemString, rs.Fields(0)) > 0 Then
lstSrcSystems.Selected(lstSrcSystems.ListCount - 1) = True
End If
rs.MoveNext
Loop
My code definitely hits the lstSrcSystems.Selected(lstSrcSystems.ListCount - 1) = True line.
After running this line, inspecting the property in the immediate window still returns 0 (it doesn't change). On the form, the item is also not selected.
UPDATE: I just checked my code again and now it is being updated, but the next AddItem apparently unselects it again.
I suspect I have some weird combination of properties that make this read only, but I can select items interactively, and indeed when I extract the selected items back off in code, the Selected property works as expected - i.e. I select an item on the form and it reflected in this property.
The form is unbound and is called from a button on another form with this code:
DoCmd.OpenForm "fSiteList", acNormal, , , acFormEdit, acDialog, Me.SRCSystems
The problem was actually that lstSrcSystems.AddItem (rs.Fields(0)) was resetting the Selected state. I can't find any mention of this behaviour or how to turn it off. I altered my form as follows:
Form / Data properties all blank (unbound)
List Control / Data / Control Source: blank
List Control / Data / Row Source: SELECT statement populating my list
This has the effect of populating the list but not binding the list or form to anything. (I found that binding it stopped me interactively editing data on it)
Form_Load code behind the form was changed to select already existing items:
Private Sub Form_Load()
Dim sSystemString As String
Dim iIndex As Integer
' List is bound to site list (from connections)
' Highlight those that are listed
If Not IsNull(Me.OpenArgs) Then sSystemString = Me.OpenArgs
iIndex = lstSrcSystems.ListCount
Do While iIndex > 0
If InStr(sSystemString, lstSrcSystems.ItemData(iIndex)) > 0 Then
lstSrcSystems.Selected(iIndex) = True
End If
iIndex = iIndex - 1
Loop
End Sub
I'm still curious to know whether there is a more 'built in' way to achieve this: edit a delimited string field.

Behind-the-scenes VBA activity interfering with listboxes

Background:
I have a VBA project in an Excel spreadsheet. In it, I have a form that contains a frame that contains multiple labels/listboxes.
When the application is loaded, these listboxes are populated with data. (Nearly) everything works as expected, and this is what the form looks like:
My Problem:
My problem is that when a user clicks on an item in a listbox, that listbox is automatically "jumped into view" or something. For example; if I click on list item 5 like so, the frame scrolls down so that the listbox is at the top:
Before Click:
After Click:
You can see that the list item under my click has been selected, but the list was scrolled up beforehand so it's not actually the intended selection. This is not being done by any of my code.
If I put a "Stop" as the "Enter" and "Click" event handlers, the correct list item is selected and the list has not jumped at "Enter", but has by "Click".
More information:
If I continue to click on other elements from the same listbox, the listbox does not "jump". I can scroll up and down, and the listbox will not jump. As soon as I click in a DIFFERENT listbox (so "listbox1_Exit" and "listbox2_Enter" have been raised), the new listbox is "jumped" into view.
My Question:
My Question is: what "behind-the-scenes" code is VBA executing between these two events, and how can I prevent this from occuring? Or is there a way to "monitor" events?
(VBA appears to just resume execution if you "step" past the end of an event handler, rather than move to the next executing line of code - which would REALLY be helpful here...)
Edit:
Minimal example - plonk a frame on an empty form, add 2 listboxes to the frame and execute this code in the form:
Private Sub UserForm_Initialize()
Dim lb1 As Control
Dim lb2 As Control
Dim Frame1 As Control
Set Frame1 = Me.Controls("Frame1")
Set lb1 = Me.Controls("ListBox1")
Set lb2 = Me.Controls("ListBox2")
lb1.Height = 500
lb2.Height = 500
For i = 1 To 25
lb1.AddItem ("Potato " & i)
lb2.AddItem ("Frog " & i)
Next i
lb1.Top = 0
lb2.Top = lb1.Height + 10
Frame1.ScrollHeight = lb1.Height + lb2.Height + 10
End Sub
then scroll to the point where you can see the end of Listbox1 and the start of Listbox2, and try clicking elements in Listbox2.

Opening new form after right click on selected record in continuous form

In my access database before I was using a list box in the form for having list of items and I had below code for opening new form after right click on each selected item in list box.
Private Sub ItemList_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Const RIGHTBUTTON = 2
If Button = RIGHTBUTTON Then
DoCmd.OpenForm "frmShortcut_GenerateTask"
DoCmd.MoveSize udtPos.X * mp.TwipsPerPixelX, udtPos.Y * mp.TwipsPerPixelY
End If
End Sub
Now I am using a continuous form instead of list box and I have defined a [isselected) field for selecting each record in continuous form after clicking on that. Now my problem is how I have to write code for right clicking and opening new form.
I used the same code I had used for list box, but it does not work and nothing happened.
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Const RIGHTBUTTON = 2
If Button = RIGHTBUTTON Then
DoCmd.OpenForm "frmShortcut_GenerateTask"
DoCmd.MoveSize udtPos.X * mp.TwipsPerPixelX, udtPos.Y * mp.TwipsPerPixelY
End If
End Sub
Private Sub P_Click()
On Error Resume Next
Me.IsSelected = Not Me.IsSelected
' Save the status
Me.Dirty = False
' Force conditional highlighting
P_ForceHighLight
' Update display in SF_Selected
Me.Parent("SF_Selected").Requery
ActiveControl.SelLength = 0
On Error GoTo 0
End Sub
I recommend either using a DoubleClick event in all of your textboxes and combos, or else putting a small button at one edge of your continuous form that allows the user to open records. Right-click is going to give you problems (so will labels and image controls) because this event doesn't cause (or ensure) that the Form's internal Recordset Bookmark property is actually moved to the record they right-clicked on. In my experience, only buttons, textboxes, and comboboxes will move the Bookmark to the record the user is trying to select.
To Test this, try putting code at the top of your different routines that shows you what record is selected:
MsgBox "RecordID = " & Me!RecordIDField
If you are consistently getting the correct ID, then your problem has to do with how your opening your new form, passing parameters or arguments, etc.