I am relatively new to Access but have a little experience on VBA. I am trying to create a database of daily work checklists with various questions for different clients. A friend suggested I create a dynamic form through VBA that will create multiple questions in textboxes every time a client is selected from a dropdown box. I am trying to create textboxes in VBA but Im getting an error message saying I need to be in design mode to create text boxes.
I lifted the below code from the internet, is it possible to activate the design view once the code is running as I need to start in form view to click the drop-down or if anyone has a suggestion for another way I'd appreciate it too. Thanks
Private Sub ComClient_AfterUpdate()
Dim x As Integer
Dim frm As Form
Dim ctrl As Control, ctlText As Control, CtlLabel As Control
Dim intDataX As Integer, intDataY As Integer
Dim intLabelX As Integer, intLabelY As Integer
Dim count As Integer
count = RecordCount
For x = 1 To count
Set ctrl = CreateControl("TestControlCreate", acTextBox, acDetail, , "", 0 + (x * 300), 0, 300, 240)
frm.RecordSource = "Get_Questions"
ctrl.ControlName = "TxtBx" & x
Set ctlText = CreateControl("Question " & x, acTextBox, , "", "", _
intDataX, intDataY)
Set CtlLabel = CreateControl(frm.Name, acLabel, , _
ctlText.Name, "NewLabel", intLabelX, intLabelY)
DoCmd.Restore
Next
End Sub
Working in MS Access can be somewhat frustrating, because its version of forms, etc. is not the full-blown SDK that you got in VB6. Therefore, a lot of VB forms code you find on the Internet will not work.
The approach I would recommend is to create a form laid out (that is you this by hand) with the maximum number checkboxes you think you will possibly need, and use VBA code to programmatically hide/display them (by manipulating the visible property). You can similarly change the labels and data-bindings at runtime. This may seem like a hack, but is a valid way of accomplishing your goal.
if you have a table or query containing all the questions for a given client, you can also display a continuous subform with a hidden textbox (ClientID), a textbox (Question) and a Checkbox (answer), in order to display all questions with a checkbox.
Your 'main-form' will basically only contain a Combobox in order to select your client, then you can link the clientID of the main-form (Combobox value) with the ClientID of your sub-form and MS Access will do the rest, with no need to code your business logic.
In this way you can, if needed, also store your answers in your database, have a nice User Interface and a dynamic design with no hard-coded questions...
Related
I have a set of database on excel with multiple forms in order to add data. I have put a Switch Board in order to limit the navigation around the databases, to force people to only access the parts they are meant to.
I am attempting to implement a drop down combo box at the top of the page to select which location the user is at, and therefore filter the databases to only allow data entry and searches for the selected location, but I cannot figure out how to do it.
Any help would be much appreciated.
Cheers.
Could you please elaborate a bit more what you are trying to achieve? I am a bite stuck to be honest.
What do you mean with "location".
When you speak of "databases", do you mean tables or in fact (I suppose external, linked) databases?
"a set of database on excel", I suppose you mean access?!
Anyway, let me try to shed some light on your issue.
Limiting user access is usually defined by limiting the access to forms.
Using buttons for navigation, this can easily be achieved by calling:
Private Sub Testbutton_Click()
If Test_Condition = TRUE Then
DoCmd.OpenForm "test_form"
Else
DoCmd.OpenForm "test_form", , , ,acFormReadOnly
End If
End Sub
Or - which might suite your "locations" a bit better
Private Sub Testbutton_Click()
Select Case Me.Location
Case 1,3,6,8
DoCmd.OpenForm "test_form"
Case 2,4,5,7
DoCmd.OpenForm "test_form", , , ,acFormReadOnly
Case Else
MsgBox "You can't access the selected form using that location"
End If
End Sub
Here, for the selected location 1, 3, 6, and 8, the form is opened in the standard view with all rights to add and edit entries.
For the selected location 2, 4, 5, and 7, access will open the form in ReadOnly-View.
For all other locations, it will prompt a message.
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.
My management wants an access database that can have a way to control which users can see specific fields on a form. For example, if a manager logs in, a form will display the performance rating field. For any other user, the performance rating field will not be visible.
So far the below are some of my options:
1) Use VBA to detect the User Name of the access application and if its Manager's name, then the textbox is visible.
2) Use a username reference table that requires users to login. Users with special access will have the textbox visible.
3) Have a special little button on the form that, if someone clicks, will load a small password dialog and then set the text visible.
Which option would be the most difficult to implement?
The idea of using a Login form for user specific information is something that is available all over the internet. I would suggest a combination of all three of your ideas would be just the perfection solution.
For either of the two methods you have (1 & 2) you first need a Table that will hold the information. The table does not need to be complex (at least not right away). The table should be of the following structure,
tbl_Employees
-------------
EmpID - Auto Number - PK
EmpNam - Text
EmpUserName - Text
EmpPassword - Text - Input Mask (If required)
IsManager - Yes/No - Default - No
Then you will have to create a Form, a basic form that will have three controls, two Text Boxes and one Button. The first text box is where you will enter the Employee User name and the second for Password. Finally a button, behind which the magic happens. The code (simplified) behind the button would be something along the lines of.
Private Sub LogInBtn_Click()
On Error GoTo errOccured
Dim eQryStr As String, empRS As DAO.Recordset
eQryStr = "SELECT EmpID, EmpUserName, EmpPassword, IsManager FROM tbl_Employees WHERE EmpUserName = '" & Me.UserNameTxt & "'"
Set empRS = CurrentDb.OpenRecordset(eQryStr)
If empRS.RecordCount <> 0 Then
If Me.PwdTxt = empRS!EmpPassword Then
If empRS!IsManager Then
DoCmd.OpenForm "ManagerForm"
Else
DoCmd.OpenForm "EmployeeForm"
End If
Me.Visible = False
Else
wrongEntry "Password"
End If
Else
wrongEntry "User Name"
End If
exitOnError:
Set empRS = Nothing
Exit Sub
errOccured:
wrongEntry "User Name/Password"
Resume exitOnError
End Sub
Private Sub wrongEntry(entityStr As String)
MsgBox entityStr & " is incorrect (OR) omitted, please check again.", vbCritical
End Sub
As you can see, I have used
(a) A Recordset object than a simple DLookup. I prefer recordset object, you can use a DLookup, but you have to make sure you handle Null (if the criteria is not met).
(b) A separate Form for Managers and Employees. I imagined there would be a lot more on a managers form that is not available on a Employee form. If you do not wish to go this way, but use one form you can, but you need to inform the opening form of who is logging in - Using the OpenArgs property of the OpenForm method.
Just if you feel to simply avoid all the hassle and use an Password box, to get the access of the TextBox. Simply follow the instructions on this thread - Password Box - A variant of Input Box. Then create a button on the form you currently have, then on click of the button, you simply code the following.
Private Sub AllowAccessButton_Click()
If Call_Password_Box = "yourPassword" Then
Me.yourHiddenTextBox.Visible = True
Else
MsgBox "Sorry you are not authorised to vies this information (OR) Incorrect Password", vbCritical
End If
End Sub
PS: The Hidden text control should be set to invisible, preferable in the Form current event.
I hope this helps.
I've implemented option nr 1 and 2, they we're quite easy to build.
option nr 3 seems equally difficult.
The question within my work environment would be which option would be low in (account)maintenance and which would demand little effort from the users.
From that view option nr.1 has been more successfull.
(And i would rather build different forms in stead of turning the view setting of field ON/OFF)
Okay, friends, I'm leaving my job in a week and a half, and I'm trying to make what I've done easier for my boss to do. He has no access knowledge, so I'm trying to create a form that will automate the reports I've been generating. Rather than create a different form for all the different reports, I'm trying to automate it from a table of parameters. Here's what I'm going for:
I have a table, which I have created, which is comprised of 5 fields. I'd like to use these fields to fill parameter fields in a standard form template. The five fields in my table are as follows:
The type of query being run (the result spit out)
The queries that generate this report, separated by a comma and no space. "QRYNAMEA,QRYNAMEB"
The Table which these queries generate, which will be used by transferspreadsheet
The destination excel file, which already has a pivot table set up to feed of the data.
The input sheet of this excel file. Currently, all of these sheets are called "Input". (that isn't important)
My issue comes with having no idea where to go after I've made my combo box. I know enough visual basic to automate my queries, but not enough to populate the form with the information in 3,4 and 5 (so far, I've been manually changing these for different queries). I have no idea how to look up the record in the table from the choice in the 'choosebox', and then select individual fields from that in my automation.
I'm pretty confident in my ability to parse #2 and automate the queries, and to put the values into the fields I'm looking at, but I don't know how to actually pull those values from the table, before I can do these things. I also can't seem to describe this well enough for google to help me.
Has anyone done something like this before? I'm assuming I just lack knowledge of one of the VBA libraries, but I've not had any luck finding out which.
edit:
my inclination at this point is to create a query for this table, which will return a single field depending on the input I give. I can imagine doing this in SQL, but I still don't know how to populate the forms, nor extract the field object from the table once I get it.
I have to head out for the day, but I'll be back on Friday to keep working on this, and I'll post my solution, once I find it. This seems like a unique conundrum, and it would be nice to give an answer to it.
Final edit: code is polished (does not have much in the way of error handling):
The first method, which pulls the fields from the table and populates the form, is activated by choosing a new entry in the combo box and looks like this:
Private Sub QuerySelect_Change()
Dim db As Database
Dim rec As Recordset
Set db = CurrentDb
Set rec = db.OpenRecordset("SELECT [Queries to Run], [Source Table], [Destination Spreadsheet], [Destination Sheet Name] FROM TBL_QRY_SETTINGS WHERE TBL_QRY_SETTINGS.[Query Type] Like '" & [Forms]![QuerySelector]![QuerySelect] & "';")
[Forms]![QuerySelector]![QueriesToRun].Value = rec("Queries to Run")
[Forms]![QuerySelector]![SourceTable].Value = rec("Source Table")
[Forms]![QuerySelector]![FileDest].Value = rec("Destination Spreadsheet")
[Forms]![QuerySelector]![SheetName].Value = rec("Destination Sheet Name")
Set rec = Nothing
Set db = Nothing
End Sub
The second code pulls that data to run the query. I like how this turned out. It runs when a button near the combobox is clicked.
Private Sub DynamicQuery_Click()
Dim qryArray As Variant
Dim i As Integer
qryArray = Split([Forms]![QuerySelector]![QueriesToRun], ",")
DoCmd.SetWarnings False
For i = LBound(qryArray) To UBound(qryArray)
Debug.Print qryArray(i)
DoCmd.OpenQuery (qryArray(i))
Next
DoCmd.SetWarnings True
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, [Forms]![QuerySelector]![SourceTable], _
[Forms]![QuerySelector]![FileDest], _
True, [Forms]![QuerySelector]![SheetName]
End Sub
Note that the final code for part (1) is almost the same as the selected answer, except that I am grabbing more than one field. This works because I know that I have unique "Query Types", and my recordset will only contain one record.
Anyway, I hope some people stumble upon this and find it useful. Send me a message if you do. As far as I can tell from brief googling, this sort of automation work has not been done in access. It should make it easier for access-illiterate to run their own queries, and be simple for designers to add to, if they want all their queries available after a few clicks.
Someone could conceivably use this to automate a variety of reports in sequence, by iterating through a table like the one I reference.
I may be massively misunderstanding what you're doing, but I think it's as easy as creating a new form using the form wizard. It will let you choose the table that contains the data, and it will let you choose which fields you want to add.
You can later change any of the textboxes to combo boxes which will allow you to limit the choices available to fill in.
Am I understanding that correctly?
EDIT: This will fill a variable (MyRandomField) with the contents of a field in a table
Dim db as Database
Dim rec as Recordset
set db = CurrentDB
set rec = db.OpenRecordSet("Select SomeField from SomeTable Where Something = 'SomethingElse'")
MyRandomField = rec("SomeFieldName")
set rec = Nothing
set db = Nothing
I was initially very pleased to discover the attachment field in Access 2010. It's a feature that aesthetically irks my inner database purist but my inner lazy sod is in charge here and it does look, on the face of it, like it could make one of my current projects much easier/simpler. Happily it displays pictures/icons automatically on the forms and reports but (why is there always a but eh!) it only displays the first one and I need it to display all of them.
You can of course scroll through the attachments one at a time but I'm pretty sure my client won't wear that, despite his request that I complete the project in MS-Access which, seemingly, only has very rudimentary built in options for display :/ BUT...
I may well be wrong, I've got almost no MS-Access experience. My coding background is firmly LAMP stack and web so I'm deeply ignorant of what's on offer in the Windows/Access ecosystem. I suspect there are excellent 3rd party reporting tools that give very flexible layout but I need to see all the attachments on the form, not just the reports.
So, blundering blindly into the void my initial strategy is this...
Create a separate table for attachments where each field is an "attachment" containing a single item only. Then use scripting in the forms and reports to...
Query that table for all attachments belonging to the record in question
Display/Format those fields as some sort of list
Dynamically append a fresh attachment field to the end of that list so the user has somewhere to upload a next attachment
Make the form page refresh whenever an attachment is added so there's aways a free one.
So, my questions are...
Is what I describe feasible in Access?
Am I missing a much simpler / better / canonical solution?
How powerful is Access's scripting language(s) with reference to display? i.e clunky or pixel perfect?
It's not still Visual Basic is it? (noooooo! ;)
If so are there any other scripting languages I can use within forms/reports?
Sorry, I know it's a bit of a long wooly question but I'm a fish out of water here!
Thanks,
Roger
Let us say I have a table with an attachment:
Let us say that I have three images in one of those attachment fields that I wish to display. I can create a query:
After which I can create a continuous form:
I searched this for a similar problem. I have multiple attachments per field. I intend to use the field to store a .jpg and a .pdf for two image controls which I created by dragging the field from the properties to the form. I have named them ctlImage and ctlFeatures. I am using this form as a non modal popup from a more info button on the product catalog form. So far I am able to search records in the product catalog and use the selected listbox search result to open the form with a where clause to set the details form to the current record. When I try to manipulate the ctlImage looking for image type the only property which shows in intellisense is ctlImage.value. I was hoping to assign ctlImage to jpg and ctlFeatures to pdf Here is the frmProducts btnMoreInfo_Click code and the frmCatalogDetails code in case one is messing up the other.
Private Sub btnMoreInfo_Click()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim str As String
Dim row As Long
Set db = CurrentDb
Set rs = db.OpenRecordset("tblProducts", dbOpenSnapshot)
If Me.lstSearchResults.ItemsSelected.Count > 0 Then
' Debug.Print Me.lstSearchResults.Column(0)
row = Me.lstSearchResults.Column(0)
rs.MoveFirst
rs.FindFirst "ProductID=" & row
Debug.Print rs("Description")
DoCmd.OpenForm "frmCatalogDetails", acNormal, , "ProductID=" & Me.lstSearchResults.Column(0, Me.lstSearchResults.ItemsSelected), acFormReadOnly, acWindowNormal
Else
MsgBox "You must select a product"
Exit Sub
End If
End Sub
and Catalog details
Option Compare Database
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim fld As DAO.Field
Private Sub Form_Load()
Set db = CurrentDb
'Set rs = db.OpenRecordset("tblProducts", dbOpenSnapshot)
'Set fld = rs("image")
Debug.Print rs("ProductID")
Debug.Print rs("Description")
'me.ctlImage.ControlSource =
'Me.ctlImage.CurrentAttachment = fld.Value
End Sub
The rs statements are commented out because I am having trouble with this form recognizing the rs from the parent form. I am getting a with block variable not set, but if I do rs.openRecordSet then the recordset goes back to the first row. Aside from your answer above, I have seen very little about manipulating the attachment object and the help has been difficult as it doesn't even cover accessing inside the attachment field. I am at a point where I will do more asking than answering on this topic, and I very much appreciate the time many of you take to craft an answer.
Rollin
Motto: Ask for help when needed, give help when asked, and remember where you came from.