strive4peaceIf you like my Sort123 Code, please donate so I can keep on developing.
|
|
|
| Access Basics | Video Tutorials | Learn VBA | ListFiles Database | Random Picker | Error Handling | Reference Database | Whistles & Bells | Help Examples | Crystal's Analyzer | Code Documenter |
Data Structure of ExampleFirstly, I must preface this example by telling you that this data is not normalized. I want to use an example that will be interesting for you and has lots of columns with different types of data. Since I love music (doesn't everybody?), I used information from a personal database I am compiling to study popular songs. While I have thousands of songs in my database, I only extracted a couple hundred records. If you notice any errors in the data, please let me know.The result is a non-normalized table of hit song information. I don't want you to assume this is a good way to design tables. From a structure point of view, this is a very bad example! ... but it is good for what I want to show you. The Genre information is normalized. The Primary Key in the Genres table is an autonumber field called GenreID. Genre is a text field that descibes a genre such as Country, Rock, and Blues. In the Hits table, GenreID is a Foreign Key to Genres and is stored as a Long Integer. By knowing this value, the text field to describe the genre can be looked up anytime. The text corresponding to the GenreID is displayed using a combobox — from a table not in the RecordSource of the form. The fact that the Genres table got added to the form RecordSource has nothing to do with the combobox text displaying. Here are the fields from the Hits table that were used on the form:
Form RecordSourceHere is the RecordSource for the form:SELECT Hits.* , Genres.Genre , Format([FirstDate],"mmm") & Chr(13) & Chr(10) & Format([FirstDate],"yyyy") AS FirstDateMoYr FROM Hits LEFT JOIN Genres ON Hits.GenreID = Genres.GenreID; It is good practice to use asterisk ( * ) to pull fields from the main table for the form. By doing this, if you change, add, or delete fields, everything is still available. in our example: The Genres table must be in the form RecordSource since the form is sorted by Genre when the user clicks on the column header for GenreID. Genre is also specified as a secondary sort for other columns. A form can only be sorted by fields that are in the RecordSource. The Genre field is not used in any ControlSource. There is a combobox that uses Genre in the RowSource. Even though Genre is displayed to the user in a combox, no form can be sorted by a combobox column unless it is also the ControlSource, which Genre is not. The ControlSource is GenreID, but sorting by this number would have no meaning for the user. Therefore, the reason for including the Genres tabe in the form RecordSource is to enable the user to include Genre in a meaningful sort. Combobox RowSourceThe RowSource for a combobox does not come from the RecordSource for the form (or report). Comboboxes are a great way to show data from related tables.in our example: The GenreID combobox stores GenreID in the Hits table but displays Genre from the Genres table. In order to illustrate how Sort123 can be used on the column header label to sort by text stored in another table that correlates to the numeric foreign key stored in the main table of the form RecordSource, the related table (Genres) is added to the form RecordSource (based on Hits) using an outer join. aah, an uncomfortably long sentence -- I welcome your editing suggestions for a better way to say this... It is interesting to note that, even though the Genres table is used in the form RecordSource, no fields from it are used on the form. Combobox ControlSourceThe ControlSource for a combobox is often a field in the form RecordSource that is a numeric foreign key.in our example: The ControlSource is GenreID in the Hits table, which is specified in Hits.* Other combobox properties that are set are listed in the GenreID Combobox section. LEFT JOINNotice that Hits is related to Genres with a LEFT JOIN ... this means that all records from the first table specified, Hits, will be displayed even if there is not a corresponding GenreID in the Genres table.If the Join type is not specifically changed from the default INNER JOIN, then only records in Hits with a matching GenreID in Genres would be displayed. This would not suit our purposes at all — we would hardly see any records! The genre data will be filled out ... sometime... but meanwhile, it is important to see where it is missing as well as other information that is there. GenreID ComboboxThe Combobox to collect GenreID and display Genre has these properties:ControlSource —> GenreID RowSource —> SELECT Genres.GenreID , Genres.Genre FROM Genres; ColumnCount —> 2 ColumnWidths —> 0;3 in ListWidth —> 3.2 in (sum of column widths + 0.2" for scroll bar) Calculated Field in RecordSourceYou will notice there is a calculated field in the RecordSource called FirstDateMoYrFormat([FirstDate],"mmm") & Chr(13) & Chr(10) & Format([FirstDate],"yyyy") AS FirstDateMoYrThis displays the First Date that a hit made number one on the charts as
Chr(13) = Carriage Return Chr(10) = Line Feed Sort123 ParametersDeclaration StatementLook at the declaration statement for the Sort123 procedure:Function Sort123( _ pF As Form _ , pField1 As String _ , Optional pField2 = "" _ , Optional pField3 = "" _ ) As Byte The first parameter is a form reference. pF
The second parameter, pField1, is the (first) field name to sort by. If the field name is an empty string, the form sort is removed (OrderByOn = False). The field name, if specified, must be in the form RecordSource. You may send up to 2 more field names for secondary and tertiary sorts. pField2 and pField3 are optional. Line ContinuationThe space underscore _ at the end of lines means that the statement is continued on the next line. Use Line Continuation to keep lines short and enhance readability of the code.Return ValueThe return value for the function is specified as Byte. This is done because no return value is ever used and byte is the smallest data type. This procedure could, therefore, be defined as a Sub instead of a Function. I initially wrote this as a function so it can be specified directly on the property sheet on the Event tab.Now I normally limit my property sheet references to simple functions without parameters since the property sheet is not compiled (thanks, Gord, for hammering this point home <smile>) In the future, I may change the return data type to boolean and send back True if anything was changed in the OrderBy or OrderByOn properties. Any dirty record should also be saved, which is not currently being done. If I update the code, it will be posted to this page. Passed Field NamesHere is a diagram showing the column headers and which fields are passed to the Sort123 procedure.
Since there are no duplicate song names in this list, the Song column is just sorted by the Song Name. If you sort by the number of days a hit held number 1 on the charts (nDays), there are multiple records for each value so more fields are specified in the sort: Number of Days, Genre, and Song name. Sort123 CodeHere is the Sort123 code that should be put into a general (standard) module:'~~~~~~~~~~~~~~~~~~~~~~~~~~ Sort123 Function Sort123( _ pF As Form _ , pField1 As String _ , Optional pField2 = "" _ , Optional pField3 = "" _ ) As Byte '091203 'written by Crystal 'strive4peace2009 at yahoo dot com 'sort form by specified field(s) 'sending the same sort fields 'toggles Ascending and Descending order ' -------------------------------------------------------- ' PARAMETERS ' pF = form reference ' if in code behind a form, this is ' Me ' pField1 -- name of field for first sort ' pField2 -- optional, name of field for second sort ' pField3 -- optional, name of field for third sort ' ' -------------------------------------------------------- ' NOTES ' you must specify FIELD names in the RecordSource ' control names do not matter ' -------------------------------------------------------- ' 'USEAGE ' commonly called on ' CLICK event of column header label control ' ' in code behind form to specify main and secondary sort fields ' Sort123 Me, "Fieldname1", "Fieldname2" 'set up Error Handler On Error GoTo Proc_Err 'dimension sort string variables ' for both ascending and descending cases Dim mOrderBy As String _ , mOrderByZA As String ' assign the first field to the OrderBy strings If Len(Trim(pField1)) > 0 Then mOrderBy = pField1 mOrderByZA = pField1 & " desc" ' assign the second field to the OrderBy strings ' if it is specified If Len(Trim(pField2)) > 0 Then mOrderBy = (mOrderBy + ", ") & pField2 mOrderByZA = (mOrderByZA + ", ") & pField2 End If ' assign the third field to the OrderBy strings ' if it is specified If Len(Trim(pField3)) > 0 Then mOrderBy = (mOrderBy + ", ") & pField3 mOrderByZA = (mOrderByZA + ", ") & pField3 End If Else ' no sort string specified ' remove OrderBy from the form pF.OrderByOn = False ' exit the procedure GoTo Proc_Exit End If ' use WITH to minimize the number of times ' this code will access the form With pF ' if the form is already sorted ' by the ascending sort string, ' then change order to be descending If .OrderBy = mOrderBy Then .OrderBy = mOrderByZA Else ' change the sort order to ascending ' if form is not sorted this way If .OrderBy <> mOrderBy Then .OrderBy = mOrderBy End If End If ' make the form use the specified sort order .OrderByOn = True End With Proc_Exit: Exit Function Proc_Err: MsgBox Err.Description, , _ "ERROR " & Err.Number _ & " Sort123" Resume Proc_Exit 'if you want to single-step code to find error, CTRL-Break at MsgBox 'then set this to be the next statement Resume End Function Standard (General) ModuleTo Create a Standard (General) Module:
Option Explicit
Now that we have this code saved in a general module, it can be used anywhere in the database. On our form, we are going to call Sort123 in the Click [Event Procedure] for each column label. Code Behind FormHere is the code behind the form to implement sorting on each column:Option Compare Database Option Explicit Private Sub Album_Label_Click() Sort123 Me, "Album", "Artist" End Sub Private Sub Artist_echo_Label_Click() Sort123 Me, "Artist", "Songname" End Sub Private Sub Artist_Label_Click() Sort123 Me, "Artist", "Songname" End Sub Private Sub BPM_Label_Click() Sort123 Me, "BPM", "Artist", "Songname" End Sub Private Sub Decade_Label_Click() Sort123 Me, "Decade", "Songname" End Sub Private Sub Dur_Label_Click() Sort123 Me, "Dur", "Songname" End Sub Private Sub FirstDate_Label_Click() Sort123 Me, "FirstDate", "Songname" End Sub Private Sub GenreID_Label_Click() Sort123 Me, "Genre", "BPM", "Songname" End Sub Private Sub HitID_Label_Click() Sort123 Me, "HitID" End Sub Private Sub nDays_Label_Click() Sort123 Me, "nDays", "Genre", "Songname" End Sub Private Sub RecordedBy_Label_Click() Sort123 Me, "RecordedBy", "Artist" End Sub Private Sub Songname_echo_Label_Click() Sort123 Me, "Songname" End Sub Private Sub Songname_Label_Click() Sort123 Me, "Songname" End Sub Private Sub TheLabel_Label_Click() Sort123 Me, "TheLabel", "Artist" End Sub Private Sub WrittenBy_Label_Click() Sort123 Me, "WrittenBy", "Artist" End Sub for more information on Code Behind a Form: How to enter a live e-mail... -- Code Behind Form (CBF) http://www.utteraccess.com/forums/showflat.php?Cat=&Number=1438041&fpart=all#Post1438041 Sort By Examples...and now what you have all been waiting for ... to see how this works on our form.click on the Song column header to sort by song name Private Sub Songname_Label_Click() Sort123 Me, "Songname" End Sub
and now click again to sort by song name in descending order:
Some of you probably noticed that more information is filled out for the songs at the top of the alphabet ... started at the top looking up other information I decided to track. click on the Artist column header to sort in ascending order by Artist and then Song Private Sub Artist_Label_Click() Sort123 Me, "Artist", "Songname" End Sub
... and then again to sort in descending order:
click on the First Date column header to sort in ascending order by First Date and then song Private Sub FirstDate_Label_Click() Sort123 Me, "FirstDate", "Songname" End SubMissing Information: In this example, there is a song that never hit number 1 on the chart, so it does not have a FirstDate value. In this case, the information is not missing because it is not known. The information is missing because it is not relevant.
Since I have Beat Per Minute (BPM) information for more of the recent songs, the BPM column is included in the capture. click on the #Days column header to sort in ascending order by #Days, Genre, Song Private Sub nDays_Label_Click() Sort123 Me, "nDays", "Genre", "Songname" End Sub
... and then again to sort in descending order (these are the songs that stayed on the chart the longest in the number one spot):
According to the records, the songs around the turn of the 20th century appear to be the most popular of all time. This is actually a bit misleading as there were less songs being released that long ago. Well, there are a couple recent songs in this first screen of information, but lets look at the middle of this list:
click on the Decade column header to sort in ascending order by Decade and then Song Private Sub Decade_Label_Click() Sort123 Me, "Decade", "Songname" End Sub
... and then again to sort in descending order (these are the most recent songs):
... and a couple of shots from the middle of this sort:
click on the BPM column header to sort in ascending order by BPM, Artist, Song Private Sub BPM_Label_Click() Sort123 Me, "BPM", "Artist", "Songname" End Sub
As you can see, several of the records have this information missing. Values that need to be filled are at the top of the sort order. Right now, we are only interested in looking at records with values Filter for records where BPM is filled out. Right-click in the BPM column and choose —> Does Not Equal Blank (for Access 2007+ — in lower versions, use Not Null for filter criteria after right-clicking)
These are the slowest songs in the database that have beat information filled out:
and these are the fastest songs...
click on the Genre column header to sort in ascending order by Genre, BPM, Song* Private Sub GenreID_Label_Click() Sort123 Me, "Genre", "BPM", "Songname" End Sub
... and then again to sort in descending order
NOTE: Because we do not want to sort by GenreID, we pulled the Genres table into the RecordSource of this form so we could have access to the corresponding text value stored in Genre. The Genre field from Genres is not on the form anywhere but because Genre is in the Recordsource, we can use it to sort. click on the Duration column header to sort in ascending order by Duration and then Song — these are the shortest songs* Private Sub Dur_Label_Click() Sort123 Me, "Dur", "Songname" End Sub
... and then again to sort in descending order — these are the longest songs:
click on the Album column header to sort in ascending order by Album and then Artist* Private Sub Album_Label_Click() Sort123 Me, "Album", "Artist" End Sub
click on the Written By column header to sort in ascending order by WrittenBy and then Artist* Private Sub WrittenBy_Label_Click() Sort123 Me, "WrittenBy", "Artist" End Sub
click on the Recorded By column header to sort in ascending order by RecordedBy and then Artist* Private Sub RecordedBy_Label_Click() Sort123 Me, "RecordedBy", "Artist" End Sub
click on the Label column header to sort in ascending order by Record Label and then Artist* Private Sub TheLabel_Label_Click() Sort123 Me, "TheLabel", "Artist" End Sub
The song and Artist columns are repeated since the record is so wide. The labels over these columns work just as the first 2 columns. click on the HitID column header to sort in ascending order by the Primary Key Private Sub HitID_Label_Click() Sort123 Me, "HitID" End Sub
* only records with data in this field are shown See how easy it is to add custom sorting to a continuous form? Once you get the code for Sort123 into your database, you can let it do most the work :) Indicate Sorting with ItalicsWhile it is true that Access gives the user ability to sort using the ribbon and shortcuts, some applications have these features disabled and many users do not know Access well enough to do this. When I design continuous forms, if the column can be sorted, then I put the label in italics. Note that the "Number 1 Hit" is not in talics — that is because nothing happens if you click on it. Putting labels that have code in italics is one way you can provide visual indicators to your users.
Download DatabaseDOWNLOAD a Sample Database for Sort123MainMenu_Sort123_BoldMe_Crystal_091207__MDB.zip contents:
CommentsIf you have comments or want to tell me how Sort123 helped you, please |
||||||
Free Book on Access: Access BasicsFree Tutorials: Video TutorialsFor information on private programming and training, |
Copyright © 2009 Crystal Long | All rights reserved | Last Updated Feb 2012 | Contact Webmaster for help with your web site