Recordset and RecorsetClone do not Reflect Sorting and Filtering

O

oli

Hello,



In adp's the forms Recordset and RecordsetClone do not reflect sorting and
filtering made by the user on the form. Thus, if you try to select records
based on the SelTop and SelHight properties of the form you might get the
wrong records.



Example: Assume following RecordSource: SELECT * FROM Address ORDER BY
LastName



'Simulate different sorting done by the user

frm.OrderBy = "FirstName"

frm.OrderByOn = True



Set rs = frm.RecordsetClone 'or frm.Recordset

rs.AbsolutePosition = frm.SelTop

'==> Now the actual record should be the one the user selected on the form,
but it might differ.



Has anyone made the same observation? Has anyone a solution for this
problem?



Kind Regards



Olivier Jacot-Descombes

Switzerland
 
V

Vadim Rapp

o> In adp's the forms Recordset and RecordsetClone do not reflect sorting
o> and filtering made by the user on the form.

Sure it does not. The recordset is what comes from the database. Sorting and
filtering is obviously made by the form.

o> Has anyone a solution for this problem?

sort on the server: specify ORDER BY in the datasource.


Vadim Rapp
 
O

oli

Try using bookmark instead of AbsolutePosition.

This does not help in this case. What I want to do is to select the records
corresponding to the rows the user selected in a subform using the record
selector.

The SelTop and SelHight properties of the subform reflect the user's
selection. Example: if the user selects the 3rd and 4th row, then SelTop=3
and SelHight=2.

With mdb's it always works if you write something like:



Set rs = fsub.RecordsetClone
i = fsub.SelTop
rs.AbsolutePosition = i
Do
do something with rs ...
i = i + 1
If i = fsub.SelTop + fsub.SelHight Then Exit Do
rs.MoveNext
Loop

With adp's this code works only if not sort and no filter is defined.
Microsoft does not document this different behavior between mdb and adp. As
a consequence, one of my customers deleted the wrong records in his adp
database, i.e., he selected one record in the subform, but the delete
operation deleted another record.



By the way: There is a trick involved in using SelTop and SelHight. If you
click a button (the "Delete" button in this case) the subform looses the
focus and the SelTop and SelHight properties loose their values! The trick
is to save these values using the "Exit" Event of the subform, doing
something like:



Sub MySubForm_Exit ()
t = fsub.SelTop
h = fsub.SelHight
End Sub
 
S

Sylvain Lafontaine

Well, then, another reason to perform the filtering and sorting on the
server! This will slow down a little bit your application but it's better
than seeing your customer deleting the wrong records!

I never used myself the form's filtering and sorting functions, so I didn't
notice before this undocumented difference between ADP and MDB.

Finally, thanks for your tip about the selecting and OnExit event; I'm sure
that this will be helpful for many of us.
 
M

Malcolm Cook

oli,

Well, I was once in a similar position and wrote the function below, ACFormRecordSetCloneWithFilterSort, whose code is at the end of
this message.

However, I would up realizing that what I was trying to do could benefit from Sylvain's earlier advice, "Try using bookmark instead
of AbsolutePosition.", and I think you can too....

What you need to do is somthing like this:
1) take note of which rows in your fsub are selected (take a copy of SelTop and SelHeight)
2) step through those rows one at a time by resetting the SelTop starting at origSelTop through orgSelTop
3)

Here's some useless code that shows the approach. It works in an ADP. It presents the user with a message box of the first field
in the underlying recordset for each selected row. Modify it to your needs. It expects to work with an active datasheet. To make
it work with subforms is possible, but there are a few tricks you'll have to figure out...

Public Function HackSelection() As Boolean
' NOTE: this must be called from macro or menu or autokeys - NOT from anything that changes the focus away form the selection
On Error GoTo HandleErr

Dim frm2Hack As Form
Set frm2Hack = Screen.ActiveDatasheet

If frm2Hack Is Nothing Then Err.Raise 513, , "There is no active datasheet to hack"

With frm2Hack
If (.SelHeight = 0) Then ' gratuitously select the entire row
.SelHeight = 1
End If

If (.SelHeight = 1 + .Recordset.RecordCount) Then ' an entire column is selected and it includes the 'new' row...
.SelHeight = .Recordset.RecordCount 'so, unselect the 'new' row (since it is not a real row).
End If

Dim HackTop As Long, HackHeight As Long, HackLeft As Long, HackWidth As Long
HackTop = .SelTop
HackLeft = .SelLeft
HackHeight = .SelHeight
HackWidth = .SelWidth
End With

Dim rsHackMe As ADODB.Recordset
Set rsHackMe = frm2Hack.Recordset.Clone

Dim RowToHack As Long
For RowToHack = HackTop To HackTop + HackHeight - 1

frm2Hack.SelTop = RowToHack
frm2Hack.SelHeight = 1
rsHackMe.Bookmark = frm2Hack.Recordset.Bookmark

'<YOUR HACK CODE GOES HERE INSTEAD OF MINE'
MsgBox rsHackMe.Fields(1).value

Next

With frm2Hack ' restore selection
.SelTop = HackTop
.SelHeight = HackHeight
.SelLeft = HackLeft
.SelWidth = HackWidth
End With

ExitHere:
Exit Function

' Error handling block added by VBA Code Commenter and Error Handler Add-In. DO NOT EDIT this block of code.
HandleErr:
Select Case Err.Number
Case 513
MsgBox Err.description
Case Else
'' put your error code here
End Select
Resume ExitHere
' End Error handling block.
End Function

I DON'T recommend trying to use the following proc. The above approach should get you where you want to go. Nonetheless, to show
you what might be possible....

Function ACFormRecordSetCloneWithFilterSort(f As Form) As ADODB.Recordset
'returns a recordset as a clone of the form's recordset after
'making sort and filter agree with the forms (client side) soft and filter.
'A TOTAL HACK!?!?!? Access does not otherwise make this possible.
Set ACFormRecordSetCloneWithFilterSort = f.Recordset.clone

Dim strNewFilter As String
Dim strNewSort As String
Static re As Object
If re Is Nothing Then
Set re = CreateObject("VBScript.RegExp")
With re
.Global = True
.IgnoreCase = True
.Multiline = True
.Pattern = "\w+\."
End With
End If

With f
If .FilterOn Then
strNewFilter = .Filter
With re
strNewFilter = .Replace(strNewFilter, "")
End With
strNewFilter = Replace(strNewFilter, " ALIKE ", " LIKE ")
strNewFilter = Replace(strNewFilter, "*", "%")
strNewFilter = Replace(strNewFilter, "?", "_")
strNewFilter = Replace(strNewFilter, Chr(34), "'")
ACFormRecordSetCloneWithFilterSort.Filter = strNewFilter
End If
If .OrderByOn Then
strNewSort = .OrderBy
With re
strNewSort = .Replace(strNewSort, "")
End With
ACFormRecordSetCloneWithFilterSort.Sort = strNewSort
End If

End With

'
'Bye_ACFormRecordSetCloneWithFilterSort:
' ' Return the result.
'
'Exit Function
'
'Err_ACFormRecordSetCloneWithFilterSort:
'
'Resume Bye_ACFormRecordSetCloneWithFilterSort
'
'End Function

Cheers,
 
O

oli

Well, then, another reason to perform the filtering and sorting on the
server! [...]
I never used myself the form's filtering and sorting functions, so I
didn't notice before this undocumented difference between ADP and MDB.

You can prevent the user from filtering by setting the subforms AllowFilters
property to False, but what if the user uses the "Sort Ascending" or "Sort
Descending" functions from the context menu? This will not do a sorting on
the server!
 
O

oli

Malcolm,

You show us two interesting solutions. Thanks!

As you noticed, the column names in the Filter and OrderBy properties of the
subform are prefixed with the subform's name! (Strange idea of Microsoft)
Instead of using RegEx you could remove this prefix using Access' Replace()
as below:

Private Function GetUnderlyingRecordset(fsub As SubForm) As
ADODB.Recordset
Set GetUnderlyingRecordset = fsub.Form.RecordsetClone
If fsub.Form.OrderByOn Then
GetUnderlyingRecordset.Sort = Replace(fsub.Form.OrderBy,
fsub.Form.Name & ".", "")
End If
End Function

Note: I set AllowFilters=False, this is why I didn't care about filters in
this example.

Regards,
Olivier
 
V

Vadim Rapp

Hello oli,
You wrote in conference microsoft.public.access.adp.sqlserver on Wed, 24
May 2006 16:04:32 +0200:

o>> Sure it does not. The recordset is what comes from the database.
o>> Sorting and filtering is obviously made by the form.

o> Yes, but it does in mdb's.

mdb = database and frontend in one bottle, so I think it's just recreating
the recordset.

Vadim
 
O

oli

Hello Vadim,

It even works with separated front- and backend mdb's. With mdb's the
recordset is always created locally by the Jet Engine, since the backend mdb
is not a database server but only a passive database file. So Access
preferes to recreate the recordset itself when it has to be reordered.

With adp's the recordset is created remotely by the SQL-Server. So Access
prefers to reduce network traffic by just sorting the lines in the form and
leaves the recordset untouched.

This is my interpretation of the facts. It would be preferable, if Microsoft
would give us more information on the behaviour of Form.Recordset and
Form.RecordsetClone and what happens exactly in mdb's and adp's when the
form is filtered and ordered.

Oli
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top