Learn Microsoft Access Advanced Programming Techniques, Tips and Tricks.

Saturday, November 29, 2008

Custom Calculator and Eval Function

Introduction.

When we think of a calculator, the image that usually comes to mind is the traditional type with buttons labeled 0–9 and the standard arithmetic operators. Windows already includes such a calculator under Accessories, which can even be switched to Scientific mode — so there’s no need to recreate that in MS Access.

Instead, we’re going to design a different kind of calculator — one that can evaluate defined expressions consisting of data field names, constants, arithmetic operators, and parentheses (to control the order of operations). This calculator will allow you to input an expression, have Access evaluate it, and display the result instantly.

There’s no need for a complex interface. All we require is:

  • A TextBox to enter the expression,

  • A Command Button to trigger the evaluation, and

  • A few lines of VBA code to process the calculation.

The result can be displayed either in a MsgBox or in another TextBox on the form.

Before we begin building the calculator, let’s look at one of Access’s most powerful yet underused built-in functions — the EVAL() function. This function will serve as the core engine of our custom calculator. With EVAL(), you don’t need to worry about following mathematical rules manually — such as evaluating parentheses first, then exponentiation, followed by multiplication and division (from left to right), and finally addition and subtraction. Simply pass the expression as a string to the EVAL() function, and it will return the correctly computed result.

EVAL() Function Usage

Try a few examples quickly to get a feel of it? Open the VBA Module Window (Alt+F11) and open the Debug Window (Ctrl+G). Type the following expressions in the Debug Window and press the Enter Key:

? EVAL("2+3*4/2")

Result: 8

? EVAL("(2+3)*4/2")

Result: 10

? EVAL("log(SQR(625))/log(10)")

Result: 1.39794000867204

? Eval("10^" & "Eval('log(Sqr(625))/log(10)')")

Result: 25

? EVAL("Datediff('yyyy',Forms!Employees!BirthDate,date())")

Result: 45 (the Employees Form must be open)

EVAL() the Expression Parser

From the above examples, we can see that you can write expressions in a TextBox using built-in functions, data field references, and numeric constants. The Eval() function then parses the expression and returns the result. This gives the user the flexibility to define and evaluate custom expressions dynamically, incorporating live data from form fields into their calculations.

Tip: The Eval() function can do much more than just evaluate formulas. It can also run other functions, trigger the Click event of a command button, or execute a macro programmatically. For additional details and examples of how Eval() can be used, open the Microsoft Access Help window in the VBA editor and search for “Eval Function.”

The sample Demo Project.

I have created a sample database (available for download at the bottom of this post) designed for an auto dealership that offers credit facilities to customers at nominal interest rates of 6%, 7%, or 8%, repayable in 24, 36, or 60 installments, respectively. The salesperson is responsible for determining the most suitable repayment plan for each customer, including the percentage of down payment, installment schedule, and applicable interest rate.

To encourage sales, the salesperson is also authorized to offer customers a discount of up to 20% on the Maximum Retail Price (MRP), based on negotiation and customer eligibility.

An image of the Form he uses to run these calculations is given below:

This is a stand-alone form (which can optionally be linked to a table) containing unbound text boxes. Each text box is labeled with its corresponding name displayed to the left. When writing expressions, it is essential to properly qualify the control names, for example:
Forms!CustomCalculator!Balance

Note that shorthand references such as Me!Balance are not accepted in this context. This can make it somewhat cumbersome to build expressions, especially when multiple field names are involved in a formula.

The VBA Code

We have created a small VBA routine that recognizes text box names enclosed in square brackets [ ], retrieves their corresponding values, and substitutes them into the expression before passing it to the Eval() function.

For easier expression entry, a combo box containing arithmetic operators and text box names (in square brackets) is provided on the form. The colored display control below shows, for informational purposes, the expression after the text box references have been replaced with their actual values. just before it is submitted to the Eval() function.

When the Calculate Command Button is clicked, the result of the calculation is displayed in the Text Box with a dark background and the label Result. The VBA Code is given below:

Private Sub cmdCalc_Click()
'-----------------------------------------------------------
'Author : a.p.r. pillai
'Date    : November, 2008
'URL     : www.msaccesstips.com
'All Rights Reserved by www.msaccesstips.com
'-----------------------------------------------------------
Dim str As String, loc1 As Integer, chk As Integer
Dim strout As String, loc2 As Integer, loc3 As Integer
Dim strin As String
Const sqrleft As String = "["
Const sqrright As String = "] "

On Error GoTo cmdCalc_Click_Err

'change the reference if different
str = Me![Expression]

loc1 = InStr(1, str, sqrleft)
If loc1 > 0 Then
   strin = Left(str, loc1 - 1)
   strout = Left(str, loc1 - 1)
   loc2 = InStr(loc1, str, sqrright)
End If
Do While loc2 > 0
   strin = strin & Mid(str, loc1, (loc2 - loc1) + 1)
   strout = strout & Me(Mid(str, loc1, (loc2 - loc1) + 1))
   loc1 = InStr(loc2 + 1, str, sqrleft)
   If loc1 > 0 Then
     loc2 = InStr(loc1, str, sqrright)
      If loc2 = 0 Then
         MsgBox "Errors in Expression, correct and retry. "
         Exit Sub
      Else
         strout = strout & Mid(str, Len(strin) + 1, loc1 - (Len(strin) + 1))
         strin = strin & Mid(str, Len(strin) + 1, loc1 - (Len(strin) + 1))
      End If
   Else
     loc3 = loc2
     loc2 = 0
   End If
Loop

If Len(str) > loc3 Then
   strout = strout & Mid(str, loc3 + 1)
End If

'this line can be removed if not required
Me![parsed] = strout

'change the reference, if different
Me![result] = Eval(strout)

cmdCalc_Click_Exit:
Exit Sub

cmdCalc_Click_Err:
MsgBox Err.Description, , "cmdCalc_Click()"
Resume cmdCalc_Click_Exit
End Sub

Private Sub cmdReset_Click()
Me![Expression] = Null
Me![parsed] = Null
End Sub

Note: There is no validation check included in the Code to detect misspelled names or unbalanced parentheses, etc. These shortcomings will automatically generate an error when the EVAL() function executes. The user will be able to review the expression, make corrections, and re-try.

Download

You can implement this program on any Form with a small change in str = Me![Expression] and Me![result] = Eval(strout) lines in the Code, if different names are used. Customize the Combo Box contents based on your input Field Names.


Saturday, November 22, 2008

Data Editing And Security Issues

Introduction.

Data entry and editing are among the most crucial activities for keeping a database accurate and up to date. These steps ensure that the information remains reliable and ready for generating meaningful reports and analysis. To make data entry easier and more efficient for users, it is good practice to include combo boxes, check boxes, and calculated fields—for example, automatically determining a Payment Due Date as 30 days after the Material Delivery Date.

Another key consideration is data security. Here, the focus is not on preventing unauthorized external access—MS Access already provides robust built-in security features for that—but rather on protecting the data from accidental modifications or deletions by authorized users during routine operations.

For example, suppose our Employee database includes a Memo field that stores detailed information about each employee’s educational background and prior work experience. Normally, when the cursor (insertion point) moves into a field, the entire content of that field becomes highlighted and selected. At this stage, if the user’s attention is momentarily diverted and a key is pressed accidentally, the entire content of the field may be deleted. If the user does not immediately notice this or forgets to restore the data using Ctrl + Z (Edit → Undo Typing), the information could be lost permanently.

Protecting from unintended Changes.

We will focus on this specific behavior of the cursor and explore how to provide a level of protection against such inadvertent data loss. The way the cursor behaves when entering a field is determined by the settings found under the Keyboard tab of the Options dialog box (available from the Tools menu). Under the Behavior Entering Field section, you will find three different options, as shown in the image below:

The first option, Select Entire Field, is the default setting. However, choosing one of the other two options is generally more advisable to prevent the kind of data loss we are focusing on. Of the remaining two, my preferred choice is Go to End of Field. The reason is simple—when this option is selected, even if you accidentally press the Delete key or any other key, the insertion point is positioned at the end of the field content, and the existing information remains safe.

Since this is a global setting in Microsoft Access, any manual changes you make here will affect all forms in every database opened on your machine. Conversely, a database you design on your system will not carry these settings when opened on another computer in a multi-user environment. Moreover, you may not want this behavior applied to every form in your database.

The best approach, therefore, is to enable this feature programmatically through a VBA routine and restore the default settings when leaving that particular form. In a shared network environment, users may have different default settings for the “Behavior Entering Field” option on their own machines, so it’s important not to change these preferences permanently.

Following is the numeric value of each Behavior Entering Field Option setting:

Behavior | Description | Option Values.
  1. Select Entire Field - 0
  2. Go to Start of Field - 1
  3. Go to End of Field - 2

When opening a form that requires this modified cursor behavior, we will follow specific steps to enable it during the form’s initialization. Then, when the form is closed, we will restore the default settings to ensure that the global behavior of Access remains unchanged.

  1. Save the current default setting of Behavior Entering Field before it is changed.

  2. Change the setting to Go to the end of Field behavior for the current session of the Form.

  3. Reset it back to the saved value in Step 1 above, before closing the Form.

We can achieve this with the following Event Procedures in the Form Module:

Option Compare Database
Dim DefaultBehavior As Integer

Private Sub Form_Load()
    DefaultBehavior = Application.GetOption("Behavior Entering Field")
    Application.SetOption "Behavior Entering Field", 2
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Application.SetOption "Behavior Entering Field", DefaultBehavior
End Sub

Copy and paste the above Code into the Form's Code Module and save the Form. The Dim DefaultBehavior As Integer statement must be placed in the Global area of the Module as shown above.

Try out the New Setting

Open the Form in normal View and try moving the cursor from one field to the other by tapping the Tab Key or the Enter Key. The insertion point will position at the end of the field contents.

Saturday, November 15, 2008

Event Trapping Summary On Datasheet

Introduction.

How do we execute the Event like LostFocus() and GotFocus() procedures in the Datasheet view?

How to display the Summation of numeric values on the Datasheet view?

For answers to both questions, we need a sample Table and a Datasheet Form.

Import the following tables from the Northwind.mdb sample database C:\Program Files\Microsoft Office\Office11\Samples\Northwind.mdb.

  • Order_Details
  • Products

We also need the Products table because it is referenced by the ProductID field in the Order_Detail table through a Combo Box. Without the Products table, the ProductID field in the Order_Detail table would remain empty, which would break the data relationship.

Design a Datasheet Form

  1. To create a Datasheet Form, click on the Order_Detail Table, select Form from the Insert Menu, and select Autoform: Datasheet from the displayed list of options in the Formwizard.

    A Datasheet form will be created and will open the Table contents in Datasheet View.

  2. Select the Save As... option from the File Menu and give the name Order Details for the Form. The Datasheet Form will be saved, but it will not be closed.

  3. Select Design View from the View Menu to change the Form to Design View mode.

The Form in Design View should look like the image shown below, although the appearance may vary slightly depending on your version of Microsoft Access.

Doesn't matter how the Fields are arranged on the Form, whether in Row or Column Format, the data will always be displayed in Datasheet format. The placement of Fields will not affect the way the data is displayed on the Datasheet, but the Tab Order does.

The Tab Order of Controls

Let us find out how the Field's Tab Order influences the Datasheet View.

  1. Change the Form into Datasheet View (View -> Datasheet View) and check the order of fields appearing in there.

  2. Change the View into Design mode again and re-arrange the ProductID and UnitPrice fields by switching their places.

  3. Change the View back to Datasheet and inspect the data field order.

    There will not be any change in the Order of Fields displayed from the previous View. If you move the Cursor using the Tab Key, then it moves in the same order as the fields' placement, as you have seen earlier before switching the fields.

  4. Change the Form back to Design View again.

  5. Select Tab Order... from the View menu. Click on the left border of the UnitPrice field on the Tab Order Control, click and drag it up, and place it below the OrderID field.

    Tip: You can click on the Auto Order Command Button to re-arrange the Tab Order according to the field placements on the Form.

  6. Open the Form in normal view now and check the change of field placements.

The Unbound Text Box

We will add one Unbound Text Box on the Form to calculate the Extended Price after adjusting the discounts of each item.

  1. Open the form in design view if you have closed it.

  2. Drag the Form Footer Section down to get more room to place another Text Box below, or you can place it to the right of the Discount Field, too.

  3. Create a Text Box and write the formula =(1-[Discount])*[UnitPrice]*[Quantity] in it.

  4. While the Text Box is still in the selected state, display the Property Sheet (View -> Properties).

  5. Change the Format Property value to Currency format. Change the Name Property value to Extended Price.

  6. Open the Form in normal view and check the newly added Text control heading at the top. It will be something like Text10:.

    In Datasheet View of Forms, MS-Access uses the Caption of the Child Labels attached to the text boxes as Field headings. We have changed the Name Property of the Text Box to Extended Price, but that is ignored here.

  7. Now, change the Form into the design view and delete the Child Label attached to the Extended Price Text Box.

  8. Change to Datasheet view again and check the field name appearing at the top; it will be Extended Price now.

Datasheet Event Procedure

  1. To try an Event Procedure on the Datasheet view, copy and paste the following VBA Code into the Form's Code Module (View -> Code to display the Code Module of the Form) and save the Form with the Code.

    Private Sub UnitPrice_LostFocus()
    Dim newUnitPrice As Double, msg As String
    Dim button As Integer
    
    button = vbQuestion + vbYesNo + vbDefaultButton2
    
    If Me![UnitPrice] = Me![UnitPrice].OldValue Then
       msg = "Replace UnitPrice: " & Me![UnitPrice].OldValue & vbCr & vbCr
       msg = msg & "with New Value: " & Me![UnitPrice]
    
       If MsgBox(msg, button, "UnitPrice_LostFocus()") = vbNo Then
            Me![UnitPrice] = Me![UnitPrice].OldValue
        End If
    End If
    
    End Sub
    

    We will attempt to trap the change in the UnitPrice field and will ask the user to confirm whether to retain the change or cancel it.

  2. Open the Form in the datasheet view and make some changes in the UnitPrice Field and leave the Field by pressing the Tab Key or Enter key.

A Message Box will appear asking for permission to retain the change or to cancel it.

Datasheets can be programmed with Event Procedures (Field level or Form level) for validation checks and display of information.

Displaying of Summary Information

Method-1

We will attempt to answer the second question we have raised on top of this page.

  1. Open the Order_Details Form in Design View.

  2. Drag the Form Footer Section down to get enough room to place two TextBoxes. Create two TextBoxes in the Form Footer Section.

  3. Write the formula =Sum([Quantity]) in the first Text Box.

  4. Display the Property Sheet of the Text Box and change the Name Property value to TOTALQTY.

  5. Write the formula =Sum((1-[Discount])*[UnitPrice]*[Quantity]) in the second Text Box.

  6. Change the Name Property Value to TOTALVALUE.

When we open the Order_Details Form in Datasheet View, it will calculate the Summary Values in TOTALQTY and TOTALVALUE TextBoxes on the Footer of the Form, but we must do something to display them.

The first idea that usually comes to mind is to use a MsgBox to display the results within a Form’s event procedure. However, since the underlying records may change over time, these updates should be reflected in the summary values. Therefore, we must ensure that the results can be refreshed to display the latest data before showing them again.

We will implement this method before we settle on a better one.

  1. Copy and paste the following Code into the Form's Code Module and save the Form:

    Private Sub Form_DblClick(Cancel As Integer)    
    Dim msg As String
         Me.Refresh
        msg = "Total Quantity = " & Me![TOTALQTY] & vbCr & vbCr
        msg = msg & " | Total Value = " & Format(Me![TOTALVALUE], "Currency")
    
         MsgBox msg
    End Sub
    
  2. Open the Form in Datasheet View.

  3. Double-click on the Record Selector at the left border of the Form.

    A Message Box pops up with the Summary Values from the TextBoxes in the Form Footer Section.

  4. Make some changes to the Quantity and UnitPrice fields to try Step 3 again. The values change will appear in the Message Box.

  5. You can filter the Data on ProductID or on OrderID by right-clicking on these fields and selecting Filter by Selection or other Options available on the displayed Shortcut Menu, and by executing Step 3 to get the Summary of selected records.

Method-2

After trying out the above method, your response may be something like "Yah.. it serves the purpose, but it doesn't give the impression of a sophisticated method. After all, it takes so many clicks and pop-up Message Boxes". I agree with you, too.

With a small change to the above Code, we can make the results the way you like them, I hope!

  1. Open the Form in Design View.

  2. Display the Property Sheet (View -> Properties).

  3. Select the Mouse Move Property and select Event Procedure from the drop-down control.

  4. Click on the build (...) button on the right side of the Property to open the Form's Code Module.

  5. Cut the Code lines from within the Form_DblClick() Event Procedure:

    Private Sub Form_DblClick(Cancel As Integer)
    
    End Sub
    

    Leave the above lines alone and paste the Code into the Form_MouseMove() Event Procedure.

  6. Change the line that reads:

    MsgBox msg

    to

    Me.Caption = msg

    After the change, the Code will look like the following:

    Private Sub Form_DblClick(Cancel As Integer)
    
    End Sub
    
    Private Sub Form_MouseMove(button As Integer, Shift As Integer, X As Single, Y As Single)
    Dim msg As String
        Me.Refresh
        msg = "Total Quantity = " & Me![TOTALQTY] & vbCr & vbCr
        msg = msg & " | Total Value = " & Format(Me![TOTALVALUE], "Currency")
        Me.Caption = msg
    End Sub
    
  7. Open the Form in Datasheet View and move the Mouse over into the data area by crossing the Field Headings or the Record Selectors on the left side.

  8. Check the Title Area of the Datasheet View, and you will find the Summary information is displayed there. A sample image is given below:

Now try changing the field values or filtering the data and moving the Mouse over the Field Headings or Record Selectors at the left to get the result on the Title Bar instantly. No Message Boxes or Double-Clicks, and what do you say about that?

Saturday, November 8, 2008

Sum Min Max Avg ParamArray

Introduction.

I’m sure your first reaction after reading the title might be, “I already know all that—tell me something new!” If you haven’t come across the last item in the title (the odd one out), then that’s exactly what I plan to share here—so keep reading.

The first four terms are quite familiar; they refer to built-in functions in Microsoft Access and worksheet functions in Excel. We’ll get to the last one a little later, but first, let’s look at how the Min() function works in Excel—and why using it in Microsoft Access presents a few challenges in comparison.

Difference between Excel and Access

We’re certainly not forgetting the other domain aggregate functions in Access—DCount(), DSum(), DMin(), DMax(), and DAvg().

Let’s start by looking at how the Min() worksheet function works in Excel. It can identify the minimum value from a range of cells in a single column, a row of cells across multiple columns, or even a block of cells spanning several rows and columns.

However, when we return to Microsoft Access, the Min() function behaves differently. It can only be applied to a single column (that is, a single field) within a query, or in the header and footer sections of forms or reports. So, how do we determine the minimum value across multiple fields?

Go through the sample table below to better understand the issue we’re dealing with.

We have received Quotations for Electronic Items from three different Suppliers, and we need to know which one is the lowest and from which Supplier. In this case, our Min() Function has no use here unless we reorganize the above data in the following format:

To obtain the desired result from this data, we’ll need to create two queries, setting aside—for now—issues such as duplicate descriptions, supplier names, or the overall table size.

  1. First Query (Total Query):
    Group the data by the Desc field and use the Min() function to determine the lowest value from the Values field.

  2. Second Query:
    Use both the original table and the first query as data sources. Join them on the Desc and MinOfValues fields from the Total Query with the Desc and Values fields of the base table. This will return all records from the table that match both the description and the lowest quoted value.

The ParamArray Method

I consider these steps to be excessive work, and I know you will agree too. Instead, we can write a User Defined Function with the use of ParamArray and pass the Field Names to the Function and find the Minimum Value from the list. Here is a simple Function with the use of the ParamArray declaration to find the Minimum Value from a List of Values passed to it.

Public Function myMin(ParamArray InputArray() As Variant) As Double
'------------------------------------------------------------------
'Author : a.p.r. pillai
'Date   : November-2008
'URL    : www.msaccesstips.com
'All Rights Reserved by www.msaccesstips.com
'------------------------------------------------------------------
Dim arrayLength As Integer, rtn As Double, j As Integer

'calculate number of elements in Array
arrayLength = UBound(InputArray())

'initialize Null values to 0
For j = 0 To arrayLength
   InputArray(j) = Nz(InputArray(j), 0)
Next
'initialize variable with 1st element value
'or if it is zero then a value with high magnitude
rtn = IIf(InputArray(0) = 0, 9999999999#, InputArray(0))

For j = 0 To arrayLength
    If InputArray(j) = 0 Then
 GoTo nextitem
   If InputArray(j) < rtn Then
        rtn = InputArray(j)
    End If
nextitem:
Next

myMin = rtn
End Function

Copy and paste the above Code into a Global Module and save it.

A few simple rules must be kept in mind while writing User Defined Functions using the ParamArray declaration in the Parameter list of the Function.

  1. While declaring the Function, the Parameter Variable InputArray() (or any other name you prefer) must be declared with the keyword ParamArray, in place of ByRef or ByVal we normally use to declare parameters to functions.

  2. The Data Type must be a Variant type.

  3. The ParamArray declaration must be the last item in the Parameter list if the UDF accepts more than one Parameter.

  4. The Optional parameter declarations should not appear before the ParamArray declaration.

  5. Since the data type is Variant, it can accept any value type.

Using the above myMin() Function, we have created a Query on the first Table given above. The SQL and the resulting image of the Query in Datasheet View are shown below.

SELECT MaterialQuote.Desc,
 MaterialQuote.Supplier1,
 MaterialQuote.Supplier2,
 MaterialQuote.Supplier3,
 mymin([supplier1],
[supplier2],
[supplier3]) AS Minimum,
 IIf([minimum]=[supplier1],"Supplier1",IIf([minimum]=[supplier2],"Supplier2",IIf([minimum]=[supplier3],"Supplier3",""))) AS Quote
FROM MaterialQuote;

In the above example, we have used only three Field Values to pass to the Function, and these can vary depending on your requirement.

Modified Version of VBA Code

A modified version of the same function is given below that accepts a Calculation Type value (range 0 to 3) as the first Parameter, and depending on that, we can find the Summary, Minimum, Maximum, or Average values passed to it through the InputArray() Variable.

Option Compare Database

Enum SMMA
    accSummary = 0
    accMinimum = 1
    accMaximum = 2
    accAverage = 3
End Enum

Public Function SMMAvg(ByVal calcType As Integer, ParamArray InputArray() As Variant) As Double
'------------------------------------------------------------------------
'calType : 0 = Summary'        : 1 = Minimum
'        : 2 = Maximum'        : 3 = Average
'------------------------------------------------------------------------
'Author  : a.p.r. pillai'Date    : November 2008
'URL     : www.msaccesstips.com
'All Rights Reserved by www.msaccesstips.com
'------------------------------------------------------------------------
Dim rtn As Double, j As Integer, arrayLength As Integer
Dim NewValue As Variant

On Error GoTo SMMAvg_Err

If calcType < 0 Or calcType > 3 Then
     MsgBox "Valid calcType Values 0 - 3 only", , "SMMAvg()"
     Exit Function
End If

arrayLength = UBound(InputArray())
'Init Nulls, if any,  to 0
For j = 0 To arrayLength
   InputArray(j) = Nz(InputArray(j), 0)
Next

For j = 0 To arrayLength
    NewValue = InputArray(j)
    'skip 0 value
    If NewValue = 0 Then
 GoTo nextitem
    End If
    Select Case calcType
    'Add up values for summary/average
        Case accSummary, accAverage
            rtn = rtn + NewValue
        Case accMinimum
            rtn = IIf(NewValue < rtn, NewValue, rtn)
            rtn = IIf(rtn = 0, 9999999999#, rtn)
        Case accMaximum
            rtn = IIf(NewValue > rtn, NewValue, rtn)
    End Select
nextitem:
Next

'Calc Average
If calcType = accAverage Then
   rtn = rtn / (arrayLength + 1)
End If

SMMAvg = rtn

SMMAvg_Exit:
Exit Function

SMMAvg_Err:
MsgBox Err.Description, , "SMMAVG()"
SMMAvg = 0
Resume SMMAvg_Exit
End Function

The Function name was defined using the first letters of the Calculation Types that the Function can perform, and I hope you like it too.

When any of the values in the InputArray() element is zero, that is ignored and will not be taken as the minimum value.

Sample Runs on Immediate Window:

? SMMAvg (0,0,10,5,7) 'Summary
 Result: 22 

? SMMAvg (1,0,10,5,7) 'Minimum value from 0,10,5,7
 Result: 5
 
? SMMAvg (2,0,10,5,7) 'Maximum value from 0,10,5,7
 Result: 10 
 
? SMMAvg (3,0,10,5,7) 'Average
 Result: 5.5 
  

We can use this Function in Text Boxes on Forms,  Reports, or from other Controls. Use it at your own risk.

Saturday, November 1, 2008

Textbox And Label Inner Margins

Introduction.

Whether it’s a Form or a Report, a well-crafted design always draws the attention of both the user and any onlooker. Everyone designs forms and reports—but if you give the same task to five different people, each will produce a unique result based on their individual skills and sense of artistry unless they all rely on the same built-in wizards.

Most users focus primarily on the information presented in a report and want it organized and accurate. However, how that information is presented is entirely up to you—and it often depends on how much time you can invest in the design process. Remember, you typically design a report only once as part of a project, so it’s worth doing it thoughtfully.

Your report may eventually circulate far and wide, through faxes, emails, or shared databases, and reach a much wider audience. When compared with other reports circulating around, you’d want someone to pause and ask, “Who designed this one?” Fortunately, Microsoft Access provides all the tools you need to create visually appealing, professional-quality reports and forms. With just a bit more time and creativity, you can achieve remarkable results using the simple yet powerful tools Access offers.

Precision Positioning of Data

Here, I’d like to introduce you to a few important properties of TextBoxes and Labels on a report—and show you how a few simple design adjustments can transform an ordinary layout into a professional, visually appealing report.

The image below shows a Tabular Report created using the Report Wizard in Microsoft Access.

Wizards are excellent tools for quickly laying out all the objects on Forms or Reports with default formatting for font type, size, and style—saving a significant amount of design time. All that’s left is to fine-tune the layout to suit your preferences.

A Sample Project.

If you’d like to try this simple design step by step, import the Shippers table from the sample database at
C:\Program Files\Microsoft Office\Office11\Samples\Northwind.mdb. (That path is for Access 2003 — if you’re using a different version, look for the appropriate \Office##\ folder.) After importing, select the Shippers table, choose Report from the Insert menu, and pick Auto Report: Tabular from the options. Access will generate the report in moments. A preview of the report in Print Preview is shown below:

The modified version of the same Report in Print Preview below:

The transformation was easy with only a few changes to the above design, and I know which change you have noticed first. If I have turned on the borders of the TextBoxes and Labels alone, then the Print Preview will look like the one below:

Make the following changes to the above design:

  1. Delete the thick line under the Header Labels.

  2. Point the Mouse on the vertical ruler to the left of the Header Label Shippers, so that it turns into an arrow pointing to the right, and then click and drag along the ruler downwards so that you can select all the Labels and Text Boxes in the Report Header, Page Header, and Detail Sections together.

    Alternatively, you can click on an empty area of the Report and drag the Mouse over all the controls to select them. Do not select the Page Footer Section Controls. We don't need them in this Report.

  3. Display the Property Sheet (View -> Properties) and change the following Values:

    • Border Color = 9868950
    • Special Effect = Flat
    • Border Style = Solid
    • Border Width = Hairline

    You need to change only the Border Color Value; others will be there as default. If not, then change them as given above.

  4. Select all the Field Header Labels alone in the Page Header Section, as we did in Step 2 above. Select Format -> Align -> Left to arrange the labels close together horizontally without leaving gaps between them.

  5. Display the Property Sheet of the selected Labels (View -> Properties) and change the Top Property Value to 0 and Height Property Value to 0.4167"

    Centralizing Text Vertically.

  6. Centralize the Text horizontally within the Labels by changing the Text Align Property Value to Center, while all the Labels are still in the selected state.

  7. Select all the TextBoxes in the Detail Section together and select Format -> Align -> Left to arrange the TextBoxes close together without leaving gaps between them.

  8. Display the Property Sheet of the TextBoxes (if you have already closed it) and change the Top Property Value to 0 and the Height Property Value to .2917" so that the data lines are not too close and crowded when Previewed/Printed.

  9. If there is a gap below the Labels in the Page Header Section and below the TextBoxes in the Detail Section, then close them by dragging up the Detail Section Header and the Page Footer Bars.

  10. Delete all the Page Footer Section controls. Close the gap by dragging the Report Footer Bar up.

  11. Next, resize the Report Header label containing the Shippers heading so that it spans the combined width of all the field header labels in the Page Header section. You can do this either by adjusting it manually by eye or by opening the Property Sheet for each header label, noting their individual Width property values, adding them together, and then setting the Width property of the Shippers heading label to that total.

  12. Change the Height Property Value to 0.416 and the Text Align Property Value to the Center.

  13. Save your Report with a Name of your choice.

    With the above modifications, the Report Print Preview image is shown below, and needs to be corrected.

The Report looks good, but with a few more cosmetic changes, it will look even better.

  1. The Field Header Labels' Caption Text must be vertically centered.

  2. The Shipper ID Numbers and other field values are too close to the Border Line, and they should be positioned a little away from the border.

  3. Open the Report in Design View and select all the Field Header Labels together as we did earlier.

  4. Display the Property Sheet and drag the right scroll bar of the Property Sheet down to the bottom. There, you will find the Inner Margin Properties that you can use to position the Text within the Controls.

    NB: These Properties are available only in MS Access 2000 and later versions.

  5. Change the Top Margin Property Value of Header Labels to 0.1".

  6. Select the Text Controls together on the Detail Section and change the Top Margin Property Value to 0.0701".

  7. Select the Shipper ID, TextBox in the Detail Section, and change the Right Margin value to 0.1".

  8. Select the Company Name TextBox, and change the Left Margin Value to 0.0597" and set the same value for Phone Number also.

  9. Save your Report and open it in Print Preview. It is similar to the 3rd Image from the top of this page.

Although the explanation may seem lengthy, once you understand the steps, you can complete the design in just a few minutes.

Powered by Blogger.