简体   繁体   English

命名范围的类别-Excel VBA

[英]Class of Named Ranges - Excel VBA

I am in the process of continuously updating and improving a financial model built in Excel with VBA macros enabled that is in active use by multiple people. 我正在不断更新和改进在Excel中启用的VBA宏构建的财务模型,该模型已被多个人积极使用。 Primarily, these templates are used as budgets for different projects, so there are many created all the time while older budgets are re-visited. 最初,这些模板用作不同项目的预算,因此在重新访问较旧的预算时,总是会创建许多模板。

I am the "keeper" of the template while the other users simply use the document. 我是模板的“管理员”,而其他用户只是使用该文档。 Whenever I need to push out an update to everyone, it creates an issue because they already have created the budget in an older version of the template and to re-create the budget in the new template would take an inordinate amount of time. 每当我需要向所有人推送更新时,都会造成问题,因为他们已经在模板的较旧版本中创建了预算,而在新模板中重新创建预算将花费大量时间。

I have gotten around this problem on smaller-scale templates by naming ranges and then applying those named ranges to an old version and then using the named ranges to copy into the same named range in the new version of the template. 我已经在较小规模的模板上解决了此问题,方法是命名范围,然后将这些命名范围应用于旧版本,然后使用命名范围复制到模板的新版本中的相同命名范围中。 However, this was done with individual lines of code to copy each named range. 但是,这是通过单独的代码行复制每个命名范围来完成的。

Is there a way to aggregate a group of named ranges into a class so that Excel can just loop through all of the items in the class and copy the data rather than me needing to code out each line to perform a copy? 有没有一种方法可以将一组命名范围聚合到一个类中,以便Excel可以循环遍历该类中的所有项目并复制数据,而不需要我编写每行代码以执行复制?

Here is a sample of the code that I am currently running: 这是我当前正在运行的代码示例:

Workbooks(WB_Active_Name).Sheets("Office Staff Input").Range("Office_Employee_Names").Value = Workbooks(WB_Secondary_Name).Sheets("Office Staff Input").Range("Office_Employee_Names").Value
    Workbooks(WB_Active_Name).Sheets("Office Staff Input").Range("Office_Employee_Positions").Value = Workbooks(WB_Secondary_Name).Sheets("Office Staff Input").Range("Office_Employee_Positions").Value
    Workbooks(WB_Active_Name).Sheets("Office Staff Input").Range("Office_Employee_Numbers").Value = Workbooks(WB_Secondary_Name).Sheets("Office Staff Input").Range("Office_Employee_Numbers").Value
    Workbooks(WB_Active_Name).Sheets("Office Staff Input").Range("Office_Employee_Bonus_Sharing").Value = Workbooks(WB_Secondary_Name).Sheets("Office Staff Input").Range("Office_Employee_Bonus_Sharing").Value

There are dozen of more lines of code similar to this for each named range. 每个命名范围都有十几行与此类似的代码行。 Inside the template, the ranges refer to lists of names of staff, ID numbers, hours worked, etc. and they also span multiple sheets within the workbook with each range of different size. 在模板内部,范围是指职员姓名,ID号,工作时间等的列表,它们还跨越工作簿中的多个工作表,每个范围的大小各不相同。

I am wondering if there is some sort of class that I could place in front of each named range when I define it so that they are treated as a class together and can be looped through. 我想知道在定义它时是否可以在每个命名范围的前面放置某种类,以便将它们一起视为一个类并可以循环通过。 For example: 例如:

Office_Employee_Names

becomes 变成

GroupClass.Office_Employee_Names

Then the code could loop through everything in GroupClass 然后代码可以遍历GroupClass所有GroupClass

IF an MVC pattern would exist in VBA, then a named range could represent a View object. 如果VBA中将存在MVC模式,则命名范围可以表示View对象。
I personally have some experience with an MV* pattern where Views also implement events (which are normally delegated to Controller objects). 我个人对MV *模式有一些经验,在该模式中,视图还实现了事件(通常将事件委托给Controller对象)。

The benefit of using this approach, is that you will start programming in a much more modular fashion. 使用这种方法的好处是,您将以更加模块化的方式开始编程。

I provide an example below: 我在下面提供一个示例:

The structure of an MV* implementation of a simple named range "persons" could have the following class structure: 简单命名范围“人”的MV *实现的结构可以具有以下类结构:

  • cls_view_persons cls_view_persons
  • cls_model_person cls_model_person

Imagine that cls_view_persons represents a view object, then this would mean that you have to instantiate it from a base sub, which will simply be: 假设cls_view_persons代表一个视图对象,那么这意味着您必须从基础子实例化它,该子类将简单地是:

dim view_persons as cls_view_persons 
set cls_view_persons = new cls_view_persons 

1. persons view (example) 1.人员视图(示例)

The cls_view_persons class will have a property that defines the range of the class. cls_view_persons类将具有定义该类范围的属性。 For example: 例如:

private pRange as new Range 

You can define the private pRange property in the class constructor. 您可以在类构造函数中定义private pRange属性。

One of the things that makes Excel buggy, is the fact that you don't know in advance the size of the range, and the fact that mistakes happen, such as a range that was not properly cleared the last time. 导致Excel出现错误的原因之一是您不预先知道范围的大小,并且发生了错误(例如上次未正确清除的范围)这一事实。
This is why it is important to also define the following properties: 这就是为什么还要定义以下属性很重要的原因:

  • _oRange as Excel.Range _oRange作为Excel.Range
  • _sNamed_range as string; _sNamed_range作为字符串;
  • _lNr_rows as long; _lNr_rows;
  • _iNr_cols as integer; _iNr_cols为整数;
  • _iOffset_x as long; _iOffset_x长;
  • _lOffset_y as integer; _lOffset_y为整数;
  • oCollection as Collection (you can also use a dictionary for this) oCollection作为Collection(您也可以为此使用字典)

Note: oCollection is an object that will consist of the different cls_view_persons instances; 注意:oCollection是一个将由不同的cls_view_persons实例组成的对象。

and methods: 和方法:

  • Init: class constructor: defining a default range object, or you could pass a range on creation time if you wish to do so; 初始化:类构造函数:定义一个默认的范围对象,或者如果您愿意,可以在创建时传递一个范围;
  • get_named_range: returning a range object; get_named_range:返回范围对象;
  • set_named_range: setting the range object property and creating the named range in the sheet object; set_named_range:设置范围对象属性并在图纸对象中创建命名范围;
  • collection_to_array: converts the collection object into an array; collection_to_array:将集合对象转换为数组;
  • clear_range: clearing the range; clear_range:清除范围;
  • A render method that uses the previous three methods and writes data from the created array to the range in a single statement: 一个render方法,该方法使用前三种方法,并在单个语句中将创建的数组中的数据写入范围:

    set pRange = vPersons

Where vPersons is an array, containing the different persons (see later). vPersons是一个数组,其中包含不同的人(请参阅下文)。

  • (A "read" method that reads from the range would be useful as well). (从该范围读取的“读取”方法也将很有用)。

    2 person model (example) 2人模型(示例)

A model represents the data logic of your application and is on itself, not necessarily related to one specific view. 模型代表您的应用程序的数据逻辑,并且自身存在,不一定与一个特定的视图相关。 In this case it is, but a model (or Collection of models) is in principle independent). 在这种情况下,是这样,但是模型(或模型集合)原则上是独立的。
A person model could be a class that defines the following properties: 人员模型可以是定义以下属性的类:

  • firstname 名字
  • lastname
  • address 地址
  • country 国家

Either these models are fetched from a database, from an Excel sheet (the latter being the worst scenario, which unfortunately happens the most) or any other source. 这些模型可以从数据库,Excel工作表(后者是最坏的情况,不幸的是发生最多的情况)或任何其他来源中获取。

Whatever you do, you need to see that you end up with a Collection object that you can feed to the View object. 无论您做什么,都需要确保最终得到一个Collection对象,您可以将其提供给View对象。
Once this is done, the View object should manage its own. 完成此操作后,View对象应自行管理。 All interpreting and rendering is delegated to this object from that moment on. 从那时起,所有解释和渲染都委托给该对象。
This means: 这意味着:

  • Verify the dimensions; 验证尺寸;
  • Clear the previous results; 清除先前的结果;
  • Render the range on the screen (ie. setting the new dimensions, creating the named range in the sheet, converting the collection into an array, and writing the array to the screen). 在屏幕上渲染范围(即,设置新尺寸,在工作表中创建命名范围,将集合转换为数组并将该数组写入屏幕)。

You will see that this approach has many benefits in terms of: 您将看到此方法在以下方面有很多好处:

  • Maintenance; 保养;
  • Less bugs; 错误更少;
  • Modular (and transferrable) / encapsulated properties / methods; 模块化(可转让)/封装的属性/方法;
  • Dynamically adaptable; 动态适应
  • You can read from any data source, if you only write an intermediary "translator" module. 如果您仅编写中间“翻译器”模块,则可以从任何数据源读取。

I would create a new worksheet in your template that list all these named ranges; 我将在您的模板中创建一个新的工作表,其中列出了所有这些命名范围; read the list from VBA and loop through them. 从VBA中读取列表,然后遍历它们。

I often retrieve data from Names by: 我经常通过以下方式从“名称”中检索数据:

Workbook.Names contains all Name Objects, and each Name Object its properties Workbook.Names包含所有名称对象,每个名称对象都有其属性

Dim WBook As Workbook
Dim WName As Name

Set WBook = ActiveWorkbook
For Each WName In WBook.Names
    Debug.Print WName.Name, WName.RefersToLocal
Next WName

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM