In an Access database, I have a split form. The datasheet is based on a query.
The table that is queried sometimes grows in fields.
This field is in the query, but not in the form because it has to be dragged from the "available fields" on design view on access into the form.
Is there some code that will allow a field to be added to a form when a button on the form is pressed instead of the user going into design view?
This would be more convenient than having every user opening and editing (possibly breaking) the database in design view.
As you can see from the comments, similar questions have been asked and so the default answer is that even with VBA code the form must be in design mode to add new controls - you cannot do this in Form View. But despite the limitations of Access there is still often a perfectly reasonable need that still requires a useful solution.
One potentially cluttered and limited solution is to add hidden unbound controls (i.e. ControlSource property is blank) that can then be bound and shown during Form View. First, during design time add hidden, unbound TextBox and Label controls to the form. Here is some template code within a button click event handler.
Private Sub cmdAddFields_Click()
Dim rs As Recordset2
Set rs = Me.RecordsetClone
Dim fld As Field2
...
'* Determine which new fields need to be shown
set fld = rs.Fields(missingFieldIndexOrName)
...
Me.txtCustom1.ControlSource = fld.Name
Me.lblCustom1.caption = fld.Name
Me.txtCustom1.Visible = True
Me.lblCustom1.Visible = True
...
End Sub
It should be apparent that you would be limited to the number of these dummy fields that you have added, and it'll require some clever way of determining what your missing fields are but that could be done by looping through the rs.Fields collection and the form's Control collection to match names (for instance).
Related
I'm trying to optimize the performance of my access frontend. One of the problems I'm stumbling on is how to set a multi-window form to open immediately to a specific record, because at the moment it queries everything twice.
Essentially, I allow my users to open multiple instances of a form. That way, a user can compare multiple records by placing the windows of those forms side by side.
At the moment, my code looks like this:
Set frm = New Form_Name
frm.RecordSource = "select * from Table where id = " & ID 'ID is a variable passed to the method
I'm pretty sure back then this question was one of the building blocks I relied on.
The problem is that on the first line, access already entirely opens the form and does everything the form does when opening, such as Form_Open, Form_Current, and loading subforms. Then, when I set the recordsource, it does all (or most) of that again, which significantly slows down the process of opening the form.
Here are some of the things I've tried:
Changing the Form_Current to recognize that it's being "used" twice and only actually run the code once. The problem is that the code is already very optimized and doesn't seem to be the bottleneck, so this doesn't seem to do much. Actually opening the form seems to be the bottleneck.
Trying to change the properties of the original form so that it opens the specific record I need, but I can't seem to change the properties without it opening the form.
Looking for a way to supply arguments to the form. That doesn't seem to be supported in VBA.
One other idea that popped into my mind was creating a variable in vba with the ID of the record, setting recordsource in the form properties to fetch that variable, but then, when I would open another form and change that ID, the form
I realize that I can do this with DoCmd.OpenForm, but that doesn't give me the option to open multiple instances of the same form.
How can I achieve this? This would mean a significant performance improvement.
OK, so I actually wanted to ask this question. But just before I hit the submit button, I had an idea...
Here's the idea: you set a global variable in VBA, which you then access in the form filter command. In my case I just added Public OpeningID As Long at the top of my VBA module. Then I create a function to get that value:
Public Function getFormID() As Long
getFormID = OpeningID
End Function
Then, in the form, you can set the filter criteria as ID = getFormID(). And then when you open the form instance, you do it like this:
OpeningID = ID
Set frm = New Form_Name
OpenindID = 0 'reset this to make sure that if it's being accessed when it shouldn't, it generates a clear error
The only problem is what happens when you refresh the form, as that will call the method again. When you refresh the form via VBA, you can set the OpeningID beforehand, and to avoid users refreshing the form, you can add this to your form (don't forget to turn on Key Previews in the form settings):
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyF5 Then KeyCode = 0
End Sub
Bang. Forms open almost twice as fast. Awesome.
I have an unbound textbox to accept the delete older than: number of days. It is in the report header. I set it to 30 days but I want the user to be able to change it. I was banging my head trying to figure out why entering 40 was not being accepted and it reverted back to 30 every time. I finally decided on using the lost_focus event to set .value to .text. That worked.
Further research showed that when the textbox get's focus text and value are both the same, 30 in my case. Changing the number in the text box to 40 shows the values of text at 40 and value at 30. Unless I specifically set Value to the value of text Access changes text to the value of value. This is different behavior than other places in Access such as forms.
Can anyone tell me why this might be? I can't find any setting that might do this. Is it because it's in a report header? what is the difference between this and every other text box I've ever used?
From a "best practices" viewpoint, Access Reports are not intended to be used interactively despite the ability to manipulate some unbound controls. Although workarounds can be implemented that function sufficiently well, such solutions are often incomplete and buggy and function differently depending on the active view: Report View vs. Print Preview. Appropriate design patterns include using Access Forms for specifying report options which then open the Report in a static configuration.
This may not satisfy the question "Why?" if seeking a deeper answer as to why Microsoft implemented inconsistent binding behavior in Access, or why they allowed interactive controls in reports at all if they don't behave the same way as in forms. But Access has plenty of other quirky behaviors that have no known/published explanation.
Regarding the priority of the Value property updating the Text property (and not vice versa): Value is the key field because it contains the actual data for the control (bound or unbound). Although it is natural to have a single control for both display and input (uh, that's how almost all controls work), the processes of displaying data and parsing user input are two distinct functions. The visual representation returned by the Text property can be manipulated using the various formatting properties, and technically could display an incomplete representation of the underlying Value data. If there are any conflicts between the stored Value property and the Text property, it is natural that the existing Value property has precedent.
My guess is that the automatic binding behavior was "relaxed" for reports to allow more flexible custom reporting output. First consider an Access Form in Datasheet view: An unbound Form control shows the same value for all records. Even if the control is edited while on a particular row, the updated value is displayed for all rows. The same control object is essentially repainted for each row and there is no concept of individual instances of the control that can hold different values. Bound controls have built-in code that repaint the control with data from the particular row, but there are still not multiple instances each "holding" the individual values. The visual output differs from an intuitive object-oriented paradigm where our minds what to assign each visual row its own in-memory instance of the controls--it just doesn't work like that in Access.
Unlike the Form behavior just described, the Report's Print Preview (and actual printed output) allows unbound controls to display different data per row using the Detail_Format() event. Within the Detail_Format() event, one can set the Value property of a control at which time the Text property is automatically updated according to various formatting properties. This update Text is then output for the current row. Perhaps (just guessing) that this behavior would not function properly if the Text property updated the value property. I suspect it would cause recursive events during report generation. Because reports are not meant to be interactive, relevant text-input parsing code was "disconnected" so that it doesn't behave like on a form.
All that explanation doesn't make Access any less frustrating nor remove its limitations, but at least learn to adapt and design things in the "Access-esque" way rather than fighting it.
your best bet is to design a form with the unbound combo boxes and have your data displayed in a subreport. I like to design my reports so that when values are updated the query for the recordsource of the report is generated doing this requires 2 queries to exist, one with all data possible and a filtered one as subreport recordsource. This will control the data for printing and also allow users to close or navigate away from the report and return to the data later.
Private Sub ComboBox1_AfterUpdate()
Dim Query1 as Object
Dim Temp_Name as Variant
Temp_Name = SubReport.SourceObject
SubReport.SourceObject = Empty
Set Query1 = Me.Form.Application.DBEngine.Workspaces(0).Databases(0).QueryDefs ("SubReport_Query")
Query1.SQL = "Select * Unfiltered_Query WHERE Field1 <= " ComboBox1 & ";"
SubReport.SourceObject = Temp_Name
End Sub
I've built an unbound form which allows a user to select from two combo boxes that contain related information (Zone and Watershed Unit -- each Zone contains multiple watershed units) in order to see what regulations apply to each. Based on these selections (stored in txtZone and txtWU on the main form) a subform would show the existing regulations (sfrmRegsbyZWU based on qryRegsbyZWU). This serves as a reference for the user, who picks a new regulation to add from another subform, clicks a button, and the selection is added to the regulations for the Zone/Watershed Unit selected. I had this successfully built with a workaround in the OnCurrent event of the subform (which was undesirable because the user couldn't click on a record and delete it) and everything worked until I added inserted code to change the query definitions for the query which is the basis for the subform as opposed to having it in the "On current" event of the subform.
At that point the requery syntax in the main form which had previously worked stopped working. This is in all embedded in a navigation form in Access 2010, so the previously working syntax was:
Forms!frmNav!NavigationSubform.Form.sfrmRegsbyZWU.Requery
I've tried every permutation of Requery I can think of, and I can not get it working. The underlying query has been changed, but the form doesn't update to reflect it. Can anyone explain to me what has gone wrong, and how to fix it? The code (attached to the _After Update() event of the combo boxes) is:
Private Sub cboZone_AfterUpdate()
'Changes WU combo box as well as the underlying text boxes
Me.cboWU.SetFocus
Me.cboWU = ""
'Blank out other selections
Me.txtWU.SetFocus
Me.txtWU = ""
'Update text box with combobox selection
Me.txtZone.SetFocus
Me.txtZone = Me!cboZone.Column(0)
'Change the query underlying the Existing Regs panel (frmRegsbyZWU, qryRegsbyZWU) to reflect selection
Dim strZSQL As String
Set qdfZ = CurrentDb().QueryDefs("qryRegsbyZWU")
Dim myZVar As Variant
myZVar = Forms!frmNav!NavigationSubform.Form.txtZone
'MsgBox (myZVar)
If IsNull(myZVar) Then
strZSQL = "SELECT * FROM tblRegulations WHERE FALSE"
Else
strZSQL = "SELECT * FROM tblRegulations WHERE Zone_No=" & Forms!frmNav!NavigationSubform.Form.txtZone
End If
'MsgBox (strZSQL)
qdfZ.SQL = strZSQL
'DoCmd.OpenQuery ("qryRegsbyZWU")
Forms!frmNav!NavigationSubform.Form.sfrmRegsbyZWU.Requery
Thanks in advance for any help!
I want to create a form in MS Access 2003 that lets the user pick from any existing query, and then have it display the results inside the form (as a sub-form in DataSheet view). The user will then be able to select one or more records and click a button on the parent form to do certain actions based on the selection. I want it to be able to work with any query, with very few limits, and display the full results of the query (all columns). The only requirement I might have is that it include certain fields for certain actions. For example, if I have a "send email" action, the query will require a field named "email", or maybe "to" and "subject".
Changing the DataSource of the DataSheet sub-form at run-time isn't a problem, I've done that before using VBA. Getting the columns displayed to change is the problem.
In a .NET WinForms app this could be done with the "auto generate columns" on a GridView control, or using the GridView.Columns collection directly in code. In VBA I don't see a way to add/remove columns from a DataSheet view. I also don't see a way to auto generate them based on the query. It appears the columns are controlled by the controls placed on the form (in form view), and while it is possible to add/remove controls using VBA, the form would have to be placed in Design View and require exclusive access to the database -- sounds very messy and I would like to avoid the exclusive access part.
Am I missing something? Is there an easy way to do this?
Here's how I would go about it. Create a blank subForm control on your main form. To change the source and the columns just leave the source object blank, then when you set it with code, the columns will reset to whatever source you use. So set it like so:
Private Sub setSource()
Me.subForm.SourceObject = "Query.myQuery"
End Sub
Then to get the selected items, assuming you know what column you want, you would do something like this:
Private Sub getSelected()
Dim rs As Recordset
Dim f As Form
Set f = Me.subForm.Form
Set rs = f.RecordsetClone
Debug.Print f.SelTop
rs.MoveLast
rs.MoveFirst
rs.Move f.SelTop - 1
Debug.Print rs!ID
End Sub
If you don't know the column explicitly you can use this to loop through the columns of the selected item and run some analysis on each name until you determine it's the column you want.
Dim i as Integer
For i = 0 To rs.Fields.Count - 1
Debug.Print rs.Fields(i).Name
Next
I'm currently creating a WinForm in VB.NET bound to an access database.
Basically what i have are two forms: one is a search form used to search the database, and the other is a details form. You run a search on the searchForm and it returns a list of Primary Keys and a few other identifying values. You then double click on the entry you want to view, and it loads the details form.
The Details form has a collection of databound controls to display the data: mostly text boxes and checkboxs. The way i've set it up is i used the UI to build the form and then set the DataBindings Property of each control to "TblPropertiesBindingSource - " where value name is one of the values in the table (such as PropertyID or HasWoodFloor).
Then, when you double click an entry in the searchform, I handle the event by parsing the Primary Key (PropertyID) out of the selected row and then storing this to the details form:
Note: Detail is the details form that is opened to display the info
Private Sub propView_CellDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles propView.CellDoubleClick
Dim detail As frmPropertiesDetail = New frmPropertiesDetail
detail.id = propView.Rows(e.RowIndex).Cells(0).Value
detail.Show()
End Sub
Then, upon loading the details form, it set's the filter on the BindSource as such:
TblPropertiesBindingSource.Filter() = "PropertyID=" & id
This works great so far. All the controls on the details form will display the correct info. The problem is updating changes.
Scenario:
If i have the user load the details for say, property 10001, it will show a description in a textBox named descriptionBox which is identical to the value of the description value of for that entry in the database. I want the user to then be able to change the text of the text box (which they can currently do) and click the save button (saveBut) and have the form update all the values in the controls to the database.
Theorectically, it should do this as the controls are DataBound, thus i can avoid writing code that tells each entry in the database row to take the value of the aligned control.
I've tried calleding PropertiesTableAdapter.Update(PropertiesBindingSource.DataSource), but that doesnt seem to do it.
Ok, I was able to figure this out picking apart some code I pillaged from a friend.
Everything was ok, the problem was the updating
When I was saving the data, i was calling just:
Me.TblProptertiesTableAdapter.Update(Me.TblPropertiesBindingSource.DataSource)
The correct code, without changing anything else is:
Me.Validate()
Me.TblPropertiesBindingSource.EndEdit()
Me.TblPropertiesTableAdapter.Update(Me.RentalPropertiesDataSet.tblProperties)
Me.RentalPropertiesDataSet.AcceptChanges()
Where RentalPropertiesDataSet is the database where TblProperties comes from. Inorder for this to work, make sure TblPropertiesBindingSource.DataSource is RentalPropertiesDataSet.Properties This was autosetup for me by VS08 when it created the BindingSource.
Basically, I needed to tell teh BindingSource to stop allowing the fields to be edited. Then we save the changes to the database, and lastly we tell the DataBase to accept the changes.