Learn Microsoft Access Advanced Programming Techniques, Tips and Tricks.

Showing posts with label Calculation. Show all posts
Showing posts with label Calculation. Show all posts

Diminishing Balance Calc in Query

Introduction.

This is an offshoot of the earlier Function Running-Sum, in Query Column.  With few changes in the earlier Function RunningSum() we can easily calculate and find the loan balance-to-pay amount, after deducting the monthly paid amount, at each record level.

The loan amount is payable in monthly installments.   Our simple task is to show the diminishing balance of the loan amount against each record-level installment amount in a separate Column of the Query.  The last record will have the remaining balance payment amount.

Let us pretend that the Loan Repayable Total Amount is 1000.

Sample Query Recordset.

The sample installment payment detail records are taken from the earlier Post: Running-Sum in Query Column as given below.

The Query SQL that calls the DiminishingBal() Function.

The SELECT Query SQL that calls the DiminishingBal() Function, in a separate Column of the Query.

SELECT Table_Units.ID, Table_Units.Units, DiminishingBal([ID],"ID","Units","DiminishingQ1") AS DiminishingBalance
FROM Table_Units;

The Query Recordset Image, with the result in the last column,  is given below:

We are using the same Query record set used as a source for the earlier RunningSum() Function and used here also for demonstration purposes.  The Recordset should have a Unique value (Numeric or String) field and be used as the first parameter to the Function.

The Total Repayable Loan Amount is kept in a separate Table.

The Total Amount to be repaid to the Bank (1000) is kept in a separate Table with the following structure:

The DiminishingBal() Function VBA Code.

The VBA Code of DiminishingBal() Function is given below:

Option Compare Database
Option Explicit

'Declare a Generic Object
Dim D As Object

Public Function DiminishingBal(ByVal IKey As Variant, ByVal KeyFldName As String, ByVal SumFldName As String, ByVal QryName As String) As Double
'-----------------------------------------------------------
'Function: DiminishingBal()
'Purpose : Calculate Diminishing Balance in a separate Column
'The Query can be used as source for other Processing needs,
'for Form View or Report
'-----------------------------------------------------------
'Author  : a.p.r. pillai
'Date    : December 2019
'Rights  : All Rights Reserved by www.msaccesstips.com
'-----------------------------------------------------------
'Parameter List, in the Order of it's placement
'1. Key Value Data Field
'2. Key-Field Name in String Format
'3. Field-Name for Calcuating Running Sum in String Format
'4  Query-Name in String Format
'-----------------------------------------------------------
'Remarks: The Key-Value Field should have Unique Numeric or
'String Values.
'-----------------------------------------------------------
Static K As Long, X As Double, fld As String, y As Long
Dim p As Variant

On Error GoTo DiminishingBal_Err

y = DCount("*", QryName)
'If the Function is not called by the same Query
'then initialize Dictionary Object and Variables
If SumFldName <> fld Or K > y Then
   fld = SumFldName
   Set D = Nothing
   K = 0
   X = 0
End If


K = K + 1
If K = 1 Then 'The major process of the function starts here
    Dim DB As Database, rst As Recordset
    
    'Create and instantiate the Dictionary Object
    Set D = CreateObject("Scripting.Dictionary")
    
    'Get Loan Repayable Amount
    X = DLookup("[LoanAmt]", "tblRepay", "[id] = 1")
    
    'Open the EMI Recordset
    Set DB = CurrentDb
    Set rst = DB.OpenRecordset(QryName, dbOpenDynaset)
    'Calculate cumulative record-level summary and
    'add the value into Dictionary Object as it's Item
    While Not rst.EOF And Not rst.BOF
    'read the record summary field value and add it to total
         X = X - rst.Fields(SumFldName).Value
    'read current record key field value
         p = rst.Fields(KeyFldName).Value
    'add the total value to dictionay object
    'as Key, Item pair
         D.Add p, X
    ' repeat this process for all records
         rst.MoveNext
    Wend
    
    'close recordset and remove the database objects
    rst.Close
    Set rst = Nothing
    Set DB = Nothing
    
    'Retrieve the first item from Dictionary,
    'using the first Key passed as parameter,
    'and return to the function calling record in the Query
    
   DiminishingBal = D(IKey)
Else
   'Subsequent calls with the record Key passed as parameter
   'will retrieve other record values from Dictionary and
   'returns to their corresponding records in the Query.
   
   DiminishingBal = D(IKey)
End If

'A control forcing to initialize the static variables
'when the program is rerun for the same query.
   If K = y Then
      K = K + 1
   End If

DiminishingBal_Exit:
Exit Function

DiminishingBal_Err:
MsgBox Err & ":" & Err.Description, vbOKOnly, "DiminishingBal()"
Resume DiminishingBal_Exit
End Function

How the Function Works.

In the Global declaration area of the VBA Module, Variable D is declared as an Object.

The DiminishingBal() Function needs four parameters:

  1. A Unique Value Field (Numeric or String Values) is the first parameter.  The parameter is declared as a Variant data Type.
  2. The Unique Value’s Field Name is the second parameter in String format.
  3. The Loan Installment Value Field Name.
  4. The Query Name is the fourth Parameter.
  5. Four Static Variables K, X, fld, and y are declared.  They must be Static Variables to retain their values between repeated calls of the Function, from each record of the Query.  The Variable p is declared as a Variant Type normal variable, to hold the Key-Value (either Numeric or String) of each record.

    The DCount() Function takes the record count of the Query in Variable y. The Value in this Variable is used as a control to check when to Reset the Static Variable Values to their initial Values and to remove the Dictionary Object from memory.  This control is necessary if the same Query is run more than once, consecutively.

    1. If the value in the control-variable K is more than the Query record count in variable y then resets the Static variables to their initial values and the Dictionary Object is deleted from memory.
    2. Or, If the installment value Field Name is different, from the field name saved in Variable fld during the last call of the function,  then it assumes that the Function is called from a different Query Column and resets the Static Variable Values. The Dictionary object is deleted from memory.

    Next, Variable K is incremented by 1.  When K=1 the main action of the Function starts.  The Database and Recordset Objects are declared in the DB and the rst Variables respectively.

    In the next executable statement Set D = CreateObject("Scripting.Dictionary") creates the Dictionary Object with the CreateObject() method and assigns it to the Object variable D, which was declared in the Global Area of the Module.

    There are other ways to declare and use this Object by adding the Microsoft Scripting Runtime File to the Microsoft Access Reference Library List.  After that you can create an instance of the Dictionary Object in the following way:

    Dim D As Dictionary
    Set D = New Dictionary
    

    If you are new to the Dictionary Object and its usage visit the Post: Dictionary Object Basics.  There are five Posts on this Topic, and you can find the links at the end of this Page.

    Next we need the value of repayable total Loan Amount and retrieves it from it’s Table tblRepay with the Dlookup() Function in the statement: X = DLookup("[LoanAmt]", "tblRepay", "[id] = 1").  There is only one record in the Table with ID number 1 and you may omit the criteria part.

    The Query Recordset is open to read records one by one. The first record’s amount, paid to the bank, is deducted from the Loan Amount (1000) in Variable X.  The Unique Key value of the record is retrieved from the record and saved in variable p, in the next statement.

    The balance loan amount, calculated after deducting the repaid Amount, is added to the Dictionary Object, with Dictionary Object’s  Add Method, as its Item Value, with the Unique Key field value in the variable p as Dictionary-Key in the statement: D.Add p, X. The Dictionary’s Add method always adds its Item value in Key, Item pairs.

    Note: If the Key-Value is not Unique then the Add method fails and will end up with Errors.

    With the rst.MoveNext statement takes the next record for the same sequence of processing and adds the result value to the Dictionary Object.

    This way individual record value is deducted from the remaining balance loan amount at that level and added to the Dictionary Object as its Item.

    Note: Here, you may ask why the Dictionary Object is chosen to hold all the calculated values rather than in an Array.  Yes, It can be done, but that method needs more statements to store and retrieve the values in a Two Dimensional Array. It will become more complicated when the Query Record’s Unique Key Value is in String form.  The Dictionary Object allows the value retrieved in either sequential or random order based on its Key.  Here, the Random method works fine with the Key-Value Type in the Numeric or String form.

    When all the record processing is complete the record set and Database Objects are closed.

    What you have to keep in mind at this point is that still the value in variable K=1 and the first Parameter IKey retains the first records Unique Id Value.  At the first record level call of the function DiminishingBal() itself, we have calculated all the record level balance loan amount values, one by one, and added the result to the Dictionary Object as its Items. The function parameter IKey still holds the first record’s Unique ID value. That is the reason why we have used a separate variable p for individual record key values while processing all the records.

    So, the entire record level processing is done during the first call of the function, initiated from the first record of the Query, and all the record level result values are held in temporary storage in the Dictionary Object.

    The next statement DiminishingBal = D(IKey) retrieves the first value and added to the Dictionary using the unique parameter value IKey  and returns the value to the calling first record of the Query.

    The next call from the second record of the Query increments the variable K, by 1 (now K=2), and the program takes to the ELSE path of the IF. . .Then statement, retrieves the second Item value from the Dictionary Object, using the IKey parameter, and returns it to the respective record of the Query.

    The rest of the DiminishingBal() function call, from the remaining records, will route the program control only through the ELSE  path, because the value in Variable K is greater than one, retrieves the values from Dictionary Item, and returns it to the function calling record.

    The Next If . . . Then statement checks whether the value in variable K = y or notVariable y holds the total record count of the Query.  If it is found True then it assumes that the last call of the DiminishingBal() function has arrived.  At this point, the K Variable is made greater than the value in variable y.

    This is necessary to initialize the Static Variables during the rerun of the same Query. In case of any change made on the Source Data before rerun, it will not reflect on the balance amount calculated earlier, because it will keep taking the ELSE route of the If . . . Then statement and retrieves the old value from Dictionary Object.

    The Demo Database, with all the necessary Objects and the Code Module, is attached for your convenience to download and try it out straight away.


    Dictionary Object Links.

    1. Dictionary Objects Basics
    2. Dictionary Object Basics- 2
    3. Sorting Dictionary Object Keys and Items
    4. Display Records from Dictionary
    5. Add Class Objects as Dictionary Items
    6. Update Class Object Dictionary Item
Share:

Date2Text and Text2Date Functions

Introduction.

We already have some frequently used Report Footer formatting simple functions, like Report Page Number formatting function =PageNo([page],[pages]),  output: Page: 1/20 –> Page: 20/20, Report Period Function =Period([StartDate], [EndDate]), output : Period: 15/09/2007 To 30/09/2007 and =Dated()  Function, output: Dated: 15/09/2007 on Report Footer.  Even though they are simple Report Header/Footer formatting functions, it saves report design time.  Check the following Links, if you have not yet come across those Functions earlier:

Useful Report Functions.

Continued on Page 2/- an indicator Label on Report Page Footer, on multi-page reports.

Our new Function formats the Date Value in the following sample Text form:

  Sunday, 27th October 2019.

The Date2Text() Function Code.

Public Function Date2Text(ByVal dt As Date) As String
Dim txt As String, num As Integer

num = Day(dt)

   Select Case num
       Case 1, 21, 31
          txt = "st "
       Case 2, 22
          txt = "nd "
       Case 3, 23
          txt = "rd "
       Case 4 To 20, 24 To 30
          txt = "th "
   End Select
   
   Date2Text = WeekdayName(Weekday(dt)) & "," & Day(dt) & txt & MonthName(Month(dt)) & " " & Year(dt)
   
End Function

Copy and paste the above Code into the Standard VBA Module, save the Code and compile it.

Let us try out the Code directly from Debug Window. Press Ctrl+G to display the Debug Window if it is not already visible on the VBA editing Window.

Sample Test Runs.

D = #27-10-2019#

? Date2Text(D)
Result: Sunday, 27th October 2019

D=Cdate("22/10/2019")

? Date2Text(D)
Result: Tuesday, 22nd October 2019

D=DateValue("11/10/2019")

? Date2Text(D)
Result: Friday, 11th October 2019

? Date2Text(Date)
Result: Thursday, 31st October 2019

Weekday 1 to 7 is Sunday to Saturday. This depends on your Computer's Regional Settings. If it is not correct in your case, then change it in the Regional settings on your Computer.

Scope of this Function.

The Date2Text() Function can be placed in TextBox on Report Header, use on Date-Field Query Column, or on the Main Form (Main Switchboard or Control Screen, etc.) as general info.

The Text2Date() Function.

The Date2Text() Function’s complementary Function Text2Date() VBA Code is given below.

Public Function Text2Date(ByVal txtDate As String) As Date
Dim S, dt As String
    
    S = Split(txtDate, " ")
    dt = Str(Val(S(1))) & "-" & S(2) & "-" & S(3)
    Text2Date = DateValue(dt)

End Function

The Date converted into Text form can be changed back into a valid date format with the Text2Date() Function. There is no validation check performed on the input value for errors and expected to pass the parameter value in the correct input format, the same as the Date2Text() Function output.

Example:

? Text2Date("Thursday, 31st October 2019")

Result: 31-10-2019

Caution: If the parameter value is entered manually, then there should not be more than one space between each segment of the date text.

  1. Days in Month Function
  2. Custom Calculator and Eval Function
  3. Rounding Function MRound() of Excel
  4. Proper Function of Excel
Share:

Wrapper Class Functionality Transformation

Introduction.

Last week, we created a new Wrapper Class named ClsTiles, which used the ClsArea Class twice within the same Class Module—one instance to store the room’s floor dimensions and another to store the tile dimensions—to calculate the number of tiles needed for the room.

In this new Wrapper Class Module, we will take the existing ClsVolume2 Class and transform it into a ClsSales Class. With a few cosmetic changes, we will give it a complete facelift inside the Wrapper Class, concealing its original purpose as a volume calculation class and repurposing it to calculate the selling price of products with a discount.

Interestingly, the ClsVolume2 Class already has all the necessary properties to store sales-related data: strDesc, dblLength, dblWidth, and dblHeight can be repurposed to represent Description, Quantity, Unit Price, and Discount Percentage, respectively.

Remember, the ClsVolume2 Class itself is a derived class, originally built using ClsArea as its base class.ClsVolume2 Class Re-Visited.

But, first, the VBA Code of ClsVolume2 Class Module  (the Base Class for our new ClsSales Class Module) is reproduced below for reference:

Option Compare Database
Option Explicit

Private p_Height As Double
Private p_Area As ClsArea

Public Property Get dblHeight() As Double
    dblHeight = p_Height
End Property

Public Property Let dblHeight(ByVal dblNewValue As Double)
    p_Height = dblNewValue
End Property

Public Function Volume() As Double
    Volume = CArea.dblLength * CArea.dblWidth * Me.dblHeight
End Function

Public Property Get CArea() As ClsArea
   Set CArea = p_Area
End Property

Public Property Set CArea(ByRef AreaValue As ClsArea)
  Set p_Area = AreaValue
End Property

Private Sub Class_Initialize()
Set p_Area = New ClsArea
End Sub

Private Sub Class_Terminate()
Set p_Area = Nothing
End Sub 

The only obstacle preventing us from using the ClsVolume2 Class directly for sales data entry is that its Property Procedure names—dblLength, dblWidth, and dblHeight—do not correspond to the required sales-related properties: Quantity, Unit Price, and Discount Percentage.

Fortunately, all these properties in the ClsVolume2 Class are of the Double data type, which is perfectly suitable for our sales calculations, so no data type changes are needed.

Similarly, the public function names Area() and Volume() are not meaningful in the context of sales, but their underlying calculation logic can be repurposed for sales-related computations without modification.

a) Area = dblLength * dblWidth is suitable for TotalPrice = Quantity * UnitPrice

b) Volume = Area * dblHeight is good for DiscountAmount = TotalPrice * DiscountPercentage

Here, we have two choices to use the ClsVolume2 as the ClsSales Class.

  1. The easiest approach is to create a copy of the ClsVolume2 Class and save it as a new Class Module named ClsSales. Then, update the Property Procedure names and public Function names to reflect sales-related values and calculations. You can also add any additional functions needed for handling sales operations directly within this new class module.

  2. Create a Wrapper Class based on the ClsVolume2 Base Class. In the Wrapper Class, implement corresponding property procedures and public functions that encapsulate and rename the Base Class’s property procedures and functions, effectively masking their original names. Add new functions, if any, to the Wrapper Class as needed.

The first option is relatively straightforward and easy to implement. However, we will choose the second option to learn how to access the Base Class’s properties within the new Wrapper Class and how to mask their original names with new property names.

The Transformed ClsVolume2 Class.

  1. Open your Database and display the VBA Editing Window (Alt+F11).

  2. Select the Class Module from the Insert Menu to insert a new Class Module.

  3. Change the Class Module’s Name property Value from Class1 to ClsSales.

  4. Copy and Paste the following VBA Code into the Module and Save the Code:

    Option Compare Database
    Option Explicit
    
    Private m_Sales As ClsVolume2
    
    Private Sub Class_Initialize()
        'instantiate the Base Class in Memory
        Set m_Sales = New ClsVolume2
    End Sub
    
    Private Sub Class_Terminate()
        'Clear the Base Class from Memory
        Set m_Sales = Nothing
    End Sub
    
    Public Property Get Description() As String
      Description = m_Sales.CArea.strDesc 'Get from Base Class
    End Property
    
    Public Property Let Description(ByVal strValue As String)
      m_Sales.CArea.strDesc = strValue ' Assign to Base Class
    End Property
    
    Public Property Get Quantity() As Double
    Quantity = m_Sales.CArea.dblLength
    End Property
    
    Public Property Let Quantity(ByVal dblValue As Double)
       If dblValue > 0 Then
         m_Sales.CArea.dblLength = dblValue ' Assign to clsArea, Base Class of ClsVolume2
       Else
         MsgBox "Quantity: " & dblValue & " Invalid.", vbExclamation, "ClsSales"
         Do While m_Sales.CArea.dblLength <= 0
              m_Sales.CArea.dblLength = InputBox("Quantity:, Valid Value >0")
         Loop
       End If
    End Property
    
    Public Property Get UnitPrice() As Double
       UnitPrice = m_Sales.CArea.dblWidth
    End Property
    
    Public Property Let UnitPrice(ByVal dblValue As Double)
       If dblValue > 0 Then
          m_Sales.CArea.dblWidth = dblValue ' Assign to clsArea, Base Class of ClsVolume2
       Else
         MsgBox "UnitPrice: " & dblValue & " Invalid.", vbExclamation, "ClsSales"
           Do While m_Sales.CArea.dblWidth <= 0
              m_Sales.CArea.dblWidth = InputBox("UnitPrice:, Valid Value >0")
           Loop
       End If
    End Property
    
    Public Property Get DiscountPercent() As Double
       DiscountPercent = m_Sales.dblHeight
    End Property
    
    Public Property Let DiscountPercent(ByVal dblValue As Double)
    ' Assign to Class .dblHeight of ClsVolume2
    Select Case dblValue
        Case Is <= 0
           MsgBox "Discount % -ve Value" & dblValue & " Invalid!", vbExclamation, "ClsSales"
           Do While m_Sales.dblHeight <= 0
              m_Sales.dblHeight = InputBox("Discount %, Valid Value >0")
           Loop
        Case Is >= 1
           m_Sales.dblHeight = dblValue / 100
        Case 0.01 To 0.75
           m_Sales.dblHeight = dblValue
    End Select
    End Property
    
    Public Function TotalPrice() As Double
    Dim Q As Double, U As Double
    Q = m_Sales.CArea.dblLength
    U = m_Sales.CArea.dblWidth
    If (Q * U) = 0 Then
       MsgBox "Quantity / UnitPrice Value(s) 0", vbExclamation, "ClsVolume"
    Else
       TotalPrice = m_Sales.CArea.Area 'Get from Base Class ClsArea
    End If
    End Function
    
    Public Function DiscountAmount() As Double
       DiscountAmount = TotalPrice * DiscountPercent
    End Function
    
    Public Function PriceAfterDiscount()
       PriceAfterDiscount = TotalPrice - DiscountAmount
    End Function
     

So far in the Wrapper Class, we have created an instance of the ClsVolume2 class, renamed its property and function members to more suitable names, and added validation checks with appropriate error messages. We also ensured that invalid input does not trigger the Base Class’s own validation routines, which could otherwise display error messages like “Value of the dblLength property is invalid” from the ClsVolume2 class.

Review the highlighted lines in the code above; they should help you understand how the property values are assigned to and retrieved from the Base Class ClsVolume2.

First, review the ClsArea Class Module, and then examine the ClsVolume2 Class Module, which is derived from ClsArea. After understanding both of these, revisit the code in this Wrapper Class for a better perspective.

Test Program for ClsSales Class in Standard Module.

Let us write a Test Program to try out the Wrapper Class.

  1. Copy and Paste the following VBA Code into a Standard Module.

    Public Sub SalesTest()
    Dim S As ClsSales
    
    Set S = New ClsSales
    
    S.Description = "Micro Drive"
    S.Quantity = 12
    S.UnitPrice = 25
    S.DiscountPercent = 0.07
    
    Debug.Print "Desccription", "Quantity", "UnitPrice", "Total Price", "Disc. Amt", "To Pay"
    With S
        Debug.Print .Description, .Quantity, .UnitPrice, .TotalPrice, .DiscountAmount, .PriceAfterDiscount
    End With
    End Sub

    Run The Code.

  2. Keep the Debug Window open (Ctrl+G).

  3. Click somewhere in the middle of the Code and press the F5 key to run the Code and to print the output in the Debug Window.

  4. You can further test the code by entering negative numbers for any of the input values and running it to trigger the new error messages. You may also try disabling one or more input lines by placing a comment symbol (') at the beginning of the line, then rerun the code and observe the results.

Calculate Price/Discount for an Array of Products.

The following test code creates an array of three Products and Sales Values by entering directly from the Keyboard. 

Copy and paste the following Code into a Standard Module and run to test the Wrapper Class further.

Public Sub SalesTest2()

Dim S() As ClsSales
Dim tmp As ClsSales
Dim j As Long

For j = 1 To 3
Set tmp = New ClsSales
   tmp.Description = InputBox(j & ") Description")
   tmp.Quantity = InputBox(j & ") Quantity")
   tmp.UnitPrice = InputBox(j & ") UnitPrice")
   tmp.DiscountPercent = InputBox(j & ") Discount Percentage")
 ReDim Preserve S(1 To j) As ClsSales
 Set S(j) = tmp
 Set tmp = Nothing
Next

'Output Section
Debug.Print "Desccription", "Quantity", "UnitPrice", "Total Price", "Disc. Amt", "To Pay"
For j = 1 To 3
   With S(j)
       Debug.Print .Description, .Quantity, .UnitPrice, .TotalPrice, .DiscountAmount, .PriceAfterDiscount
   End With
Next

For j = 1 To 3
  Set S(j) = Nothing
Next

End Sub

Once the correct values have been successfully entered into the array, the product names and corresponding sales values are displayed in the Debug Window.

CLASS MODULES.

Demo Database Download


  1. MS-Access Class Module and VBA
  2. MS-Access VBA Class Object Arrays
  3. MS-Access Base Class and Derived Objects
  4. VBA Base Class and Derived Objects-2
  5. Base Class and Derived Object Variants
  6. Ms-Access Recordset and Class Module
  7. Access Class Module and Wrapper Classes
  8. Wrapper Class Functionality Transformation

COLLECTION OBJECT.

  1. Ms-Access and Collection Object Basics
  2. Ms-Access Class Module and Collection Object
  3. Table Records in Collection Object and Form

DICTIONARY OBJECT.

  1. Dictionary Object Basics
  2. Dictionary Object Basics-2
  3. Sorting Dictionary Object Keys and Items
  4. Display Records from Dictionary to Form
  5. Add Class Objects as Dictionary Items
  6. Update Class Object Dictionary Item on Form
Share:

Ms-Access VBA Class Object Arrays

Introduction.

Last week, we introduced a simple custom class object with two properties—Length and Width—and a method to calculate the Area of any item using these values. I hope you now have a good understanding of the basics of an MS Access custom class object. If not, please review this page before continuing: MS Access Class Module and VBA.

Class module objects can significantly reduce the amount of repetitive code in your programs. Whenever you find yourself writing the same logic repeatedly, consider whether it could be encapsulated in a class module. (Of course, some repetitive code may also be handled with utility functions, but class objects require a different approach.)

Creating a class object may seem like extra work, as refining its functionality often requires more code initially. However, your main modules remain simpler, while the detailed and more complex code stays hidden inside the class—organized, reusable, and easier to maintain.

For now, we’ll build on our simple class module and extend it to create an array of objects and calculate the area of multiple items.

The ClassArray() Sub-Routine.

The following sample VBA code, placed in a Standard Module, creates an array of five objects from the ClsArea class. It then prints their property values and method results in the Debug Window.

You can copy and paste the code into a standard module, but it’s even better to type it out yourself—this way, you’ll understand each line and what it does more thoroughly.

Public Sub ClassArray()
Dim tmpA As ClsArea
Dim CA() As ClsArea
Dim j As Long, title As String
Dim L As Long, U As Long

title = "ClassArray"
For j = 1 To 5 ‘the Loop is set for 5 items
  'instantiate temporary ClsArea Object
  'to enter input
  Set tmpA = New ClsArea

tmpA.strDesc = InputBox(Str(j) & ") Description:", title, "")
     tmpA.dblLength = InputBox(Str(j) & ") Enter Length:", title, 0)
     tmpA.dblWidth = InputBox(Str(j) & ") Enter Width:", title, 0)
    
  'Redimension Array
  ReDim Preserve CA(1 To j) As ClsArea
  Set CA(j) = tmpA   'Copy Object to Array
  
Set tmpA = Nothing 'Remove temporary object from memory.
Next


‘PRINTING SECTION
'Print the Array contents into the Debug Window
L = LBound(CA)
U = UBound(CA)

Debug.Print "Description", "Length", "Width", "Area"
For j = L To U
  With CA(j)
    Debug.Print .strDesc, .dblLength, .dblWidth, .Area
  End With
Next

’stop

'Clear array objects from memory
  Erase CA


End Sub
 

Click somewhere in the middle of the Code and press F5 to execute the Code. Input the Values for Description, Length, and Width from the Keyboard for 5 different Items, one after the other.

The Sample Run of the Program is given below:

Description   Length        Width         Area
Door Mat       5             8             40 
Carpet         12            23            276 
Bed Room       21            23            483 
Store Room     15            25            375 
Terrace        40            50            2000 

Review of Code Line by Line.

Let’s examine the code line by line. In the declarations area, we first declare an object variable:

Dim tmpA As ClsArea

The next line declares an empty array of the same type:

Dim CA() As ClsArea 

The array is left un-sized here — we’ll set its length later in the procedure (typically with ReDim) to hold the required number of ClsArea instances.

In the next two lines, the variable title is declared as a String, and the variables j, L, and U are declared as Long integers.

The title variable is then initialized with the string "ClassArray", which will be used as the prompt text in the InputBox() function.

The For… Next loop is set up with j as the control variable, running five times. After the next two comment lines, the Set statement is used to instantiate (allocate memory for) the ClsArea class object, assigning it to the temporary object variable tmpA.

The following three lines of code assign input values to the strDesc, dblLength, and dblWidth properties of the tmpA object.

After another comment line, the ClsArea object array (CA) is resized with the ReDim statement to hold elements from 1 to j. On the first pass, this creates space for element 1; on subsequent passes, the array is resized to hold elements 1 to 2, 1 to 3, and so on, up to 1 to 5. The Preserve keyword ensures that previously created objects in the array are retained while expanding its size.

Note: The ReDim statement changes the size of the object array, but without the Preserve keyword, it erases any objects already stored in the array. Using Preserve ensures that existing objects remain intact when the array is resized.

The Set statement in the next line copies the tmpA object, along with its data, into the CA(j) object, the newly created j-th element of the array.

Immediately afterward, Set tmpA = Nothing removes the temporary object from memory.

The For…Next loop then repeats this process four more times, allowing input for additional items. Each time, a new tmpA object is created, populated with data, and then stored in the CA array.

After the loop, two lines of code determine the lower and upper bounds of the array (the smallest and largest index values).

Next, a Debug.Print statement prints a heading line in 14-character-wide columns in the Debug window. A second For…Next loop then iterates through the array, using the LBound and UBound values to access each object.

Inside this loop, a With…End With structure is used to simplify referencing the current object (CA(j)). This avoids repeatedly writing CA(j).strDesc, CA(j).dblLength, CA(j).dblWidth, and CA(j).Area. Instead, the property values and the Area() method result are printed more concisely.

Finally, you can remove the comment marker from the Stop statement to pause the program during execution. Run the code again, enter five sets of item details, and experiment with the object array. You can access or modify any property of any object in the array by referencing it with its index number, and you can print the results in the Debug window as shown.


The statement Set CA(j) = Nothing within the For ... Next Loop clears the array of objects, one by one, from Memory.

Pass Class Object Array as Function Parameter.

We can also pass the Class Object Array as a parameter to another program, similar to how we worked with a User-Defined Data Type.

To demonstrate this, let’s separate the printing section of the code from the main program and move it into its own subroutine.

  1. Copy the ClassArray() subroutine code.

  2. Paste it into the same Standard Module.

  3. Rename the new procedure to ClassArray2(), as shown below.

This new subroutine will handle the printing logic independently, while still working with the array of class objects that we pass to it.

Public Sub ClassArray2()
Dim tmpA As ClsArea
Dim CA() As ClsArea
Dim j As Long, title As String
Dim L As Long, U As Long

title = "ClassArray"
For j = 1 To 5 ‘the Loop is set for 5 items
  'instantiate temporary ClsArea Object
  'to enter input
  Set tmpA = New ClsArea

     tmpA.strDesc = InputBox(Str(j) & ") Description:", title, "")
     tmpA.dblLength = InputBox(Str(j) & ") Enter Length:", title, 0)
     tmpA.dblWidth = InputBox(Str(j) & ") Enter Width:", title, 0)
    
  'Redimension Array
  ReDim Preserve CA(1 To j) As ClsArea
  Set CA(j) = tmpA   'Copy Object to Array
  
 Set tmpA = Nothing 'Remove temporary object from memory.
Next

‘PRINTING SECTION
'Print the Array contents into the Debug Window
L = LBound(CA)
U = UBound(CA)

Debug.Print "Description", "Length", "Width", "Area"
For j = L To U
  With CA(j)
    Debug.Print .strDesc, .dblLength, .dblWidth, .Area
  End With
Next

’stop

'Clear array objects from memory
Erase CA

End Sub
 

The ClassArray2 Code Without Printing Section.

Create a new Sub-Routine as given below:

Public Sub ClassPrint(ByRef clsPrint() As ClsArea)
Dim L As Long, U As Long
Dim j As Long

End Sub
 

In the next step, we will separate the printing logic into its own subroutine. To do this, highlight the printing section of the ClassArray2() program, up to the Stop statement, cut it, and paste it below the Dim statements in a new subroutine named ClassPrint(). The revised versions of both programs are shown below:

Public Sub ClassArray2()

Dim tmpA As ClsArea
Dim CA() As ClsArea
Dim j As Long, title As String

title = "ClassArray"
For j = 1 To 5 ‘the Loop is set for 5 items
  'instantiate temporary ClsArea Object
  'to enter input
  Set tmpA = New ClsArea
     tmpA.strDesc = InputBox(Str(j) & ") Description:", title, "")
     tmpA.dblLength = InputBox(Str(j) & ") Enter Length:", title, 0)
     tmpA.dblWidth = InputBox(Str(j) & ") Enter Width:", title, 0)
    
  'Redimension Array
  ReDim Preserve CA(1 To j) As ClsArea
  Set CA(j) = tmpA   'Copy Object to Array
  
 Set tmpA = Nothing 'Remove temporary object from memory.
Next

Call ClassPrint(CA) ‘Pass the Object Array to print routine

'Clear array objects from memory
Erase CA

End Sub

The ClassPrint() Program.

Public Sub ClassPrint(ByRef clsPrint() As ClsArea)
Dim L As Long, U As Long
Dim j As Long

'Printing Section
'Print the Array Contents into the Debug Window.
L = LBound(clsPrint)
U = UBound(clsPrint)

Debug.Print "Description", "Length", "Width", "Area"
For j = L To U
  With clsPrint(j)
    Debug.Print .strDesc, .dblLength, .dblWidth, .Area
  End With
Next

End Sub

Review the code changes in both programs carefully and make corrections wherever necessary. Once ready, place the cursor in the middle of the ClassArray2() code and press F5 to run the program.

In this version, the Call statement in the first program passes the CA Class Object Array as a ByRef parameter to the ClassPrint() subroutine. The subroutine then prints the object properties and invokes the Area() function to calculate and display the results.

In the next session, we will extend our ClsArea class module by using it as a base class to build a new VBA Class Object that calculates the volume of a given item.

  1. MS-Access Class Module and VBA
  2. MS-Access VBA Class Object Arrays
  3. MS-Access Base Class and Derived Objects
  4. VBA Base Class and Derived Objects-2
  5. Base Class and Derived Object Variants
  6. Ms-Access Recordset and Class Module
  7. Access Class Module and Wrapper Classes
  8. Wrapper Class Functionality Transformation
  9. Ms-Access and Collection Object Basics
  10. Ms-Access Class Module and Collection Object
  11. Table Records in Collection Object and Form
  12. Dictionary Object Basics
  13. Dictionary Object Basics-2
  14. Sorting Dictionary Object Keys and Items
  15. Display Records from Dictionary to Form
  16. Add Class Objects as Dictionary Items
  17. Update Class Object Dictionary Item on Form

Share:

User Defined Data Type-2

Introduction.

This is the second post on User-Defined Data Type. The Link to the first post on this topic is here.

The topic of User-Defined Data Type came up when we used Two-Dimensional Arrays of Variant data types storing different value types (String, Integer, Double) in each element of the Array.  The Variant data type has the ability to change its data types automatically, as and when a particular type of value is received in the variable or its individual array element.  Instead of using a single Variant Variable with two dimensional Array, we can use four singly dimensioned Variables of different data types, as an alternative method.  Most of the time these methods are more than sufficient for simple data processing tasks.

But, learning something new is always interesting in programming.  The User-Defined Data Type is an interesting VBA feature.  We will explore and learn how to use it in our programs. 

How to Define a User-Defined Data Type

The steps go something like this:

  1. Define a new Data Type, with composite data types,  consisting of existing built-in variable types:  Integer, Long Integer, Double, String, etc.   The User-defined data type must be defined within the Type. . . End Type structure at the Module level.  The data type declaration must be at the beginning of the Standard Module.  The following sample declaration defines the data type myRecord and has two data elements: RecordID as Long Integer type and Description as String type.
    Public Type myRecord
    
    RecID as Long
    Description as String
    
    End Type
    

    The Scope of the Type declaration is Public by default.  Public/Private declaration is Optional.  You can declare it as Private, in that case, the availability of a Variable declaration is (like Dim AbcRec as myRecord) only within the Module, where the Type is declared.  The default scope (Public) enables the availability of the declared type within all Modules of this Project and to other Projects when this database is referenced in other Projects.  Let us start with a simple example:

    Type Sales
        Desc As String
        Quantity As Long
        UnitPrice As Double
        TotalPrice As Double
    End Type
    

    The dataType name is Sales.

  2. As you can see in the Sales data type we have used built-in data Types String, Long Integer, and Double for different data elements.

  3. Using the User-Defined variable in the program starts with Dimensioning a Variable of Type Sales, like any other variable.   
    Public Function typeTest()
    Dim mySales As Sales
    
       mySales.Desc = "iPhone 8 Plus"
       mySales.Quantity = 1
       mySales.UnitPrice = 75000#
       mySales.TotalPrice = mySales.Quantity * mySales.UnitPrice
    
    Debug.Print mySales.Desc, mySales.Quantity, mySales.UnitPrice, mySales.TotalPrice
    
    End Function
     

    Result printed in the Debug Window:

    iPhone 8 Plus  1             75000         75000 
    

    Unlike built-in Variables, addressing individual variable elements always starts with the <Type Name> followed by the <Element Name>, and both names are separated with a <dot>. The Desc, Quantity, UnitPrice, and TotalPrice elements are addressed as individual Properties of mySales.

  4. Let us make the above code a little bit flexible and clean, by placing the Variable elements within the With…End With structure.  The InputBox() function will allow us to enter data directly from the keyboard, into each element of the Sales Record.

    Public Function typeTest()
    Dim mySales As Sales
    
    With mySales
       .Desc = InputBox("Item Description: ")
       .Quantity = InputBox("Item Quantity: ")
       .UnitPrice = InputBox("Item Unit Price: ")
       .TotalPrice = .Quantity * .UnitPrice
    End With
    
    'Print the values on Debug Window
    With mySales
      Debug.Print .Desc, .Quantity, .UnitPrice, .TotalPrice
    End With
    End Function
    
    

    The modified code will get information on one Record and print them out on the Debug Window.  Before running the Code open Debug Window (Ctrl+G) to view the output.

    Arrays of User-Defined Type

  5. Next, we will define an Array of mySales Variable to enter information for five different items.  Pass the Array of User-Defined Variable with multiple elements of data to SalesPrint() Function as Parameter. The SalesPrint() function will calculate and update the TotalPrice element, before printing the Array values in the Debug Window. Keep the Debug Window open. The sample VBA Code of the programs is given below:
    Public Function SalesRecord()
    Dim mySales(5) As Sales
    Dim j As Integer, strLabel As String
    
    For j = 0 To UBound(mySales) - 1
        strLabel = "(" & j + 1 & ") "
        With mySales(j)
           .Desc = InputBox(strLabel & "Item Description:")
           .Quantity = InputBox(strLabel & "Quantity:")
           .UnitPrice = InputBox(strLabel & "UnitPrice:")
           .TotalPrice = 0
        End With
    Next
    
    Call SalesPrint(mySales())
    
    End Function
    
    

    Check the Dim statement, it is like any other array definition.  Dimension two more variables j and strLabel.  Variable j is used as a control variable in the For…Next loop.  strLabel is used to construct and store a label, like (1), (2), etc. to use in the InputBox() prompt.  This is an indicator to identify the current record number when we enter data into each record.

    We have used meaningful names for the Array Elements (Desc, Quantity, UnitPrice rather than using array index numbers like Sales(0,0) for Description or Sales(0,1) for Quantity, etc.). The MySales(j).TotalPrice is assigned 0.  This element's value will be calculated and assigned in the SalesPrint() function.  We will pass this Array as ByRef Parameter to the SalesPrint() Function.

    The SalesPrint() Function.

  6. The SalesPrint() function Code is given below:

    Public Function SalesPrint(ByRef PSales() As Sales) Dim j As Integer, strLabel As String Debug.Print "Description", " ", "Quantity", "UnitPrice", "Total Price" For j = 0 To UBound(PSales) - 1 strLabel = "(" & j + 1 & ") " With PSales(j)

    'calculate TotalPrice

    .TotalPrice = .Quantity * .UnitPrice 'print the values in debug window Debug.Print strLabel & .Desc, " ", .Quantity, .UnitPrice, .TotalPrice End With Next End Function

    The SalesPrint() function receives the Sales Record Array reference in the PSales variableHere also we have defined two local variables, j as Integer and strLabel as String. In the next line, we are printing a header line in the Debug Window in preparation for displaying Sales record details under proper headings.

    When a comma is used to separate each item they are printed on 14 column zones on the same line.  We have used an empty item with space as the second item on the print line to print the Quantity item on the 28th column so that the Item Description can have more than 14 characters long.

  7. Next, we are using a For . . . Next loop control structure is to access each record from memory, using the control variable j’s current cycle value of the loop as an array index number.  This will run from 0 to 4 (5 times).

  8. The First-line within the ForNext loop creates a label to give the sequence number, in the form of (1), (2), and so on to identify the records in the order in which they are entered into memory using the InputBox() function.

  9. The next statement puts the root level name of the User-Defined Type PSales within the With . . . End With Structure in order to address its Properties (.Desc, .Quantity, etc.) easily rather than repeating the upper-level Object name PSales as we did in the calling program.

  10. The next executable line calculates the Total Price value and assigns it to the TotalPrice element.

  11. Next line prints the current record to the Debug Window.  This process repeats within the For . . . Next loop and print all items in the Array. 

By this time I hope you understood the usefulness of this feature.  If you explore a little further with this method you can save these records from memory into an Access Table.  This type of Variable declaration is made for a particular task and the same data type may not be useful for general-purpose tasks as we do with built-in Variables.  Its data elements properties like Desc, Quantity, UnitPrice, etc. may not be useful for other purposes.

There are some interesting ways we can use the User-defined dataType and we will continue this discussion on this topic next week.

Share:

Passing Two Dimensional Array to Function

Introduction.

First of all, I have some good news for you, our website: LEARN MS-ACCESS TIPS AND TRICKS,  is selected by https://blog.feedspot.com panelist as one of the Top 5 Microsoft Access Blogs on the Web and awarded the Badge given below.

Microsoft Access Blogs

You can find some of the top-ranked Blogs on a variety of subjects, RSS Feeds, YouTube Channels,  top News Websites, and others.  Subscribe to Blogs/News Feeds, or topics of any other area of interest, and get regular updates from www.blog.feedspot.com to your Inbox, as and when it happens, by providing your E-mail address.

Coming back to our VBA lessons, last week we have briefly touched on the topic of passing Arrays as Parameter to Function ByRef method.  We were able to work with the singly dimensioned Array in its original location within the called function, to sort the values in descending order.  For this example, we have loaded the array with values for only five elements, but the array can have many rows and columns of values.

The Re-Dimension (ReDim) Statement.

An Array can re-dimension, for more rows or fewer rows later on in the program more than once, if we cannot determine the length of the array in advance. In that case, you should not specify the number of elements in advance in the initial Dimension Statement.

Example:

'Cannot Re-dimension pre-defined Arrays
.
Dim Products(1 to 5) as String
.
or
.
Dim Products(5) as String'The number of elements are predefined

'Re-dimension this Array later for required  
'Number of elements, not known in advance. 
.
Dim Products() as String
.
'Re-Dimension the Array for required number of elements
'Remember the array index numbers will be 0 to 4, total 5 elements
ReDim Products(5) As String
'
'OR
'In this case Array Index Number Range 1 to 5
ReDim Product(1 to 5) As String

'later on in the program
'all the values assigned to first 5 elements will be lost.
.
ReDim Products(7) As String 
.
or
.
ReDim Products(Ubound(Products)+2) As String
.
'To preserve the values already assigned to first 5 elements
.
ReDim Preserve Products(7) As String

Note:

The important point to note here is that the ReDimension should take place in the calling program itself, if the need arises in the called function, before passing the Array to the called program.  Even though the Array is passed to the called function ByRef and we are able to work with the passed variable directly, we cannot re-dimension the array to increase/decrease the number of elements in the array, from within the called function.

Two-Dimension Array as Function Argument.

Now, that we are aware of the limitation of the array when passed to the called function, we will try to pass a two-dimensional array of Sales data to a function and print the values in the Debug Window.  The Sales data array has the following values in a record:

  1. Product Name - Text
  2. Quantity  - Integer
  3. Unit Price – Double
  4. Total Value  - Double (will be calculated in the called function)

The sales record shows that the data fields have different data types.  To pass values for each field separately to the called function, we need four different array Variables with different data types (1. The String data type for Product-Name, 2. Integer for Quantity, 3. Unit Price & 4. Total Price with Double precision number) and load each field data into separate Array Variables.

We are going to do it differently here.  We will be using only one Variable to pass all four field values to the called function.  We will define a single Variable as a two-dimensional Array of Variant data types, with four rows (each row represents a single record) and four columns (each column is a field).

The Variant Data Type.

We are not storing the above column names anywhere in the array and it is assumed that the first column is Product name, the next column to the right of the first one is Quantity, the next column is Unit Price and the last column is Total Price.  Since the Variant Data Type variable has a peculiar behavior, the data type of the cell changes automatically to match the data type assigned to it.

Before writing it as a complete function, we will look at the dimension statement and how the sales values are assigned to each element of the array. 

' variant Variable can hold different data types in each element 
Dim Product(4,4) as Variant 
'
Product(0, 0) = "Hard Disk": Product(0, 1) = 5: Product(0, 2) = 125.5: Product(0, 3) = 0

Product(1, 0) = "Keyboard": Product(1, 1) = 2: Product(1, 2) = 25.25: Product(1, 3) = 0

Product(2, 0) = "Mouse": Product(2, 1) = 3: Product(2, 2) = 13.75: Product(2, 3) = 0

Product(3, 0) = "DVD Writer": Product(3, 1) = 10: Product(3, 2) = 30: Product(3, 3) = 0

In the above example, we have only four records (or four rows or lines of data) in the Table.  There are four fields (four columns) in each record.  Each cell is numbered with two numbers (row index number, column index number), separated by a comma.  The left-side number is the row index number of the column and the number to the right side of the comma is the column index number.  Both number range is 0 to 3 (4 rows and 4 columns).  The first column (column index 0) is Product Name, 2nd Column  (column index 1) Quantity, 3rd Column (index number 2) Unit Price and the last one is (index number 3) Total Value, which will be calculated and assigned later.

The entire array of these values can be passed to a function as the ByRef parameter and we can work with the array directly from within the called function.  If you are new to two-dimensional arrays, it will be a little confusing at first to comprehend the arrangement of values and how to address each cell to work with it.   This becomes more difficult when there are calculations involving cells of the same row. 

We have a better way to deal with this problem with User-Defined Variables.  Yes, you heard me correctly, we can define our own Variable Type,  besides the built-in variables with default data types.  We will explore this topic further next week and I am sure you will be happier with this new idea, after struggling with these rows and columns set up.  Believe me, this is a very powerful feature once you are familiar with these kinds of data arrangements.  You can work with 5 rows, 500 rows, or 5000 rows with the same statements in the function.

Create the Product List Data.

Public Function ProdList()
Dim Products(4, 4) As Variant
Dim j As Integer, k As Integer, stridx As String
' 0 = Description
' 1 = Quantity
' 2 = Unit Price
' 3 = Total Price to be calculated
'Array elements index numbers are 0 to 3
For j = 0 To 3
 For k = 0 To 3
    stridx = "(" & j & "," & k & ")"
    Select Case k
        Case 0
          Products(j, k) = InputBox("Product Name" & stridx)
        Case 1
          Products(j, k) = InputBox("Quantity" & stridx)
        Case 2
          Products(j, k) = InputBox("Unit Price" & stridx)
        Case 3
          Products(j, k) = 0 'total value will be calculated
    End Select
    Next k
Next j

Call ProdPrint(Products)

End Function

VBA Code Line by Line

We have defined the Products variable as a Variant data type with 4 rows, and 4 columns for assigning values of different data types in them.   The next line sets up three more variables: j & k as a control variable for For … Next loops, the variable stridx for building a string to display the index numbers of cells when displayed in the InputBox() function Prompt text.

Two nested For … Next loops are set up to control the Variable index numbers of rows and column values.  The outer loop controls the row number and the inner loop with the k control variable is used for the column index number.

Next, we used the Select Case ... End Select statements to run several other statements depending on the value in the control variable j. If the value in variable k=0 (and j=0) then the Inputbox() function runs below the Case 0 tests and gets the Product Name and assigns it to the Products(0,0) cell.  When k=1 then the InputBox() gets the value of the Quantity and assigns it to the Products(0,1) cell. When k=2 gets Unit Price and in the next step assigns Products(0,3)=0. The outer loop with the control variable runs only once with zero value as the row index number.

This action repeats 3 more times for the outer For…Next loop to control the row index number and each time the inner For … Next loop runs four times to control the column numbers to get values from the User for each cell for the row number in the j control variable.

The Output Function ProdPrint().

When control comes out of the loop the ProductPrint() Function is called by passing the Products variable as a parameter to the function.

Public Function ProdPrint(List As Variant)
Dim j As Integer, k As Integer

'Ubound() function will get the
'total rows in the array - first value in the Dim statement
For j = 0 To UBound(List, 1) - 1
      List(j, 3) = List(j, 1) * List(j, 2)
    For k = 0 To UBound(List, 2) - 1 'get second value in Dim statement
        Debug.Print List(j, k),
    Next k: Debug.Print
Next j

End Function

The ProductPrint() function takes the Products Array's location address (ByRef) method. If you omit the ByVal or ByRef keyword before the Parameter variable it assumes that Variable List holds the location reference of the Products (parameter passed ByRef).

As in the earlier program, two integer variables j & k  are defined as control variables for outer and inner For … Next loops.  We need these For … Next loops to control the index numbers (rows & columns) to access each element of the array.  The starting value of the Loop is 0 but to calculate the end value we have used another function Ubound() (get Upper Boundary) value of the Array dimension.  In the first program, we have written the control value as 0 to 3.  Here also we could do that, but here we have used the Ubound() function to find the row and column numbers. This will calculate the Array size correctly if the Array size is changed through ReDim statements.

Usage UBound() Function to get Two-Dimension Index Numbers.

UBound(Array,1)

The Ubound(List, 1) gets the number of rows to value, which is 4. But the row index numbers start from 0 in memory so we have used index numbers 0 to 3 in the For … Next loop. The second value 1 in the bracket of the Ubound() function asks for the number of rows in the array.  Since the row index number starts from 0 we are subtracting 1 from the number of row values (4-1).

UBound(Array,2)

The UBound(List, 2) gets the number of column values.  The second parameter value is optional, if it is omitted, it will only get the row value.  If the variable is a singly dimensioned array, then the second value is never used.

The statement immediately after the first For … Next loop ‘List(j, 3) = List(j, 1) * List(j, 2)’ calculates the Total Price of each item and assigns it to the rightmost cell before printing the values of the Sales record item within the next For…Next loop, on the debug window.

Controlling the Print-Head

The comma at the end of the Debug.Print statement positions the next item in the 14th column on the same line, after printing the earlier item.

The empty Debug.Print statement, immediately after the inner Next statement without a comma at the end brings the print control back to the first column of the next line, positions correctly to start printing the next Sales Record.

If we place a semi-colon (;) at the end of the Debug.Print statement the print-head positions to the next character position, without leaving any space between the items printed.

Next week we will explore the User-Defined Variable with the mixed data type. We can give appropriate names for each element of the array rather than assuming names as we did in the above examples. I am sure it will be very interesting to compare the difficulty we had in memorizing each array element's logical name according to its array position.


Share:

Percentage on Report Summary

Introduction.

We have worked with a Query to solve this problem earlier in the Blog Post: Percentage on Total Query.  This time let us see how it is done on a Report.  Our task is to show a detail-line-wise value’s percentage on Report Summary Total.

The solution is simple.  Create a Report with some values in it with a Report level summary.  Add a Text Box in the detail section and write an expression to divide the Report Summary Value into the detail level value.

Design a Sample Report to Try.

  1. Import the Order Detail Table from Microsoft Access Sample Database: C:\Program Files\Microsoft Office\Office11\Sample\Northwind.mdb

  2. Open a new Query in SQL View; without selecting a Table from the displayed list.

  3. Copy and Paste the following SQL String into the SQL editing window of the new Query:

    SELECT [Order Details].[Order ID], Sum([Order Details].Quantity) AS TQuantity, Sum([Order Details].[Unit Price]) AS UnitPrice, Sum([Unit Price]*[Quantity]) AS TotalPrice
    FROM [Order Details]
    GROUP BY [Order Details].[Order ID];
    
  4. Save the Query with the name OrderSummary.

  5. Design a Report (as shown in the image given below) with the Detail Section and Report Footer summary controls using the OrderSummary as Source.

  6. Click on the Text Box at the Footer of the Report to select it.

  7. Display the Property Sheet (F4 or ALT+Enter) of the Text Box.

  8. Change the Name Property Value to GTPrice (stands for Grand Total Price).

  9. Write the expression =Sum([TotalPrice]) in the Control Source Property.

  10. Select the Text Box at the right end of the Detail Section and display its Property Sheet.

  11. Write the expression =[TotalPrice]/[GTPrice] in the Control Source Property.

  12. In the Format Property selects the Percent format from the drop-down list.

  13. Type 2 in the Decimal Places Property.

  14. Save and Close the Report.

  15. Open the OrderSummary in Print Preview and check the detail line percentage value calculated on Report Footer Grand Total Price.

Creating Page-Level Totals.

Want to know how to calculate and display Page-wise control totals; you can learn it from here.

Share:

PRESENTATION: ACCESS USER GROUPS (EUROPE)

Translate

PageRank

Post Feed


Search

Popular Posts

Blog Archive

Powered by Blogger.

Labels

Forms Functions How Tos MS-Access Security Reports msaccess forms Animations msaccess animation Utilities msaccess controls Access and Internet MS-Access Scurity MS-Access and Internet Class Module External Links Queries Array msaccess reports Accesstips WithEvents msaccess tips Downloads Objects Menus and Toolbars Collection Object MsaccessLinks Process Controls Art Work Property msaccess How Tos Combo Boxes Dictionary Object ListView Control Query VBA msaccessQuery Calculation Event Graph Charts ImageList Control List Boxes TreeView Control Command Buttons Controls Data Emails and Alerts Form Custom Functions Custom Wizards DOS Commands Data Type Key Object Reference ms-access functions msaccess functions msaccess graphs msaccess reporttricks Command Button Report msaccess menus msaccessprocess security advanced Access Security Add Auto-Number Field Type Form Instances ImageList Item Macros Menus Nodes RaiseEvent Recordset Top Values Variables Wrapper Classes msaccess email progressmeter Access2007 Copy Excel Export Expression Fields Join Methods Microsoft Numbering System Records Security Split SubForm Table Tables Time Difference Utility WScript Workgroup database function msaccess wizards tutorial Access Emails and Alerts Access Fields Access How Tos Access Mail Merge Access2003 Accounting Year Action Animation Attachment Binary Numbers Bookmarks Budgeting ChDir Color Palette Common Controls Conditional Formatting Data Filtering Database Records Defining Pages Desktop Shortcuts Diagram Disk Dynamic Lookup Error Handler External Filter Formatting Groups Hexadecimal Numbers Import Labels List Logo Macro Mail Merge Main Form Memo Message Box Monitoring Octal Numbers Operating System Paste Primary-Key Product Rank Reading Remove Rich Text Sequence SetFocus Summary Tab-Page Union Query User Users Water-Mark Word automatically commands hyperlinks iSeries Date iif ms-access msaccess msaccess alerts pdf files reference restore switch text toolbar updating upload vba code