I've been teaching myself VB.NET for a couple of weeks now, and I'm stumped. I have 18+ years experience in PHP, and so far the logic has been very similar. What I can't figure out is how to dynamically specify a label to change the text. What I have is 50 labels, one for each state. Then I have a MySQL query pulling a count for that state and then updating the label for the state.
My code currently is:
While dataReader.Read
If dataReader.Item("state") = "CO" Then
lblCO.Text = "(" & dataReader.Item("total") & ")"
lblCO.Visible = True
End If
End While
What I don't want to do is make 50 "IF" statements to specify the new text for each label. Each of the labels start with lbl and end with the two state abbreviation. For example: lblCA, lblCO, lblFL.
I thought I could concatenate like this: (similar style of concatenating in PHP)
While dataReader.Read
lbl&dataReader.Item("state").Text = "(" & dataReader.Item("total") & ")"
lbl&dataReader.Item("state").Visible = True
End While
I quickly found out that you can't concatenate like this in VB.NET.
Does anyone know how to do this dynamically? Or am I stuck making 50 "IF" statements?
Thank you!
I first want to thank Plutonix and Mike_OBrien. I was able to combine both of your responses to make a solution that worked perfect for me.
Plutonix: I figured out you had the ".Text" in the wrong place, but overall your snippet worked. Oh, in VB.NET 2013, it forced me to use "Me." instead of the form name.
Mike_OBrien: I liked your "IsNot Nothing" check
The solution that worked perfect for me:
While dataReader.Read
If Me.Controls("lbl" & dataReader.Item("state")) IsNot Nothing Then
Me.Controls("lbl" & dataReader.Item("state")).Text = "(" & dataReader.Item("total") & ")"
Me.Controls("lbl" & dataReader.Item("state")).Visible = True
End If
End While
So the above will make sure that the label exists. If it does exist, then it will update the text to the count that was pulled from MySQL. Like I said before, I'm very new to VB.NET. So even though the code above works, if anyone knows a reason that what I figured out might be bad for the application or system, I'm open for suggestions.
As long as you are in the code behind of the form you are trying to manipulate you could do:
Dim currControl = From r In Me.Controls Where DirectCast(r, Control).Name = "lbl" & StateAbbreviation Select r
You only need the DirectCast portion if you have option strict on otherwise you can do:
Dim currControl = From r As Control In Me.Controls Where r.Name = "lbl" & StateAbbreviation Select r
replace StateAbbreviation with whichever state you are currently trying to edit.
then do:
If currControl IsNot Nothing AndAlso currControl.Any Then
currControl(0).Text = "Whatever you want to assign to the label"
End If
This requires .NET 3.5 or greater. But it basically just uses LINQ to find the control you want to edit and then uses the reference to that control to set the display value.
Related
I haven't even been able to find anyone with this problem online, let alone a solution!
I had several msgboxes in some VBA code I was writing to provide feedback for debugging. After a while the code was stable and I commented out the MsgBox lines.
One set of msgboxes wouldn't vanish though. I even deleted it rather than removing it. In the end I decided I must have a duplication somewhere, somehow, even though all the related code ended up calling this specific function where I'd removed the MsgBox.
I couldn't see any other explanation as I'd literally deleted the lines of code. I searched the entire project for fragments of the string which formed part of the MsgBox's text. Ctrl + F, set to search entire project, tried with and without pattern matching.
I found literally nothing. I decided the string must be more constructed than I thought, and instead opted to search using 'MsgBox' to search through every time I had used the command. I checked every single MsgBox in every project file and found nothing even remotely like the debugging message the code was still generating.
Anyone have any other ideas what may be going on? My best theory is that I've somehow hidden or duplicated something, but aside from depreciating a form whose code doesn't contain a single MsgBox or call code which does, I've done nothing I can think of to cause this.
I'd paste the code but you'd still just be taking my word for it that the MsgBox isn't there.
Can anyone help? It has to be hiding in there somewhere, somehow.
Many thanks,
Code where the MsgBox originally appeared (it provided feedback based on the RecordSet's NoMatch property):
Public Function createRecordSnapshot(Table As String, dbRefIn As String, payNum As String)
Dim fieldNames As String
Dim oldDataSet As String
Dim rs As Recordset
Dim fieldCount As Integer
Dim recordTot As Integer
Dim srchString As String
Set rs = CurrentDb.OpenRecordset(Table, dbOpenDynaset)
srchString = "DBRef = '" & dbRefIn & "' and PayrollRef = '" & payNum & "'"
fieldCount = rs.Fields.Count
rs.FindFirst (srchString)
Dim o As Integer
For o = 0 To fieldCount - 1
fieldNames = fieldNames & rs.Fields(o).Name & "|"
Next o
For o = 0 To fieldCount - 1
oldDataSet = oldDataSet & rs.Fields(o).Value & "|"
Next o
createRecordSnapshot = fieldNames & vbNewLine & oldDataSet
End Function
After a modification it is good practice to re-compile the code manually, as it makes running the code faster (can skip the compilation on next run) and refreshes previously compiled code. The option is in the VB Editor's Debug menu.
I am developing an Access database (using Office 2016) and have several tab controls which I want to display the number of records in the subform/subreport.
After a lot of searching etc I have it working for the subforms using a function which I call in the main forms current event (but in a seperate function so I can also call via a macro when I change the main forms record with a combo box, as it wasn't updating otherwise). The code I am using is:
Function ClientTotals()
Dim i As Integer
i = Form_sbfrm_ClientContacts.Recordset.RecordCount
Form_frm_Clients.ClientTabs.Pages("Contacts").Caption = "Contacts (" & i & ")"
End Function
This works perfectly for me and my tab name becomes "Contacts (No. of records)" but I can't get the syntax right to change this to work for a report, is it possible?
I have tried:
Function ClientTotals()
Dim i As Integer
i = Form_sbfrm_ClientContacts.Recordset.RecordCount
Form_frm_Clients.ClientTabs.Pages("Contacts").Caption = "Contacts (" & i & ")"
Dim j As Integer
j = Report_rpt_CurrentProjects.Recordset.RecordCount ' this line is highlighted with the debugger
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
End Function
As well as:
Dim j As Integer
j = rpt_CurrentProjects.Report.Recordset.RecordCount ' this line is highlighted with the debugger
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
and various others.
Another question I have is why is the syntax for the form "Form_sbfrm" etc and not using a "!". If I change to "!" it bugs out.
Thanks for your help, KAL
Thanks Delecron,
I think I will stick with the tabs for now as they are giving me exactly what I want, but remember what you have said for when I make future improvements if its a better way of doing it.
EDIT
Using what you have said I changed my VBA to a DCOUNT method:
Dim j As Integer
j = DCount("*", "qry_CurrentProjects", "FK_Project_Client_ID = Forms!Navigation!Navigationsubform.form!Client_ID")
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
This means my report tabs are now also working just how I wanted
I was getting in a muddle with the criteria/filter, hense the edit.
If Recordset is an old method I am assuming it would be best to replace my other code with the Dcount method?
Thanks again, KAL
Further EDIT
After doing this I could see that everytime the form was changed there was a slight flicker. Not bad but you could see there was a lot of calculation going on. Therefore I have changed my method to the following, and posted here for anyone looking at this in the future.
In the form footer a textbox with COUNT([Project_ID])
In my function
Dim j As Integer
j = Form_frm_Clients!rpt_CurrentProjects.Report!txt_CurrentProjectsCount.Value
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
Now I can see it is working quicker with no flicker.
Recordset if you need to return complex data, if you need one value, a total or a sum, Domain functions are the way to go. Don't overdue them though, having too many on a form or a report can start to bog down usability.
Glad I can help.
Recordset.Recordcount is a legacy feature that only worked in ADP files (Access front end's to a SQL database). Those are no longer supported.
If the report is based on 1 client only and there is no grouping then you can do this:
Click on the detail section and in Events create an event for On Paint. In there set
(Name of Page).Caption = DCount("*", "NAME OF QUERY/TABLE") or
(Name of Page).Caption = DCount("*", "NAME OF QUERY/TABLE", "Filter Expression") (Filter expression optional).
If the report is grouped where it will show a different page per client or date range or any other grouping this will not work since the Caption field is not data bound. You would have to add logic to the Dcount statement above to field by the current filter condition.
For example, say you have a database of 200 clients and you're running one big report on all of them, each page will get its own tab control per client, the syntax would be
(Name of Page).Caption = DCount("*", "ClientContacts, "ClientID = " & ClientID)
The better way to do it especially if you are grouping is get rid of the tab control and use databound controls. You could create a box around the information that would be in the tab page and put a textbox where the tab would go. Create a group header for however you are grouping the data. Create another invisible textbox in the group header and set the controlsource = Count([fieldname]) where fieldname is whatever you are grouping the data by (the inside brackets count).
Then in the textbox you created to simulate the tab, set the controlsource to the name of the invisible textbox.
Let me know if this helps.
I'm building a database that will be accommodating a large number of records, so I want a search function that is easiest on the server. I am using the following code but I know it isn't sustainable for a larger database. It's looking at the search box and running a query to narrow the search results:
Private Sub SearchFor_Change()
'Create a string (text) variable
Dim vSearchString As String
'Populate the string variable with the text entered in the Text Box SearchFor
vSearchString = SearchFor.Text
'Pass the value contained in the string variable to the hidden text box SrchText,
'that is used as the sear4ch criteria for the Query QRY_SearchAll
SrchText.Value = vSearchString
'Requery the List Box to show the latest results for the text entered in Text Box
'SearchFor
Me.SearchResults.Requery
'Tests for a trailing space and exits the sub routine at this point
'so as to preserve the trailing space, which would be lost if focus was shifted from
'Text Box SearchFor
If Len(Me.SrchText) <> 0 And InStr(Len(SrchText), SrchText, " ", vbTextCompare) Then
'Set the focus on the first item in the list box
Me.SearchResults = Me.SearchResults.ItemData(1)
Me.SearchResults.SetFocus
'Requery the form to refresh the content of any unbound text box that might be feeding
'off the record source of the List Box
DoCmd.Requery
'Returns the cursor to the the end of the text in Text Box SearchFor,
'and restores trailing space lost when focus is shifted to the list box
Me.SearchFor = vSearchString
Me.SearchFor.SetFocus
Me.SearchFor.SelStart = Me.SearchFor.SelLength
Exit Sub
End If
'Set the focus on the first item in the list box
Me.SearchResults = Me.SearchResults.ItemData(1)
Me.SearchResults.SetFocus
'Requery the form to refresh the content of any unbound text box that might be
'feeding off the record source of the List Box
DoCmd.Requery
'Returns the cursor to the the end of the text in Text Box SearchFor
Me.SearchFor.SetFocus
If Not IsNull(Len(Me.SearchFor)) Then
Me.SearchFor.SelStart = Len(Me.SearchFor)
End If
Ideally I want a form that has several search fields, and one 'find' button that runs the queries to return the results in a list box.
I'm also not sure how to set it up so that when the user double clicks on a selection from the search results, the selected record is opened in a form in edit mode.
Any help would be much appreciated, thanks!
First off, you've asked two questions in one post. I recommend you take out the second question regarding opening the selection in edit mode on double click.
As best as I can understand, you're concerned about the performance of your current code as well as the lack of features or flexibility it offers.
Regarding performance:
Don't use the change method to perform the filter. If you really do want to use the change method, use it only to set a timer interval to something like 500 (ms) and then perform the filter on the Timer event. This was the filter won't occur until after the user has stopped typing for a half second.
Avoid "fuzzy" searches (use of asterisk/percent in text fields). It doesn't look like you're using them now. While fuzzy searches usually make software more user friendly, they make it less user friendly when they cause a significant hit on the performance.
When working with large amounts of data, most performance gains come from carefully restructuring the way your application works, by upgrading to SQL Server, and by upgrading your server and network to better hardware. You can only improve about so much when using a JET/ACE backend database container. SQL Server with ADO and ODBC linked tables both offer some advantages over DAO with JET/ACE. ODBC linked tables offer lazy loading while ADO offers things like disconnected recordsets which can be filtered without an additional call back to the server (there are limitations to this).
As already mentioned above, you might need to carefully rethink how your application works and how it is designed. It's better to try to limit the amount of complicated queries that are needed and the amount of text-based searching that is allowed/required. Use more lookup/reference tables. Instead of storing thinks like categories as text, consider storing them as a Long Number CategoryID instead. Queries on indexed numeric fields usually perform better than queries on text-based fields, especially if you are using LIKE with asterisks in your query.
As far as the rest of your question (flexibility and features), consider creating a procedure that builds a criteria/where statement for you based on the values of multiple controls. In a situation such as yours my code would look something like this (below). Notice that I did use asterisk (fuzzy search) in my Description search/filter. If it performs poorly you'll need to consider taking that out and allowing the user to put their own asterisks in instead.
Private Sub cmdSearch_Click()
Call SetRowSource
End Sub
Private Sub txtSearch_AfterUpdate()
Call SetRowSource
End Sub
Private Sub cboCategoryID_AfterUpdate()
Call SetRowSource
End Sub
Private Sub txtBrand_AfterUpdate()
Call SetRowSource
End Sub
Private Sub SetRowSource()
Dim sSQL as String
sSQL = "SELECT ItemID, Description, Brand FROM tblItems "
sSQL = sSQL & GetWhere
Me.lstSearchResults.RowSource = sSQL
End Sub
Private Function GetWhere() as String
Dim sWhere as String
If Nz(Me.cboCategoryID, 0) <> 0 Then
sWhere = sWhere & "CategoryID = " & Me.cboCategoryID & " AND "
End If
If Nz(Me.txtSearch, "") <> "" Then
sWhere = sWhere & "Description LIKE '*" & Replace(Me.txtSearch, "'", "''") & "*' AND "
End If
If Nz(Me.txtBrand, "") <> "" Then
sWhere = sWhere & "Brand = '" & Replace(Me.txtBrand, "'", "''") & "' AND "
End If
If sWhere <> "" Then
sWhere = Left(sWhere, Len(sWhere)-5)
GetWhere = "WHERE " & sWhere
End If
End Function
I think I might be a little bit odd in the Access community but I generally do not allow my controls to reference other controls. In your case the RowSource in your listbox references the controls of the form it's located on. For a variety of reasons, I prefer to build my SQL statements in VBA code, particularly when they are subject to change/filtering. Another thing you might consider doing is using a Datasheet form instead of a listbox. You can set the form's RecordSource and just apply your WHERE statement to the form's Filter property then. Datasheet forms are more flexible for the user as they can resize columns and do sorting without any help from you the programmer. You can always lock the controls so they can't do any editing. When I use datasheets this way I think use the DoubleClick event to allow them to open the record, which is arguably less user friendly then using the single click on a listbox.
As of Wednesday morning ...
First, I successfully display and select from my city_table, populating the city_no on the form
I am then attempting to use that value to extract/use the city_nm and state_nm on the same form.
I coded this as the (modified) afterupdate code for city_no.
Private Sub city_no_AfterUpdate()
Dim got_city_nm, got_state_nm
got_city_nm = DLookup("[city_nm]", "city_table", "[city_no]" = "city_no")
got_city_nm = DLookup("city_nm", "city_table", "city_no='" & Me.city_no & "'")
city_nm = got_city_nm
got_state_nm = DLookup("[state_nm]", "city_table", "[city_no]" = "city_no")
got_state_nm = DLookup("state_nm", "city_table", "city_no='" & Me.city_no & "'")
state_nm = got_state_nm
seat_no = "XXX"
End Sub
Still nothing happens including the last statement (assigning "XXX" to seat_no)
So I simplified it to just try that by itself:
Private Sub city_no_AfterUpdate()
seat_no = "XXX"
End Sub
Still nothing. I'm clearly missing something simple and trivial. I appreciate all the help. I've been working on this in my spare time as a personal project, using Access/VBA for the first time. I have about 4 hours into this project trying to learn it from public sources.
Open the VBA window and click in the left margin of the code. It will put a red dot in the margin. This is called a "break point". Run your form and make your change, and the code will stop where the break point is and highlight that line in yellow. To continue running through the code, press F8. This will run the lines of code one at a time and allow you to see exactly what's being processed.
When you get to each line of code, "hover" your mouse over the variables from the current and previous lines to see what the application THINKS the variable is equal to. You may find that what you think and what the app thinks are two different things at times.
If the code jumps over a bunch of lines and goes straight to the End Sub, you've got an error on the last line it highlighted.
Using this methodology is PARAMOUNT to understanding how VBA works. I've been working with it for 20 years and I still do this when I can't figure out what's wrong. See if this helps you answer your own question. If not, hopefully it will at least pinpoint a line of code that's not working properly and you can come back and ask a specific question about it.
EDIT: A quick look at the code makes me wonder something; is City_No a NUMBER or a TEXT? Because if it's a NUMBER, you want to get rid of the single quote around the variable. Text needs single quotes, numbers don't.
FURTHER EDIT: I think you need something more like this:
Private Sub city_no_AfterUpdate()
Dim got_city_nm, got_state_nm
got_city_nm = DLookup("city_nm", "city_table", "city_no='" & Me.city_no & "'")
Me.city_nm.Value = got_city_nm
got_state_nm = DLookup("state_nm", "city_table", "city_no='" & Me.city_no & "'")
Me.state_nm.Value = got_state_nm
seat_no = "XXX"
End Sub
If you're trying to fill in the value of a control (i.e. textbox), you need to reference it with "Me." (if it's on the same form) or "Forms!MyFormName." (if it's on another form).
I am using an Access2010 project as frontend, referring to a MS SQL Server 2008 as backend. Within my Access project there is form frmKlientenÜbersicht. This form has a view abfKlientenÜbersicht as dataSource.
Now I am trying to get the current number of records showing up in my form by using this code:
Private Sub Form_Current()
Debug.Print "Form_Current: " & anzahlDatensätze
lblAnzahlDatensätze.Caption = anzahlDatensätze & " Klient(en)"
End Sub
Private Function anzahlDatensätze() As Integer
Dim rs As Recordset
Set rs = Me.RecordsetClone
rs.MoveLast
anzahlDatensätze = rs.RecordCount
End Function
This works fine until I am using some filters. If I am using any filter on my form, the number of records stays unchanged!
What do I have to change to get the current number of records showing up (if filtered or not)?
What is the reason why my code does not show the correct number of records?
EDIT: According to the given comments and answers I tried setting Count([pkKlient] onto a textbox (see new pic) and tried DCount("*", "abfKlientenÜbersicht", me.Filter) from within VBA Code.
Unfortunatelly it seems that the filterClause is not valid when using it as parameter value for DCount. (see pic for filterClause).
As you can see count(..) does not result in a correct number - and the filterClause (generated by access!!) seems not to be valid for use by DCount(..)
If someone wants to try it, on your own, just create an ADP, add a form, add a view, form dataSource is a view, set a filter, and try to get the number of records?!!
Looking forward for any comments/answers/hints!
with VBA, DCount will give you what you need
DCount("*", "MyTable", Me.Filter)
If you want to put this on the form, there's an easier way. use an unbound box, and set it to =count([FieldName])
This count should remain correct, regardless of if it's counted filtered records or not.
Some notes, there are a dozen things that could go wrong with this, it could hardly even be called tested. However, it was returning the correct count for me.
Apparently, the form filter just hides records, whereas this will apply a real filter. However, you need to get the format into the right shape for a valid filter. In the end, a WHERE statement would probably be easier.
Private Sub Form_ApplyFilter(Cancel As Integer, ApplyType As Integer)
With Me.Recordset
''Filter
If ApplyType = 1 Then
''Very, very roughly. Remove form name, use single quotes
''You will need a lot more code for safety
sfilter = Replace(Me.Filter, "[" & Me.Name & "].", "")
sfilter = Replace(sfilter, """", "'")
.Filter = sfilter
MsgBox "Recordset : " & Me.Recordset.RecordCount & vbCrLf _
& "Filtered : " & .RecordCount
Else
''Remove filter - ApplyType 0
.Filter = ""
End If
End With
End Sub
Additional note with similar caveats
You can also set a textbox to something on these lines:
=IIf([FilterOn]=True,DCount("id","ATable",
Replace(Replace([Filter],"[" & [Name] & "].",""),"""","'")),Count([id]))
(Remove the break in the line, it is cosmetic)