Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Access Programming: Week 4 4. Programming the Access application Access Object Model (revisited) Application Forms Reports Controls Controls Modules References DoCmd Screen So far on the course we have created programs to manipulate Form and Report objects, often using the DoCmd object, which is an object whose methods correspond to most of the macro actions. The Modules collection contains the Module object which refers to a specific open module. In general you use the Module object interactively to write code, rather than write code about modules. The Forms, Reports and Modules collections have no methods; Access manages them for you. We have seen the exclamation point syntax when referring to objects in collections. ?Forms!frmCustomers.RecordSource There are three additional ways. If you open the form frmCustomers you can test them in the Immediate window. 1. Refer by name ?Forms(“frmCustomers”). RecordSource 2. Refer by variable strName = “frmCustomers” ?Forms(strName).RecordSource 3. Refer by position ?Forms(0). RecordSource The last of these is trickiest to use because 0 refers to the first open form, 1 to the next open form and so on. But if users can open and close forms at will, problems would be inevitable. The Screen object The Screen object can be used in procedures if you are concerned with writing reusable code. The Screen object refers to the active object, the object that has the focus (or it could refer to an object that previously had the focus). It does not make the form, report or control the active object. Code has potential to be reusable if specific object names are not referred to. The screen object’s properties include: Screen.ActiveForm Screen.ActiveControl Screen.ActiveReport Screen.PreviousControl For example, in a program the line Screen.ActiveForm.Visible = False will hide the form that is the active form References The References collection object refers to the libraries that are available to the programmer. Libraries contain the resources a programmer needs, i.e. the inbuilt objects and functions that one needs to manipulate. For example if you wanted to program an Excel workbook from an Access database you would need to add a reference to the Excel Object library. We will be adding references in next week’s course. 1 Access Programming: Week 4 Referring to a subform A form can display a subform, i.e. you can display two forms on the screen. (It is necessary for the tables on which the form is based to participate in a one-to-many relationship, and for the table on the many side of the relationship to have a primary key, or a field with its Indexed property set to Yes, no duplicates). The form containing the subform control is called the main form and the form displayed within the subform control is called the subform. Open frmSalesOrders to see an example, and go to Design view. Referring to a subform can seem a little tricky at first. In the first illustration the subform control is selected: the SourceObject property is subfrmOrderDetails Link Child Fields and Link Master Fields are what synchronize the subform to the main form. The subform control is selected Test the reference to the subform control in the Immediate window as follows: ? Forms!frmSalesOrders.subfrmOrderDetails.SourceObject It should show the SourceObject property of the subform control In the next illustration the subform is selected – the properties are different to those of the subform control: The window displays the properties of the Form object. The RecordSource property is qryOrderDetails Here the subform is selected To reference the subform in the Immediate window: ? Forms!frmSalesOrders.subfrmOrderDetails.Form.RecordSource 2 Access Programming: Week 4 A procedure to hide a Subform You may want to hide a subform under certain circumstances. Open frmSalesOrders in Design view and add a toggle button just above the right corner of the subform. Name the button tglHide, and set its caption property to Hide. Write the following procedure for its After Update event. Listing 4.1 Private Sub tglHide_AfterUpdate() If Me.tglHide Then Me!subfrmOrderDetails.Visible = False Me!tglHide.Caption = "Show" Else Me!subfrmOrderDetails.Visible = True Me!tglHide.Caption = "Hide" End If End Sub If you wanted the subform to be hidden when the form opens go to the Properties window: - change the Visible property of the subform control to No - change the caption of the button to Show. - in the code reverse the settings of the Visible and Caption properties in the code. The program in listing 4.5 uses simple syntax, taking advantage of the Me property of frmSalesOrders. However you have to use semi-qualified syntax if referring to a subform from another form, a query or the Immediate window. For example this is how to refer to the OrderNo control Forms!frmSalesOrders.subfrmOrderDetails.Form.OrderNo Test the reference in the Immediate window (you would need the form to be open in Form view) ? Forms!frmSalesOrders.subfrmOrderDetails.Form.OrderNo The general syntax for referring to a control on a subform has an extra element, Form: Forms!formname!subformcontrolname.Form.controlname The Form property of the subform control represents its subform. Similarly the Report property of a subreport control refers to its subreport. 3 Access Programming: Week 4 Active X controls1 You can add Active X controls to Access to provide capabilities that Access does not possess e.g. to display an interactive calendar, or play a sound or video file. These are self-contained objects with properties, methods and In Access 2007, click the Design tab, and then events. click Insert ActiveX Control in the Controls group. There are thousands of Active X controls (formerly known as ocx’s) commercially available and some that are free. In the Insert ActiveX Control dialog box, click to We will look the interactive Calendar control, which is free with the retail version of Access. Depending on your select Calendar Control 10.0 or a later version installation of Access there may be other controls. from the Select an ActiveX Control list box, and clickActiveX OK. control. (You might not To insert a Calendar, in the form frmSalesOrders, click the Insert menu andthen choose wish to save the changes after you have finished) Choose Calendar control and click OK. Click the form and the control will be inserted on the form. When you add a control Access automatically adds a Reference to the object’s type library – you could view its methods and properties in the Object Browser. Make some room for the control to display it; you can resize the control. You can select the control and view its properties: 1 Calendar control no longer exists in Access 2010. Alternative solutions are discussed on http://social.technet.microsoft.com/Forums/en/office2010/thread/662ac883-0f5c-4b89-8a6f-0a3efc4ec259 4 Access Programming: Week 4 Its name is Calendar 1 You can view and modify more properties by clicking the Build button at the Month property With frmSalesOrders in Form view you can type ?Forms!frmSalesOrders!Calendar1.Value in the Immediate window, and the currently selected date should be returned. To bind the calendar to a control on the form go to the Calendar’s Control Source property, and choose OrderDate; the calendar will now display the value in the OrderDate field. To change the value of the OrderDate field for a particular order you can click a day and enter the record. To automatically refresh the form on changing the date, go to the form’s module and type the following: Listing 4.2 Private Sub Calendar1_AfterUpdate() Me.Refresh End Sub Note that Access doesn’t provide an AfterUpdate event for a calendar control in the Properties window, because the event is control specific, Access can only provide generic events. However the control does recognise an AfterUpdate event and you can use it. 5 Access Programming: Week 4 Calendar Example Another way to use a Calendar control is to link it to a subform, so the subform displays records for the date value of the calendar. We will use a Calendar to display sales orders on a particular date. The finished form should look as follows: First we make the subform Click Forms in the database window, click New, and choose Form Wizard Select SalesOrders as the data source and click OK Select the fields as illustrated Click Next 6 Access Programming: Week 4 Select Datasheet for the layout Click Next The style doesn’t matter click Next again, and finally type subfrmOrders as the form’s title. Click Finish. Close the subform. Now make the main form. In the database window, click Forms, New and choose Design view without specifying any data. Insert a Calendar control, go to its properties and name it ocxCal. Save the form as frmDates. Then arrange your screen so you can see both the Database window and frmDates in Design view behind it. A neat way of inserting a subform is to drag the subform from the Database window and drop it into the main form. 7 Access Programming: Week 4 Next we must take the important step of setting the Link Child Fields and Link Master Fields properties. Set the Child field to be OrderDate, and set the Master field to be ocxCal. This is necessary to synchronize the two forms. Go to form view to try it out. Select May 2001, and click the 13th. Click the subform and you should see the orders on that day. To make it operate more smoothly write this short procedure for the Calendar control’s AfterUpdate event. Listing 4.3 Private Sub ocxCal_AfterUpdate() Me!subfrmOrders.Requery End Sub Save the form when you have finished. 8 Access Programming: Week 4 Connectivity Using DoCmd The DoCmd object provides TransferDatabase and TransferSpreadsheet methods (you can even use the corresponding macro actions if you don’t want to program). The transfer could be an import or an export. DoCmd routines can be event handlers attached to buttons on forms, or run from VB Editor if one-offs, or called as a function if you want to reuse the code on more than one form. In the first example we are in an mdb file which contains a table Invoices, and the ExportTable routine exports it to the specified pathname as Invoices. Open Accounts.mdb create a module and try this code. Listing 4.4 (Exporting from current database) Sub ExportTable() On Error GoTo ExportTable_Err DoCmd.TransferDatabase acExport, "Microsoft Access", _ "U:\Company2004.mdb", acTable, "Invoices", "Invoices", False ExportTable_Exit: Exit Sub Modify path as appropriate ExportTable_Err: MsgBox Error$ Resume ExportTable_Exit End Sub Next we create a link named Invoices to a table Invoices, which is in an external file, so this code would work in any database. If you want to reuse the code, make it a function and call from the On Click property of a button on a form. Listing 4.5 (Making a link in the current database to a table in another database) Sub Linktable() On Error GoTo Linktable_Err DoCmd.TransferDatabase acLink, "Microsoft Access", _ "U:\Accounts.mdb", acTable, "Invoices", "Invoices", False Linktable_Exit: Exit Sub Linktable_Err: MsgBox Error$ Resume Linktable_Exit End Sub Here’s a reference for the TransferDatabase method: can be one of these constants: TransferType acExport (optional) acImport default The acLink transfer type is not supported for Microsoft Access projects (.adp files) acLink Database Type (optional) DatabaseName (optional) ObjectType (optional) Source (optional) Destination (optional) StructureOnly (optional) one of the types of databases you can use to import, export, or link data. Microsoft Access is default the full name, including the path, of the database you want to use to import, export, or link data the type of object whose data you want to import, export, or link. You can specify an object other than acTable only if you are importing or exporting data between two Access databases. Options include acTable default ,acQuery,acReport the name of the object whose data you want to import, export, or link the name of the imported, exported, or linked object in the destination database True to to import or export only the structure of a database; False (default) to import/export data. 9 Access Programming: Week 4 StoreLogIn (optional) to store the login ID and password; False (default) if you don't want include the details Importing an Excel file The DoCmd object can be used to connect to Excel, using its TransferSpreadsheet method (naturally you can use the corresponding macro actions if you don’t want to program). The following code imports an Excel file called NewPrices.xls from the specified path as a table NewPrices Listing 4.6 Sub ImportPrices() On Error GoTo ImportSheet_Err DoCmd.TransferSpreadsheet acImport, 8, "NewPrices", _ "D:\Spreadsheets\NewPrices.xls", True ‘change path ImportSheet_Exit: Exit Function ImportSheet_Err: MsgBox Error$ Resume ImportSheet_Exit End Sub 8 refers to the version of Excel True refers to the fact that the first row of the spreadsheet is a header row; use False if the first row contains data. Action Queries for the New Prices example We will extend the example as follows: Import the NewPrices spreadsheet Update the prices in the Products table to those in the imported table Add any new rows in the imported table to the Products table (append) A snag is that the ProductId field in the Products table is of the data type Text, but Access imports it as a number (a Double) in the NewPrices table. As things stand we need to change the data type in the Products table to a Double (even worse we have to delete the relationships the Products table is in before we can change the design, then recreate the relationships after). 1. Updating Prices using an Update Query The task is to update the Unit Prices in the Products table to the amount in a spreadsheet file NewPrices. Import the spreadsheet NewPrices as a table, call the new table NewPrices. Before you create the query ensure the ProductID field in the NewPrices table is Text (to match the data type of ProductID in the Products table). Create the illustrated query and run it using the Run button. 10 Access Programming: Week 4 2. Appending records from a table where there is no record in the original table The idea here is to append products to the Products table from the NewPrices table where the Product is new, i.e. there is not yet a record of it in the Products table. Create the following Append query. Note that the Criteria contains an SQL statement (i.e. a subquery) Check the query result, then turn it into an Append query and run it. Save the query as qryAppendPrices Putting it all together Note that the code calls the ImportPrices program. Also not e that it has been made a function – this is not necessary but it makes it possible to attach the code to the click event of as many buttons as you like. (Put a button on the Menu form and type =ImportAndModify() into its Click Event in the Properties window. Function ImportAndModify() ImportPrices DoCmd.OpenQuery "qryUpdatePrices" DoCmd.OpenQuery "qryAppendPrices" End Function Importing Text The TransferText method is available to import text files. Listing4.7 Sub ImportText() On Error GoTo ImportText_Err DoCmd.TransferText acImportDelim, "", "New", "U:\sample.txt", False, "" ImportText_Exit: Exit Function ImportText_Err: MsgBox Error$ Resume importText_Exit End Sub 11 Access Programming: Week 4 Appendix: Built-in functions Both Access and VBA provide functions that you can use in procedures. Functions are classified into categories: some work with Date and Time fields; amongst others there are also Text Manipulation functions; Data Type Conversion functions; Domain Aggregate functions; Mathematical functions; Financial functions; General purpose functions. One way to see the categories of functions is to open the Expression Builder, expand Functions, then expand Built-In Functions. The middle column shows the categories. If you choose a function from the right hand column you can click the Help button to access further information. Functions are diverse in the tasks they perform and there is no one way to use them all. Each function must be looked at on the basis of its own characteristics. The majority take one or more arguments, some like Date take no arguments – some like the financial functions might need some research on your part before you could use them correctly. You can also look for help on specific functions is the Contents page of VB Help; expand Visual Basic Language Reference, expand Functions, then expand the appropriate alphabetical category The following tables list selected functions. The examples can be tested in the Immediate Window Date and Time functions Function Date() or Now() Day() Month() Year() Weekday() Description returns the current system date returns an Integer representing the day from a Date value returns an Integer that represents the month of a Date value returns an Integer that represents the year of a Date value returns day of the week (Sunday = 1) Example (You can use # instead of “ in examples) Date() Day(“31-Jan-2004”) returns 31 Month(“31-Jan-2004”) returns 1 Year(“31-Jan-2004”) returns 2004 Weekday(“31/01/2004”) returns 7 DateAdd() DateDiff() DateSerial() returns a data to which a specified time interval has been added; all three arguments are required DateAdd("yyyy", 2, date()) returns an Integer representing the difference between two dates DateDiff(“d”, Date(), ”31-Jan-2004”) returns a date given three integers that specify year, month, day in that order DateSerial(2004, 1,31) returns the date 2 years from today DateAdd("ww", -3, ”4 Mar 2004”) returns the date 3 weeks earlier than 4th March 2004 returns the number of days between 31-Jan-2004 and the current date returns 31/01/2004 12 Access Programming: Week 4 Nested functions You can have a function within another, e.g., Month(Date()) will return the current month as an Integer To evaluate a nested function start with the innermost brackets, i.e. Date() – this evaluates to the current date, then the Month function extracts the month part of the date. Example You can use inbuilt functions within in your own functions. For example, here is a function to find the start of the next financial year, given a date – it uses the DateSerial() function Listing 4.9 Public Function NextFinYear(dt As Date) As Date If Month(dt) > 3 And Month(dt) <= 12 Then NextFinYear = DateSerial(Year(dt) + 1, 4, 1) Else NextFinYear = DateSerial(Year(dt), 4, 1) End If End Function Explanation The crux of the problem is that the date supplied to the function might be before or after the end of March. If it is after March, the next financial year begins in the year after the current one. DateSerial adds one to the year part, and returns 4 for the month and 1 for the day. If the month part of the supplied date is January, February or March, the next financial year begins in the current year. DateSerial composes a date out of the current year and supplies a 4 and a 1 for April 1 st. How to test for a leap year The rule for testing for a leap year is that a year is a leap year if it is exactly divisible by 4 but not by 100, except that years divisible by 400 are leap years – this is the Gregorian calendar. Listing 4.10 Function IsLeap(dt As Date) As Boolean If (Year(dt) Mod 4 = 0 And Year(dt) Mod 100 <> 0) Or Year(dt) Mod 400 = 0 Then IsLeap = True Else IsLeap = False End If End Function Text manipulation functions LCase() UCase() Len() Trim() LTrim() RTrim() Left() Right() Mid() returns lower case version of a string returns upper case version of a string returns number of characters in a string removes leading and trailing spaces from a string removes leading spaces from a string removes trailing spaces from a string returns leftmost characters of a string returns rightmost characters of a string returns specified number of characters from specified starting point in a string LCase(“ABCD”) UCase(“abcd”) Len(“ABCDE”) Trim(“ ABC “) LTrim(“ ABC”) RTrim(“ABC “) Left(“ABCDEF”,3) Right(“ABCDEF”,3) Mid("Lindsay Davenport",9,4) returns Dave. The function returns the string starting at the ninth character which is four characters long StrComp() compares two strings for equivalence and returns the integer result of comparison: 0 if strings are equal, -1 if strings are different StrComp(“ABC”,”abc”) returns 0. The strings are equal. 13 Access Programming: Week 4 Val() Chr() or Chr$() returns numeric value of a string in data type appropriate to the format of the argument. returns a character associated with a character code e.g. ANSI (you sometimes see a character code used in a program instead of the character, but its not necessary) Val(“123.45”) returns 123.45 (i.e. a number) chr(34) returns " (double quote) chr(42) returns * (asterisk) Mathematical functions Abs() returns absolute value of numeric field Int() returns numeric value with the decimal fraction truncated returns a number rounded to a specified number of decimal places. syntax is: Round() Round(expression, numdecimalplaces) where the numdecimalplaces argument Rnd() is optional; the function returns an integer if it is omitted. creates random number (single) between 0 and 1 Abs(-1234.5) returns 1234.5 Int(13.9) returns 13 round(12.459, 2) returns 12.46 round(12.9) returns 13 to generate a random whole number between 0 and 9 you can use: Int(Rnd()*10) Explanation – the Rnd() function generates a single between 0 and 1; this value is multiplied by 10, so we have a number between 0 and 9; finally the Int() function removes the fractional part leaving a whole number. Other maths functions are: Atn , Cos , Exp, Fix, Log, Sgn, Sin, Sqr, Tan General-Purpose functions Iif() IsNumeric() IsNull() IsDate() IsEmpty VarType Format returns one value if the result of an expression is True, another if the result is False returns True if argument is one of the number field data types, otherwise returns False returns True if argument is Null, otherwise returns False indicates whether an expression can be converted to a date indicates whether a variable (of type variant) has been assigned a value Returns an Integer indicating the subtype of a variable. Refer to Help for the list of constants with which the function is used. This function can be used to format numbers, strings and dates. Iif([Exam mark] >=50, “Pass”,”Fail”) IsNumeric([Field Name]) IsNull([Field Name]) IsDate("21 feb 98") returns True IsEmpty(variable name) e.g. you could test if a variable is an integer as follows: If VarType(varname) <> vbInteger Then Format(5459.4, "£##,##0.00") returns £5,459.40 Format("HELLO", "<") returns "hello" Format(now, "dd/mm/yy hh:mm:ss AMPM") returns 28/10/04 01:00:16 PM Format(date,"Long Date") returns 28 October 2004 14 Access Programming: Week 4 IIf() (Immediate If) An Immediate If is like a one line If ... Then ... Else statement; it can be used in places that an If ... Then ... Else statement can’t, e.g., in a query which is Control Source for an object on a form or report. The way it works is that it takes three arguments: the first argument is a condition to be evaluated the second argument is the action if the condition evaluates to True the third argument is the action if the condition evaluates to False As an example, if the value of a field on a report is 0 you might not want to print a zero. You could use the Iif statement in such a situation: Expr1: IIf([DiscountValue]=0," ","Discount: " & [DiscountValue]) IIf statements can often be hard to read – it helps to split the argument into three components [DiscountValue]=0 the condition is True if DiscountValue evaluates to zero "" action to take if True is to print a space, i.e. nothing shows up on report "Discount: " & [DiscountValue]) action to take if condition is false is to print the string “Discount “ and concatenate DiscountValue Data Type Conversion functions CInt() converts a string to a number CInt(“99”) returns 99 rounds a fraction to an Integer CInt(123.5) returns 124 CLng() rounds a fraction to a Long Integer CCur() CDate() converts numeric value to Currency data type converts string expression to Date CStr converts its argument to a string CLng(100000.1) returns 100000 CCur(123.5) CDate("21Feb 98") returns 21/02/1998 CStr(99) returns “99” Other conversion functions are: CBool, CByte, CDbl, CDec, CSng, and CVar. You may come across Str and Val functions: these are older conversion functions that can be replaced with the more modern versions. Timer function If you are concerned with testing performance issues in your database, you can use the Timer function to time how long something takes. For example, here is an example that times how long a query takes to run. Public Function QueryRunTime(strQueryName As String) As Double Dim Start As Double, Finish As Double Start = Timer DoCmd.OpenQuery strQueryName Finish = Timer QueryRunTime = Finish - Start MsgBox strQueryName & " run time is " & QueryRunTime End Function Call the function like this, say in the Immediate window ?queryruntime("qryRegionalCustomers") 15 Access Programming: Week 4 Domain Aggregate functions Domain Aggregate functions provide statistical information about sets of records (known as domains). In this respect they are similar to the functions in Totals queries, which are known as SQL Aggregate functions. The syntax of Domain Aggregate functions takes a little getting used to. The following example can be taken as representative: DCount(expr, domain, [criteria]) where expr identifies the field for which you want to count records; it can be a field in a table, a control on a form domain is a string expression identifying the set of records. It can be a table name or a query name criteria is an optional string expression to restrict the range of data on which the function is performed DCount() DSum() DAvg() determines the number of records that are in a specified set of records (a domain). calculates the sum of a set of values in a domain calculates the average of a set of values in a domain DCount("OrderNo", "SalesOrders") DSum("ItemCost", "qryOrderDetails") Tthe third optional argument will take a string which is an SQL Where clause without the word WHERE e.g. DSum("ItemCost", "qryOrderDetails", "ProductID = '103'") DAvg("PayRate", "Employees" You can use domain aggregate functions to supply summary information in a text box on an unbound form as follows: Create a new form in design view without specifying any data source Click the text box tool Then click on the form; you will get an unbound text box In the text box type the expression: =DAvg("PayRate", "Employees") Go to form view; the textbox should show the average pay rate of all employees in the Employees table; all you need to do is format it as currency and type a meaningful caption. The function gives a result equivalent to the query illustrated right – domain aggregate functions are available however where SQL aggregate functions are not, like on a form. Add some more text boxes and try the other examples from the table. 16 Access Programming: Week 4 Getting the Discount value into the Invoice report Working with reports can be incredibly tricky, especially where subreports are concerned – you don’t have to read this section if you don’t want to. On the other hand it might shed some light on problems you have experienced in you own work. This is the way I got the discount value into the invoice. In query builder view of qryOrderDetails I have amended the way DiscountValue is organised. The basic calculation is now in a column captioned DValue. The DiscountValue field, which is to appear on forms / reports takes DValue and applies the Format function to it as illustrated. (I suppose it could have been done all in one giant expression, but it would be almost impossible to read). Close and save the query in the Query Builder. Next open the report rptInvoice in design view, select the subreport control, then the subreport and click the Field List button. DiscountValue should be present. Drag it to the subreport, and delete its label. Preview the report the DiscountValue field should be visible The field is beautifully formatted, but leaves a problem if you don’t want zeros to appear on the invoice. Go back to qryOrderDetails in query builder view and create an expression as illustrated (you can leave its caption as Expr1) Close the Query Builder and save. Now in design view of the subreport use Expr1 instead of DiscountValue. Here’s the evidence: Expr1 field Obviously there’s more work to be done on the report e.g. subtract discounts from subtotal, charge VAT and so on. 17