Suggestion |
Class and Scope
|
1 Class in Excel 2 Class: example 3 Scope: 1 file 3.1 CallByName 3.2 Object variable 3.2.1 Local scope 3.2.2 Private scope 3.2.3 Public scope 4 Application Scope:Personal.xlsb 4.1 Application.Run 4.2 CallByName 4.3 Workbookfunction 4.4 Reference to Personal.xlsb 4.4.1 Template with reference to Personal.xlsb 5 Application Scope:AddIn 5.1 Create the AddIn 5.2 Load AddIn 5.2.1 In VBA 5.2.2 With dialog window 5.2.3 Manually 5.3 Access to the Classmodule 5.3.1 Application.Run 5.3.2 CallByName 5.3.3 Workbookfunction 5.3.4 Reference to the AddIn 5.3.4.1 Direct Access 5.3.4.2 CallByName 5.3.4.3 Template with reference 6 Users in a network 7 Collaboration |
In VBA class modules can standardize and simplify VBA code. Elements that occur frequently can be defined as a class. A class is always part of the VBA project of a file. Next, you need to make sure that the class is accessible to other code. Therefore it is important to determine what the scope of the class should be. The range can be: - 1 single procedure (Macro or Function): procedure level - several procedures in the same macromodule (of a workbook, a worksheet or a Userform): moduel level - all procedures in a workbook/file: file level - all open files in Excel: application level - several users working on the same server: network level To illustrate this I created a classmodule called 'c_seqnr'. . The code in this classmodule generates a unique consecutive number per year. That class has 1 property: 'Value' The procedure Property Get Value is the only code in the classmodule. The procedure Property Get Value assigns a value to the class. The value of the class is a year, followed by a serial number. The sequence number to generate is based on the name of a file ending with '_nr'. For simplicity sake that file is in the same directory that contains the file with the class. E.g the file's name is G:\OF\202000013_nr and the class generates the consecutive numbers 202000013, 202000014, 202000015, etc. The code in the Classmodule Public Property Get Value()
c00 = ThisWorkbook.path & "\"
End Propertyc01 = Dir(c00 & Year(Date) & "*_nr") If c01 = "" Then Open c00 & Year(Date) & "00000_nr" For Output As 1
Else
Close Value = Val(c01) + 1
End IfName c00 & c01 As c00 & Value & "_nr" - c00 is the directory of the serialnumber file - c01 is the name of the file - Value is the number in the file's name plus 1 - checks whether the directory contains a file ending with '_nr' and starting with the current year. - if such a file does not exist, the code creates it and saves it as: e.g. 202000000_nr, 202100000_nr, etc. - reads the name of the file - adds 1 to the number part in the name - assigns the new number to the classmodule's property 'Value' - renames the file with a new serial number: 202000001_nr, 202000002_nr, etc. In order to use the classmodule you have to create a new 'instance' of the classmodule with the instruction. New c_seqnr Several methods exist to read the resulting value VBA contains the function CallByName with which you can access/read a property of an object directly. In this case, the object to be read (the class) is called 'c_seqnr', with the property 'value'. To create a new instance of the class and to read its value we use: MsgBox CallByName(New c_seqnr, "Value",vbGet) MsgBox CallByName(New c_seqnr, "Value",2) How you should/can do this depends on the scope that you intend. With Local Scope you can only use the variable within 1 macro or function. The instruction 'Set ... = New ' assigns a new instance of the classmodule to a local object variable. Use the object variable to read the value of the classmodule. Sub M_snb()
Set seqnr = New c_seqnr
End SubMsgBox seqnr.Value The object variable for the new class instance must be declared on the macromodule level. To do this, in the 'declaration' part of the macromodule (the space in the macromodule before the macro/function procedures) you put the code with which you assign the classmodule to an object variable. The scope of a macro module is called 'Private Scope' . You can do this in the macromodule of - the Excel file ('ThisWorkbook') and/or - a worksheet ('Sheet1', 'Sheet2', etc.) and/or - a Userform ('Userform1', 'Userform2', etc.) - a general macromodule ('Module1', 'Module2', etc.) There are two ways to assign the object variable: - with the instruction 'Dim ... As New ' or - with the instruction 'Private ... As New' Both methods are equivalent. You can subsequently use the object variabele in a macro or in a function. Dim seqnr As New c_seqnr Sub M_snb() MsgBox seqnr.Value
End Submsgbox F_snb Function F_snb() F_snb = seqnr.value
End FunctionPrivate seqnr As New c_seqnr Sub M_snb() MsgBox seqnr.Value
End SubMsgbox F_snb Function F_snb() F_snb = seqnr.value
End FunctionThis can only be done in the declaration section of an ordinary macromodule. This means that at least 1 ordinary macro module is required in the VBP project. We assign a new instance of the class to that object variable. Declare the Public object variable in the general macromodule Public seqnr as New c_seqnr Sub M_snb()
MsgBox seqnr.value
End SubFunction F_snb()
F_snb = seqnr.Value
End Function4 Application Scope: Personal.xlsb In order to apply the class in every open Excel file you can use the Personal.xlsb file or an Addin.Create the classmodule 'c_seqnr' in the Personal.xlsb file. You cannot get direct access to the classmodule in the Personal.xlsb file from another Excel file. There are 4 methods though to access the classmodule in Personal.xlsb Create a normal macromodule in Personal.xlsb. Create a macro or a function that refers to the classmodule 'c_seqnr' in Personal.xlsb. We name this macromodule 'M_seqnr' It encompasses this code: Function F_seqnr()
With New c_seqnr
End FunctionF_seqnr = .Value
End WithNow we can call this function from every loaded Excel file using: Sub M_snb()
MsgBox Application.Run("personal.xlsb!M_seqnr.F_seqnr")
End SubSub M_snb()
MsgBox Application.Run("personal.xlsb!F_seqnr")
End Sub(I owe this suggestion to DerHoepp in http://www.office-loesung.de) Function seqnr()
Set seqnr = New c_seqnr
End FunctionWe can call this function in every loaded Excel file using: Sub M_snb()
MsgBox CallByName(workbooks(1).seqnr,"Value",2)
End SubSub M_snb()
MsgBox CallByName(workbooks("personal.xlsb").seqnr,"Value",vbGet)
End Sub(I owe this suggestion to DerHoepp in http://www.office-loesung.de) Function seqnr()
Set seqnr = New c_seqnr
End FunctionWe can call this function in every loaded Excel file using: Sub M_snb()
MsgBox Workbooks("personal.xlsb").seqnr.Value
End Sub4.4 Reference to Personal.xlsb In the VBEditor you can specify which VBA libraries that file uses.In VBA these are called 'references'. The references are stored in each file as file properties. An Excel file can be regarded as a VBA library. This also applies to the personal.xlsb file. . You only need to add a reference once since the reference remains as a property of the file: even after closing. References must have unique names. Excel uses the VBProject name of a file as the reference name. In an Excel file, the VBProject has the name 'VBProject' by default. This also applies to the Personal.xlsb file. . As a result, name conflicts quickly arise. So you'd better start changing the name of the VBProject in Personal.xlsb and/or of the file with the reference. Steps 1. change the name of the VBProject in Personal.xlsb Workbooks(1).VBProject.Name = "VB_000" ThisWorkbook.VBProject.Name = "VB_001" ThisWorkbook.VBProject.References.AddFromFile Workbooks(1).FullName 5. Create a normal macromodule in Personal.xlsb: e.g. 'M_seqnr' 6. Declare in 'M_seqnr' a public Object variable, containing a new instance of the classmodule 'c_seqnr'. Public v_seqnr as New c_seqnr Sub M_snb_pv()
MsgBox v_seqnr.Value
End SubMsgBox VB_000.v_seqnr.Value Sub M_snb_pf()
MsgBox F_seqnr
End SubMsgBox VB_000.F_seqnr Sub M_snb_wbf()
MsgBox seqnr.Value
End SubMsgBox VB_000.ThisWorkbook.seqnr.Value 4.4.1 Template with the reference To avoid having to create a reference to the Personal.xlsb file in a new Excel file, you can save an Ecel file with the reference and use it as a template.If you save the file as an Excel template .xltm, Excel automatically creates a copy. If you save the file as an .xlsm or .xlsb file, you will get a copy using the VBA method Workbooks.Add. The reference to the Personal.xlsb file is 'inherited' to the new file. An Addin can serve as alternative to the Personal.xlsb. A loaded Addin is invisible. The Application (Excel) stores installed Addins aa a property of the application. The next time Excel is loaded the Addin is loaded too. Create the Classmodule 'c_seqnr' in the file: Public Property Get Value()
c00 = ThisWorkbook.path & "\"
End Propertyc01 = Dir(c00 & Year(Date) & "*_nr") If c01 = "" Then Open c00 & Year(Date) & "00000_nr" For Output As 1
Else
Close Value = Val(c01) + 1
End IfName c00 & c01 As c00 & Value & "_nr" example addin Sub M_snb()
c00 = "G:\OF\"
End SubAddIns.Add(c00 & "seqnr.xlam", False).Installed = True Sub M_snb()
Application.Dialogs(321).Show
End SubSimilar to the Personal.xlsb file you cant' access the classmodule directly. 4 Methods provide the means. Create a normal macromodule in seqnr.xlam. We name this macromodule 'M_seqnr' Create a macro or a function that refers to the classmodule 'c_seqnr' in seqnr.xlam. It encompasses this code: Function F_seqnr()
With New c_seqnr
End FunctionF_snb = .Value
End WithSub M_snb()
MsgBox Application.Run("seqnr.xlam!M_seqnr.F_seqnr")
End Sub(I owe this suggestion to DerHoepp in http://www.office-loesung.de) Function seqnr()
Set seqnr = New c_seqnr
End FunctionWe can call this function in every loaded Excel file using: MsgBox CallByName(Workbooks("seqnr.xlam").seqnr, "Value", 2) (I owe this suggestion to DerHoepp in http://www.office-loesung.de) Function seqnr()
Set seqnr = New seqnr
End FunctionWe can call this function in every loaded Excel file using: Sub M_snb()
MsgBox Workbooks("seqnr.xlam").seqnr.Value
End SubSub M_snb()
MsgBox Workbooks(AddIns("seqnr").Name).seqnr.Value
End SubSub M_snb()
MsgBox AddIns("seqnr").seqnr.Value
End SubIn VBA these are called 'references'. The references are stored in each file as file properties. An Excel file can be regarded as a VBA library. This also applies to the Addin 'seqnr.xlam' file. . You only need to add a reference once since the reference remains a property of the file: even after closing. References must have unique names. Excel uses the VBProject name of a file as the reference name. In an Excel file, the VBProject has the name 'VBProject' by default. This also applies to the seqnr.xlam file. . As a result, name conflicts quickly arise. So you'd better start changing the name of the VBProject in seqnr.xlam and/or of the file with the reference. Steps 1. change the name of the VBProject in seqnr.xlam Workbooks("seqnr.xlam").VBProject.Name = "VB_000" ThisWorkbook.VBProject.Name = "VB_001" ThisWorkbook.VBProject.References.AddFromFile Workbooks("seqnr.xlam").FullName 5. Create a normal macromodule in Addin seqnr.xlam: e.g. 'M_seqnr' 6. Declare in 'M_seqnr' a public Object variable, containing a new instance of the classmodule 'c_seqnr'. Public v_seqnr as New c_seqnr You can use this reference name 'VB_000' instead of Workbooks("seqnr.xlam"). To get accees to the classmodule we use the Public variable 'v_seqnr' or the workbookfunction 'seqnr'. Read the value of variable 'v_seqnr' Sub M_snb() MsgBox VB_000.v_seqnr.value
End SubSub M_snb()
MsgBox VB_000.ThisWorkbook.seqnr.value
End SubSub M_snb()
MsgBox VB_000.F_seqnr
End SubSub M_snb()
MsgBox CallByName(VB_000.v_seqnr, "value", 2) MsgBox CallByName(VB_000.v_seqnr, "value", vbGet) End Sub Sub M_snb()
MsgBox CallByName(Workbooks("seqnr.xlam").v_seqnr, "value", 2)
End SubMsgBox CallByName(Workbooks("seqnr.xlam").v_seqnr, "value", vbGet) Sub M_snb()
MsgBox CallByName(VB_000.ThisWorkbook.seqnr, "value", 2)
End SubMsgBox CallByName(VB_000.ThisWorkbook.seqnr, "value", vbGet) Sub M_snb()
MsgBox CallByName(Workbooks("seqnr.xlam"). ThisWorkbook.seqnr, "value", 2)
End SubMsgBox CallByName(Workbooks(AddIns("seqnr").Name).ThisWorkbook.seqnr, "value", 2) 5.3.4.3 Template with reference To avoid having to create a reference to the AddIn seqnr.xlam in every new Excel file, you can save an Excel file with the reference and use it as a template.Save the Excel file and its reference as .xlsb or .xlsm file. Open a copy of this file: Workbooks.Add "G:\OF\snb_ref.xlsb" Save the Excel file and its referenca as a template (.xltm) Open a new file based on this template Workbooks.open "G:\OF\snb_ref.xltm" To allow different users in a network to use the same classmodule, use an AddIn. . Save the file as AddIn (.xlam), property IsAddin = True in a location accessible to all intended users. Set the property of the AddIn to 'read-only', so that each user uses a copy of the AddIn. Every user can now add the AddIn to Excel. In this way, the Addin only needs to be maintained in one place. Above we have discussed the different ways in which you can give 1 or more users access to a classmodule. This is independent of the content of the classmodule. This example creates a unique serial number per year. An issue that is often raised in forums about Excel and/or VBA. The proposed method in the class in this example is to read the name of a file and change the name of a file without opening it. As a result, many users can use this method at the same time. The choice of where to store the serial number file therefore depends primarily on which users the file should be accessible/available to. The file must be stored in a directory available to all intended users. Every user must have the rights to read and change the name of the file. Changing the 'extension' of the serial number file can result in an unlimited number of different individual types of serial numbers (e.g. for customers, invoices, credit notes, staff members, members, participants, etc.) . The core is always which directory contains the serial number file. In the example code it is sn(0). sn = Array(ThisWorkbook.path & "\", Year(Date) & "_") You can change this at will, provided that the access rights of the intended users are guaranteed. |