Filter on Subdatasheet - ms-access

In Access 2003, I have form with a a datasheet and an embedded subdatasheet. I would like to apply filters to both the main and sub datasheets. First, I build a filter string for the main datasheet strWhere, and one for the sub datasheet sdsWhere. Next, from the From object of the main datasheet, I set .filter=strWhere. Then, I set a filter on the subdatasheet's Form object. Basically:
With Me.Controls(dataSheetName).Form
.Filter = strWhere
.FilterOn = True
With .Controls(subsheetname).Form
.Filter = sdsWhere
.FilterOn = True
End with
End with
The problem is that the subdatasheet is only filtered for the first record of the main datasheet. Looking at the RecordSet of the sub datasheet, it only contains the child records of the first record in the main datasheet. I cannot find the other records anywhere.
I know that it should be possible to filter all subdatasheet records, because from the Access interface, placing the cursor in the desired value, and selecting Records>>Filter by Selection has the desired effect. (Except that records in the main form with have empty subdatasheets show, and I would like them to not show)

Use a query. Make a query joining both the Main and the Sub datasets. Filter on the query. I don't really see any other efficient way.

I'm an idiot - figured it out, of course just move the subdatasheet's Recodset position, and then apply the filter until eof. But we need to goto the first record, first otherwise it will miss the records before the record the user is currently positioned at. And make sure that subdatasheetexpanded is true.
Dim mainDS As Form, subDS As Form
Set mainDS = Me.Controls(dataSheetName).Form
If mainDS.SubdatasheetExpanded = False Then
mainDS.SubdatasheetExpanded = True
End If
mainDS.Recordset.MoveFirst
Do While Not mainDS.Recordset.EOF
Set subDS = mainDS.Controls(sdsheet).Form
subDS.Filter = sdsWhere
subDS.FilterOn = True
mainDS.Recordset.MoveNext
Loop

Related

Access VBA get current autofilter settings

Is there a way to find out what Autofilter has been applied to a query?
I can't use a form, as the database structure is changing (I can't do anything about it). So using a form and Me.Filter is sadly out. Is there anything similar for an opened select query?
What I'm doing now, but it makes me sick:
DoCmd.RunCommand acCmdSelectAllRecords
DoCmd.RunCommand acCmdCopy
I'd actually need to run another query against the filtered query. So: pasting everything into a new table would be OK, getting filter settings as a list of conditions or an SQL statement would be better.
Any ideas?
It seems you know how to examine a form's user-applied filter, but you don't want to use a form because the database schema is still volatile, which means that your query's field set is likely to change.
In that case, create a simple container form with a subform control and use your query as the subform control's SourceObject.
Here is a simple example with a named query (qryFoo2) as SourceObject ...
? Forms!Form20!Child0.SourceObject
Query.qryFoo2
With no user filter applied ...
? Forms!Form20!Child0.Form.Filter
? Forms!Form20!Child0.Form.FilterOn
False
Then if I apply a ("starts with 'h'") filter on my query's text field ...
? Forms!Form20!Child0.Form.Filter
([qryFoo2].[text_field] Like "h*")
? Forms!Form20!Child0.Form.FilterOn
True
Neither have I heard of Autofilter, and I believe Me.Filter returns any applied filter (except in the recordsource, of course) of the form.
However, if you experience that this is not so, you should be able to use the RecordsetClone as it always returns the current records displayed in the form:
Dim rs As DAO.Recordset
Set rs = Me.RecordsetClone
If rs.RecordCount > 0 Then
While rs.EOF = False
' Loop rs and do stuff.
rs.MoveNext
Wend
End If
Set rs = Nothing

Requery only one record in a datasheet view form (MS Access)

I have a rather simple looking problem but it turned out to be more complicated than I thought.
I have a field (column) in my subForm which is a ComboBox.
I have a field (column) in another subForm by which I would like to filter this comboBox.
Basically, the comboBox before filtering has some 600 records, too many to scroll by casual user. I created a simple subForm whose field is linked to a mainForm and this works perfectly (ie. the selected record-field-ID is displayed on mainForm).
Now what I want is that this comboBox is filtered by this record (ie. only showing relevant fields). The problem is that if I simply Requery it with this given filter, the other fields show up blank.
I want this filter to apply only to NEW RECORDS comboBox, not the whole datasheet view.
What I did is:
Private Sub Sekacie_Operacie_GotFocus()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
Me.Sekacie_Operacie.RowSource = Replace(SQL, ";", "") & " WHERE Sekacie_Operacie.Operacia_ID = " & Me.Parent.Form!txtOP_ID
Me.Sekacie_Operacie.Requery
'works kinda as intended
End Sub
Private Sub Form_AfterInsert()
Me.Sekacie_Operacie.RowSource = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
Me.Refresh
End Sub
And when I select the record in my filter subForm:
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
With Me.Parent.Form.subSekacie_Operacie_Modelu
.Form!Sekacie_Operacie.RowSource = SQL
.Form.Refresh
End With
End Sub
However, this workaround still shows "blank" records sometimes (I have to refresh the form by clicking different record) and I find it strange I had to go all the way to do this. Is there no simpler way of accomplishing this?
A couple of immediate things:
No need to refresh the whole sub form, just refresh the control.
Private Sub Form_AfterInsert()
' Me.Refresh ' replace this line with the following line
Me.Sekacie_Operacie.Refresh ' or is it Requery I can't remember
End Sub
The following code can be improved
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
With Me.Parent.Form.subSekacie_Operacie_Modelu
.Form!Sekacie_Operacie.RowSource = SQL
.Form.Refresh
End With
End Sub
' by using code like this
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
me.Sekacie_Operacie.RowSource = SQL
' I think the above line will cause the combo to refresh
' anyway (in some version of accees it might not,
' but, if needed add the following line
' me.Sekacie_Operacie.refresh
End Sub
The following line has the potential to produce and error
(perhaps when the main form is a new record)
WHERE Sekacie_Operacie.Operacia_ID = " & Me.Parent.Form!txtOP_ID
replace it with
WHERE Sekacie_Operacie.Operacia_ID = " & nz(Me.Parent.Form!txtOP_ID,0)
Note that adding such code will stop the subform from working independently of the main form and will require it to have a parent form.
You could get around this by using:
if not me.parent is nothing then
WHERE Sekacie_Operacie.Operacia_ID = " & nz(Me.Parent.Form!txtOP_ID,0)
end if
About your approach.
I find it a bit weird what you are doing. Consider this:
Say the query qrySekacie_Operacie returns 100 rows when run without any WHERE clause. When you are inserting a record into the subform you want to limit the combo by adding a WHERE clause that uses a value in the parent formMe.Parent.Form!txtOP_ID) , this might limit the number of rows displayed to 10.
It is worth noting that if any of the other rows in the subform contain value other than one of these 10 the combo will display a blank value. I think this is what you are experiencing.
My first question was why not use the on current event of the parent form to change the subform combo box SQL. It appears this is not what you need.
It appears that other values can exist in the dataset of the subform and that you only want certain value to be enterable in this subform but you want the subform to display any values that have been entered.
So that gets interesting. Here's what I would do:
Create a hidden combo control (with no control source) that is only visible when a new record is being entered. When this new combo is shown the other one is hidden.
Set up this new combo with the required SQL
use a before insert event to copy the value form the new combo to he old one.
Easy peezy?
Let me know ho you get on.
One other thing
I like to Ensure a subform encapsultes code that operates on it "private" controls.
As I general rule of thumb, where possible (which is 99% of the time), I like to try and keep subforms so that they will work as forms in their own right. They can be though of as an object, so if they work independently, it is an indication that their code has been encapsulated within their own code module.
So if the form has me.form.parent = nothing then the code behaves differently as it is a independent form.
So rather than the parent form code module having code that sets the SQL of the subforms combo, I would write a public sub in the subforms module (called SetComboSQL) that can be called (with the appropriate parameters. ie OPid ) to set the combo SQL.
To call the procedure in the parent form I would add
Private Sub Form_Current()
Me.MySubformControlName.Form.SetComboSQL(OPid:=Me.txtOP_ID)
End Sub
' note that intellisense won't pick up this proeprty BUT it will work. Use debugger to check it out.
I hope you find this tip useful (as it can be a massive headache saver. THE MOST BASIC OO PRINCIPAL is encapsulation. An object should own all the code that effects it's properties (and controls).

subform combobox rowsource update - how update drop down list

I have a treeview control on an MS Access form. A subform combo box control's list of values depends on the node selected in the treeview on the main form.
I am trying to achieve refresh of the combo box drop down list contents on the subform as follows:
Public Sub TreeView1_nodeClick(ByVal node As Object)
subForm.Controls("Bid").RowSource = "... newquery depending on tree node values ..."
subForm.Controls("Bid").Requery
End Sub
But oddly enough, this does not update the list of values.
A second click on the same node does update the list to the expected values; when another node is clicked the list again is wrong (it contains the list related to the next to last assigned rowsource, instead of related to the last one assigned).
Is there some delay in activating the combo box' rowsource?
What is the solution for this obnoxious problem?
OK found a solution for this myself - I don't particularly like it (*), but it does work, so I regard it as a temp workaround, until somebody else provides a solution using the royal route (setting rowsource and somehow make this have the desired effect).
I checked msdn site and found that it is also possible to use a self-defined function for the combobox list population. The easy part of this is the specification of the fact that Access will have to use that function: just enter the function name in the row Source Type property of the combo box, just the plain function name, without any = in front or () behind it.
Now for the less easy part - MS requires a specific format for the function and a specific content. Mine looks as follows:
Private Function customFuncName(fld As Control, id As Variant, row As Variant, col As Variant, code As Variant) As Variant
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset(... new query ...)
Select Case code
Case acLBInitialize
customFuncName = True
Case acLBOpen
customFuncName = 1
Case acLBGetRowCount
customFuncName = rs.RecordCount
Case acLBGetColumnWidth
customFuncName = -1
Case acLBGetValue
customFuncName = rs.GetRows(rs.RecordCount)(col, row)
End Select
End Function
See msdn.microsoft.com rowSourceType property and the link specific function code arguments on it.
(*) because of the obvious recordset overhead causing performance to drop - but since the list I show is max 40 rows it works fine for me

How To Refer To Continuous Subform Contol

I have an Access 2003 form with one subform, set to continuous forms, in a subform control. For one record in the main form, 1 to many records will appear in the sub form. The data displays properly.
The main form is named Widgets and the sub form is named Transactions. There are 5 textbox controls that display the data in the subform. The one in question is ReceiptDate.
What I would like to do is look at the values and determine if there was a receipt for the year 2009, and if so then change the background of that row to yellow so it stands out when the user encounters that condition. Maybe even change the date field's font to boldface..
I tried many ways of referencing the subform's controls. When I have tried Me.Transactions.ReceiptDate I have only received the first record in that subform. I'd like to be able to loop through them and see if the condition is met. I tried Me.Transactions.ReceiptDate(1) and Me.Transactions.ReceiptDate(0) and so forth.
I tried the For Each ctl In Form.Controls route as well. It worked for a few iterations and then I received a run-time error 2455 "You entered an expression that has an invalid reference to the property Form/Report".
I had the subform in "datasheet" mode but thought that was causing me not to be able to read through an array of subform controls. So I changed it to "continuous" mode. I get the same errors for either.
Is there a way to reference specific "rows" in the subform and do something based on a value found? Also, I am performing this in the On Current event as I dont know where else to put the code. The subform loads before the parent form so its possible that these controls arent even fully "there" but then I do get the first row's date when I try it in the Immediate Window.
UPDATE 12.23.2010:
With the gracious help of #Remou I am able to debug.print the ReceiptDate fields from the RecordSet. This is great because now I can evaluate the data and do certain things based on the values.. The #Remou's code helped me put this into the OnCurrent event:
Dim i As Long
Dim frm As Form
Dim rs As DAO.Recordset
' Get the form and its recordset.
Set frm = Me.Form
Set rs = frm.RecordsetClone
' Move to the first record in the recordset.
rs.MoveFirst
' Move to the first selected record.
rs.Move frm.SelTop - 1
' Enumerate the list of selected records presenting the ReceiptDate field
For i = 1 To rs.RecordCount
Debug.Print rs![ReceiptDate]
rs.MoveNext
Next i
So now that I am able to know which row in my subform has a receipt from 2009, I need to be able to highlight the entire row or rows as I come across them in that for loop. How can I reference the actual row? Datasheet view or Continuous Forms view - I have tried both.
Conditional Formatting is great but it only allows me to highlight one particular record and I'd much rather be able to do this via VBA because...... from here I will want to give the use the ability to click on any record in the subform and get the receipt details and potentially print them.
Any ideas?
In this situation, it is best to use conditional formatting.
To refer to a control on a subform, refer to the subform control by name, then the form property to get the form contained, then the control by name:
Me.MySubformControlName.Form.MyControl
See: http://www.mvps.org/access/forms/frm0031.htm
I have finally got it. The frm.SelTop = x will set the selected record and from there I can set the background or font style, etc.. Very cool. A simple test for 2009 and setting the selected record:
Dim i As Long
Dim frm As Form
Dim rs As DAO.Recordset
' Get the form and its recordset.
Set frm = Me.Form
Set rs = frm.RecordsetClone
' Move to the first record in the recordset.
rs.MoveFirst
' Move to the first selected record.
rs.Move 0
' Enumerate the list of selected records presenting
' the CompanyName field in a message box.
For i = 1 To rs.RecordCount
If Year(rs![ReceiptDate]) = 2009 Then
frm.SelTop = i '<-----------------------------
End If
rs.MoveNext
Next i
In order for me to get the email off the bottom of my continuous form, I used this much simpler code (as I avoided the RecordsetClone code)
Me.[email subform].Form.SelTop = Me.[email subform].Form.Count 'selects the last row
str = Me.[email subform].Form.Email 'capture the value of the last row
MsgBox str

Tagging Records in a Continuous Form

In a continuous subform, I display records based on a DISTINCT query. Because it's distinct, each row does not include a record ID.
Does anyone know of a way to add a checkbox (or similar), so that a user can select any of the records, which will then be used to create new records via code?
I prefer to use a subform to a list, as it features lots of column sorting and filtering functions.
MTIA
Depending on what you need to create the records, something like this sample may suit:
Function DisplaySelectedCompanyNames()
Dim i As Long
Dim F As Form
Dim RS As Recordset
'' Get the form and its recordset.
Set F = Forms![Customers1]
Set RS = F.RecordsetClone
'' Move to the first record in the recordset.
RS.MoveFirst
'' Move to the first selected record.
RS.Move F.SelTop - 1
'' Enumerate the list of selected records presenting
'' the CompanyName field in a message box.
For i = 1 To F.SelHeight
MsgBox RS![CompanyName]
RS.MoveNext
Next i
End Function
Further information: http://support.microsoft.com/kb/208502
FYI, I decided to use the Windows ListView OCX control, as it offers the ability to add a checkbox for each row.