Using added value in Drop Down List - language-agnostic

Currently, I have received a piece of code to work with. There is a dropdownlist which displays the grade/year of student (Prep - Year 12). The "Prep" value is added to the Dropdownlist by this code, I believe:
Public Sub PopulateGradeWithPrep(ByRef ddlYear As DropDownList, ByVal intMax As Integer, ByVal intMin As Integer)
Dim iLoop As Integer
'populate year ddl
If ddlYear.Items.Count = 0 Then
For iLoop = intMin To intMax
If (iLoop = 0) Then
ddlYear.Items.Add(New ListItem("Prep", CStr(iLoop)))
Else
ddlYear.Items.Add(CStr(iLoop))
End If
Next
End If
End Sub
The name of the dropdownlist is YearLevelAdmission, the is a code to populate the data from the above code to it which is this:
Private Sub PopulateScreenList(ByVal strYearLevelAdmission As String)
'Populate year level admission
slco.PopulateGradeWithPrep(ddlYearLevelAdmission, 12, 0)
'populate year level admission value
If Not slco.IsStringBlank(strYearLevelAdmission) Then
slco.SelectItemInDDList(ddlYearLevelAdmission, strYearLevelAdmission)
End If
slco is Module Common. The First code was written in .vb file.
Those 2 code above are given to me and I am using code behind, trying to select the value of "Prep", then some of the panel will be invisible. This is my code:
If ddlYearLevelAdmission.SelectedValue ="Prep" Then
panel1.visible= false
panel2.visible = false
Else
panel1.visible = true
panel2.visible = true
End If
The problem is when I select Prep, there is auto post back but nothing change, the two panels are still there. I have been stuck for a day already.
Help is really appreciated
Cheers

When you add "Prep" to DropDown you are actually adding a ListItem, not a string. I believe you ment to add a string - instead of
ddlYear.Items.Add(New ListItem("Prep", CStr(iLoop)))
you should write
ddlYear.Items.Add("Prep")

Related

VBA - Compile error: Expected: If or Select or Sub or Function or Property or Type or With or Enum or end of statement

Forgive me for the noob question. Working in VBA is not my normal job.
I get the error listed in the title. The offending code is this:
While index <= 10
index = index + 1
End While
I can't imagine what could be wrong with that, seeing that it is copied directly from the documentation: https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/while-end-while-statement
Seeing that the code can't be the problem, I'm supposing the problem must be something in the context, but before I put this block in, it was working fine.
This block of code is part of a simple, private subroutine which declares a variable, assign a value to it, and then writes it into a file, like so:
Private Sub btnExpAll_Click()
Dim sFldr As String
sFldr = BrowseFolder("Where do you want to export the files?")
If sFldr = "" Then Exit Sub
DoCmd.Hourglass True
Dim sListLocal As String
Dim index As Integer
Dim strInputFileNameLocal As String
sListLocal = ""
index = 0
While index <= 10
index = index + 1
End While
strInputFileNameLocal = sFldr & "\list-local.html"
Open strInputFileNameLocal For Output As #1
Print #1, sListLocal
Close #1
DoCmd.Hourglass False
End Sub
The idea is to add more text to the file as we go through the while loop based on certain conditions - but since I can't even get the while loop to compile...
Link referenced in question is VB.net example, which has similarities to VBA and in some simple examples, easily confused. Replace End While with Wend or use Do … Loop structure.
Review: https://excelmacromastery.com/vba-while-loop/

Access 2013 Dlookup issue

good morning all
I usually find the answer to my queries already on here but this one has me stumped. I have 3 tables,
area containing Area and Region, Phones comtaining phone number and IMSI (SIM number) and a log containing all 4 fields.
I've put in a Dlookup (as found on this site) on the log and although it works the first time the event is triggered it allways comes up with the wrong value. It's probably something stupid and simple, it's years since I used Access and that was probably Access 2007 or something.
Hope you can help. I have created a cut down test version but not sure how to add it to the question.
The VB code is here
Private Sub Area_Change()
Dim FoundRegion As String
FoundRegion = DLookup("[Region]", "[Area]", "'[Area]![Area]=[Area]'")
Me.Region = FoundRegion
End Sub
Private Sub Phone_Number_AfterUpdate()
Dim FoundIMSI As Double
FoundIMSI = DLookup("[IMSI ]", "[phones]", "'[phones]![Phone Number]=[Phone Number]'")
Me.IMSI = FoundIMSI
End Sub
not sure how to add the database
Private Sub Area_Change()
Dim FoundRegion As String
FoundRegion = DLookup("[Region]", "[Area]", "'[Area]![Area]=[Area]'")
Me.Region = FoundRegion
End Sub
Private Sub Phone_Number_AfterUpdate()
Dim FoundIMSI As Double
FoundIMSI = DLookup("[IMSI ]", "[phones]", "'[phones]![Phone Number]=[Phone Number]'")
Me.IMSI = FoundIMSI
End Sub

If [ComboBox] Is Null Statement in VBA/Access 2007

I have a form with one ComboBox (YearToBeBuilt) and two textBox fields (Cost and YearofExpenditureCost). All controls are linked to a main table and the table is updated once the selections/entries have been made on the form.
I have written a procedure in VB called ReCalcIt()which performs the following procedure when called:
Private Sub ReCalcIt()
If Me.YearToBeBuilt = "" Then
Me.YearofExpenditureCost = Me.Cost
Else
Me.YearofExpenditureCost = Me.Cost * (1 + 0.031) ^ (Me.YearToBeBuilt - 2010)
End If
End Sub
When I wrote this I thought that this would do the following:
If the ComboBox [YearToBeBuilt] is blank (e.g.-no selection made) then
the textbox [YearOfExpenditureCost] will return the value of the TextBox [Cost]. Else, the calculation for YearofExpenditureCost is performed.
But this is not working the way it should
What am I doing wrong? I am a VBA n00b so perhaps my syntax is incorrect?
Try it with
If Len(Me.YearToBeBuilt & vbNullString) = 0
So the code will look like this:
Private Sub ReCalcIt()
If Len(Me.YearToBeBuilt & vbNullString) = 0 Then
Me.YearofExpenditureCost = Me.Cost
Else
Me.YearofExpenditureCost = Me.Cost * (1 + 0.031) ^ (Me.YearToBeBuilt - 2010)
End If
End Sub
The usual way is to say:
If IsNull(MyControl) Then
See also: Access VBA: If Form Value <> NULL then run query else, end if. - not doing anything
It seems to me that this might be dependent on the format of the combo box? If it's a number, then
If Len(Me.YearToBeBuilt & vbNullString) = 0
doesn't work. Because the Me.YearToBeBuilt has a value of 0, and so the above returns 1.
I'm wondering if there is a more robust method? Maybe
Me.YearToBeBuilt.ListIndex = -1

How do I access the selected rows in Access?

I have a form which includes a data sheet. I would like to make it possible for a user to select multiple rows, click on a button and have some sql query run and perform some work on those rows.
Looking through my VBA code, I see how I can access the last selected record using the CurrentRecord property. Yet I don't see how I can know which rows were selected in a multiple selection. (I hope I'm clear...)
What's the standard way of doing this? Access VBA documentation is somewhat obscure on the net...
Thanks!
I used the technique similar to JohnFx
To trap the Selection height before it disappears I used the Exit event of the subform control in the Main form.
So in the Main form:
Private Sub MySubForm_Exit(Cancel As Integer)
With MySubForm.Form
m_SelNumRecs = .SelHeight
m_SelTopRec = .SelTop
m_CurrentRec = .CurrentRecord
End With
End Sub
Here is the code to do it, but there is a catch.
Private Sub Command1_Click()
Dim i As Long
Dim RS As Recordset
Dim F As Form
Set F = Me.sf.Form
Set RS = F.RecordsetClone
If F.SelHeight = 0 Then Exit Sub
' Move to the first selected record.
RS.Move F.SelTop - 1
For i = 1 To F.SelHeight
MsgBox RS![myfield]
RS.MoveNext
Next i
End Sub
Here's the catch:
If the code is added to a button, as soon as the user clicks that button, the selection is lost in the grid (selheight will be zero). So you need to capture that info and save it to a module level variable either with a timer or other events on the form.
Here is an article describing how to work around the catch in some detail.
http://www.mvps.org/access/forms/frm0033.htm
Catch 2: This only works with contiguous selections. They can't select mutliple non-sequential rows in the grid.
Update:
There might be a better event to trap this, but here is a working implementation using the form.timerinterval property that i have tested (at least in Access 2k3, but 2k7 should work just fine)
This code goes in the SUBFORM, use the property to get the selheight value in the master form.
Public m_save_selheight As Integer
Public Property Get save_selheight() As Integer
save_selheight = m_save_selheight
End Property
Private Sub Form_Open(Cancel As Integer)
Me.TimerInterval = 500
End Sub
Private Sub Form_Timer()
m_save_selheight = Me.selheight
End Sub
I've tried doing something like that before, but I never had any success with using a method that required the user to select multiple rows in the same style as a Windows File Dialog box (pressing Ctrl, Shift, etc.).
One method I've used is to use two list boxes. The user can double click on an item in the left list box or click a button when an item is selected, and it will move to the right list box.
Another option is to use a local table that is populated with your source data plus boolean values represented as checkboxes in a subform. After the user selects which data they want by clicking on checkboxes, the user presses a button (or some other event), at which time you go directly to the underlying table of data and query only those rows that were checked. I think this option is the best, though it requires a little bit of code to work properly.
Even in Access, I find sometimes it's easier to work with the tables and queries directly rather than trying to use the built-in tools in Access forms. Sometimes the built-in tools don't do exactly what you want.
A workaround to the selection loss when the sub form loses the focus is to save the selection in the Exit event (as already mentioned by others).
A nice addition is to restore it immediately, using timer, so that the user is still able to see the selection he made.
Note: If you want to use the selection in a button handler, the selection may not be restored already when it executes. Make sure to use the saved values from the variables or add a DoEvents at the beginning of the button handler to let the timer handler execute first.
Dim m_iOperSelLeft As Integer
Dim m_iSelTop As Integer
Dim m_iSelWidth As Integer
Dim m_iSelHeight As Integer
Private Sub MySubForm_Exit(Cancel As Integer)
m_iSelLeft = MySubForm.Form.SelLeft
m_iSelTop = MySubForm.Form.SelTop
m_iSelWidth = MySubForm.Form.SelWidth
m_iSelHeight = MySubForm.Form.SelHeight
TimerInterval = 1
End Sub
Private Sub Form_Timer()
TimerInterval = 0
MySubForm.Form.SelLeft = m_iSelLeft - 1
MySubForm.Form.SelTop = m_iSelTop
MySubForm.Form.SelWidth = m_iSelWidth
MySubForm.Form.SelHeight = m_iSelHeight
End Sub
There is another solution.
The code below will show the number of selected rows as soon as you release the mouse button.
Saving this value will do the trick.
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
MsgBox Me.SelHeight
End Sub
Use a Global variable in the form, then refer to that in the button code.
Dim g_numSelectedRecords as long
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
g_numSelectedRecords = Me.SelHeight
End Sub
Dim formRecords As DAO.Recordset
Dim i As Long
Set formRecords = Me.RecordsetClone
' Move to the first record in the recordset.
formRecords.MoveFirst
' Move to the first selected record.
formRecords.Move Me.SelTop - 1
For i = 1 To numSelectedRecords
formRecords.Edit
formRecords.Fields("Archived") = True
formRecords.Update
formRecords.MoveNext
Next i
Why not use an array or recordset and then every time the user clicks on a row (either contiguous or not, save that row or some identifier into the recordset. Then when they click the button on the parent form, simply iterate the recordset that was saved to do what you want. Just don't forget to clear the array or recordset after the button is clicked.?
Another workaround to keeping the selection while attempting to execute a procedure - Instead of leaving the datasheet to activate a button, just use the OnKeyDown event and define a specific keycode and shift combination to execute your code.
The code provided by JohnFx works well. I implemented it without a timer this way (MS-Access 2003):
1- Set the Form's Key Preview to Yes
2- put the code in a function
3- set the event OnKeyUp and OnMouseUp to call the function.
Option Compare Database
Option Explicit
Dim rowSelected() As String
Private Sub Form_Load()
'initialize array
ReDim rowSelected(0, 2)
End Sub
Private Sub Form_Current()
' if cursor place on a different record after a selection was made
' the selection is no longer valid
If "" <> rowSelected(0, 2) Then
If Me.Recordset.AbsolutePosition <> rowSelected(0, 2) Then
rowSelected(0, 0) = ""
rowSelected(0, 1) = ""
rowSelected(0, 2) = ""
End If
End If
End Sub
Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer)
rowsSelected
If KeyCode = vbKeyDelete And Me.SelHeight > 0 Then
removeRows
End If
End Sub
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
rowsSelected
End Sub
Sub rowsSelected()
Dim i As Long, rs As DAO.Recordset, selH As Long, selT As Long
selH = Me.SelHeight
selT = Me.SelTop - 1
If selH = 0 Then
ReDim rowSelected(0, 2)
Exit Sub
Else
ReDim rowSelected(selH, 2)
rowSelected(0, 0) = selT
rowSelected(0, 1) = selH
rowSelected(0, 2) = Me.Recordset.AbsolutePosition ' for repositioning
Set rs = Me.RecordsetClone
rs.MoveFirst ' other key touched caused the pointer to shift
rs.Move selT
For i = 1 To selH
rowSelected(i, 0) = rs!PositionNumber
rowSelected(i, 1) = Nz(rs!CurrentMbr)
rowSelected(i, 2) = Nz(rs!FutureMbr)
rs.MoveNext
Next
Set rs = Nothing
Debug.Print selH & " rows selected starting at " & selT
End If
End Sub
Sub removeRows()
' remove rows in underlying table using collected criteria in rowSelected()
Me.Requery
' reposition cursor
End Sub
Private Sub cmdRemRows_Click()
If Val(rowSelected(0, 1)) > 0 Then
removeRows
Else
MsgBox "To remove row(s) select one or more sequential records using the record selector on the left side."
End If
End Sub

Repetition of query to produce report

I am creating a bill of materials program.
There are two main tables named Products and Sub_Products.
In the Products table, the fields are (Product_Name, Code).
In the Sub_Products table, the fields are (Code, Sub_Name).
The tables are linked with code, i.e.: one product is made up of many sub_products, each sub_product is a product as well, making it have many sub_products.
I have created a query that reads a product and gets its sub_products. I need a query to compare Sub_Name with Product_Name and then check more sub_products,
continuing until no more sub_products are found.
Any ideas?
I guess you will have to use a script rather than SQL query to loop through them. Assuming that the products can be nested more than 3 levels.
I've been working on this exact problem in an ASP.NET MVC application. A function that gathered all the subproducts for each product and recursed on each subproduct worked well. We have some BOMs that are 15 levels deep.
I realize this question was asked a long time ago, but I had a very similar question and finally figured out a good answer. So I am posting it here in case anyone needs to know how to create a Bill of Materials.
In my example there is a table called "Part_Item_Table" which lists parent items and all of their childeren. Those childeren can also be parents to other childeren. The difficulty was that the BOM could be 3 levels deep all the way up to 30 levels deep or more. My "Part_Item_Table" also lists whether items are "Make" items or not. Only "Make" items will have childeren. The table you are querying may not have that feature, but the code below will probably still be helpful to get the idea.
This set of code uses several things that were new to me such as recursive code, calling a query I had already created and passing in a variable using the querydef methods, and using recordsets to get large information sets in and out of functions. I also used a sequence field in my BOM Table so I could sort by it and view the BOM in the order it is meant to be (Showing visually which level 3 items roll up into which level 2 items). If there is something that can be improved I am open to suggestions. This does work for my needs right now and hopefully it is helpful to someone else.
Option Compare Database
Public stFirstPart As String
Private Const BOMTable As String = "BOM_Table" 'Set this variable to the name of the table
Private Const ComponentQ As String = "GetComponentsQ" 'Set to the name of the query in the database
Function BOM()
Dim stQuery As String 'Used to make a query
Dim i As Integer 'Used to create the sequence number
Dim iLevel As Integer 'Used to show BOM level
Dim rsParent, rsBOMTable As DAO.Recordset 'Used to hold query results
'Make sure there is a part number in the form
If IsNull(Forms![Entry Form]![Part_Number]) Then
Debug.Print "There is no part number entered in the form"
MsgBox "There is no part number in the form.", vbOKOnly, "Can't fool me."
Exit Function
End If
stFirstPart = Forms![Entry Form]![Part_Number] 'Get the top part number from the form
'Make sure this is a Make item. Only make items will have childeren
stQuery = "SELECT ITEM.ITEM_NO, ITEM.MAKE_BUY_FLAG, ITEM.CURRENT_FLAG " & _
" FROM PART_ITEM_TABLE AS ITEM " & _
" WHERE (((ITEM.ITEM_NO)='" & stFirstPart & "') AND ((ITEM.MAKE_BUY_FLAG)='M') AND ((ITEM.CURRENT_FLAG)='Y'));"
Set rsParent = CurrentDb.OpenRecordset(stQuery)
If rsParent.EOF And rsParent.BOF Then
Debug.Print "This is not a make item"
MsgBox "This is not a Make item.", vbOKOnly, "I tried."
Exit Function
End If
'Clear the BOM table and load this first part number
DoCmd.SetWarnings False
DoCmd.RunSQL "Delete from " & BOMTable & ""
Set rsBOMTable = CurrentDb.OpenRecordset(BOMTable, dbOpenDynaset)
i = 1
iLevel = 1
rsParent.MoveFirst
With rsBOMTable
.AddNew
!Sequence = i
!Level = iLevel
!Item_Number = stFirstPart
!Make_Buy = "M"
.Update
End With
rsParent.Close
Set rsParent = Nothing
rsBOMTable.Close
Set rsBOMTable = Nothing
'-----------------------------------------------------------------------------------------------------------------------------------
'Start going down levels
'-----------------------------------------------------------------------------------------------------------------------------------
iLevel = 2
Call RecursiveLevels(stFirstPart, iLevel, i)
DoCmd.SetWarnings True
End Function
Function RecursiveLevels(PartNumber As String, iLevel As Integer, i As Integer)
Dim rsLevels As DAO.Recordset
Dim stPart As String
Set rsLevels = GetComponents(PartNumber)
If rsLevels.BOF And rsLevels.EOF Then
Debug.Print "This was a Make item with no children. That shouldn't happen. "; PartNumber
GoTo ExitPoint
End If
rsLevels.MoveFirst
Do While Not rsLevels.EOF
If rsLevels!Make_Buy <> "M" Then ' Anything that is not a Make item is written to the BOM table one line at a time.
i = i + 1
Call WriteToBOMTable(iLevel, i, rsLevels!Parent_Number, rsLevels!Component_Number, rsLevels!Make_Buy)
Else 'The Make item is written to the table, then we query for all of its children
stPart = rsLevels!Component_Number
i = i + 1
Call WriteToBOMTable(iLevel, i, rsLevels!Parent_Number, rsLevels!Component_Number, rsLevels!Make_Buy)
If stPart = stFirstPart Then 'Check to make sure this recursive thing doesn't go on forever.
Debug.Print "This part number is the same as the first part number. Circ Reference. "; stPart
GoTo ExitPoint
End If
iLevel = iLevel + 1 ' get ready to go one level deeper
Call RecursiveLevels(stPart, iLevel, i)
End If
rsLevels.MoveNext
Loop
ExitPoint:
iLevel = iLevel - 1 'Done with this level. Come back up a level.
rsLevels.Close
Set rsLevels = Nothing
End Function
Function WriteToBOMTable(Level As Integer, i As Integer, ParentNumber As String, ComponentNumber As String, MakeBuy As String)
Dim rsBOMTable As DAO.Recordset
Set rsBOMTable = CurrentDb.OpenRecordset(BOMTable, dbOpenDynaset)
With rsBOMTable
.AddNew
!Parent_Number = ParentNumber
!Item_Number = ComponentNumber
!Level = Level
!Make_Buy = MakeBuy
!Sequence = i
.Update
End With
Debug.Print "Level: "; Level; "Component: "; ComponentNumber
rsBOMTable.Close
Set rsBOMTable = Nothing
End Function
Function GetComponents(PartNumber As String) As DAO.Recordset
Dim qdf As QueryDef
Set qdf = CurrentDb.QueryDefs(ComponentQ)
qdf.Parameters("PartNumber") = PartNumber
Set GetComponents = qdf.OpenRecordset
End Function