Reduce code lenght using array more wisely ?

L

Laurent

I've just completed my first big add-in project. The program manages
"customizable sets" of extensive Word display options. It basically
reproduces most of Word option dialog items, but save choices in separate
configuration sets that can be apply at a click of a button. The add-in
works well, is ready for multilingual use and quite fast. :)

My problem is bloated code, not speed, or function. It has more than 2000
lines. Maintenance is not easy.

The biggest problem I have is a long serie of variables that are part of
each parameter set, but often different data type . The number of variables
was quite small at the beginning of the project but it kept growing as I
added more and more features. (I also have to admit that I learned VB
only two weeks ago and I probably made gross design mistakes in some parts
of the coding :) )

For each variable, there are 10 customizable sets and 10 "factory" sets,
for a total of 20 possible and applicable sets of display parameters.

It goes a bit like this:

Public strName(10, 1) As String
Public bBlue(10, 1) As Boolean
...
... (long list)
...
Public bParagraphs(10, 1) As Boolean
Public iType(10, 1) As Integer
Public sZoomValue(10,1) as single
etc.

for about 25 different variables for each set of parameters.

Because they differed in type, I designed an option form with a mix of
checkbox, textbox, spin and combobox that display and change any set.

All operations in the add-in must repeat all the list of variables:
1. Declare variables
2. Assign hard-coded factory defaults to variable
3. Management routine to change factory default (maintenance)
4. Read custom settings from registry
5. Save custom settings to registry
6. Reset custom settings to factory default
7. Push any set content to the form
8. Put form content back to a given set
9. Apply a set of parameters (main function)
Moreover, each variable's corresponding component on the form has a name
much similar to the variable name and can have:
10. Assigned caption from external source (for multilingual purpose)
11. Assigned controlTip from external source
12. Code to change enable,visible,locked,or backstyle according to
state and condition of other parameters (exemple: the draft mode
and AreaStyl do not work in Page Type).

25 variables X 12 basic operations = already 600 lines of mostly repeating
code ! :-(

Now, using a three dimension array instead of a two dimensions one seems
the logical path to follow to reduce code lines. Instead of having:

25 variables x (10 sets x 2 mode)

one could have (25 variables x 10 sets x 2 mode)

(or even (25 variables x 20 sets) but it would be more confusing)


It would allow much more compact code, like using FOR... NEXT loop instead
of a long list of 25 pretty much similar operations.

But NOT ALWAYS: for example, boolean parameters are treated differently
from integer parameters in most operation. So I would preferably create
25x10x2 array for each data type

I am not sure however it will change anything in speed of execution or
memory requirement. Actually, VBA help file says it will INCREASE memory
requirements, as array takes more memory than single variables !

The problem with a 3D array is also how to tell which parameters is in
which array location.

For example, I have at present such lines as:


chkboxDraft = bDraft(iSetNo,iMode)
and
cboType.ListIndex = iType(isetNo,IMode)

If I had one or more 3D arrays it would be

chkboxDraft = bBooleanParametersArray(7, iSetNo, iMode)
cboType.ListIndex = iIntegerParametersArreay(3, iSetNo, iMode)

But here, HOW can I remember that the value 7 refers to the Draft setting
and the value 3 in the other array refers to Type setting ? Refers to an
handout paper print ? Wont it be even more confusing than actual indiviual
variables ? Less lines of code but much more bugs possible ?

It's here I am wondering and decided to submit this design problem to this
group.

How would you design the whole gizmo if number of lines code is an issue

Or, is number of lines codes an issue after all ?

Thanks for sharing you general perspective on this.
 
J

Jonathan West

Hi Laurent,

OK, here are my thoughts.

Generally, keeping number of lines of code down is a Good Thing, so long as
you don't get obsessive about marginal savings. The reason for this is that
if you use loops rather than repeated blocks of similar code, then it is
much easier to maintain the whole thing when the inevitable calls for new
features come along.

So, that being the case, it would be a good idea to create that loop. But
you still have the problem of having disparate variable types.

For this, it seems that there are a number of weapons you can bring to bear.

1. The Controls collection. This allows you to cycle through all the
controls in the collection, without naming each one in turn.

2. The Tag property. You can put any arbitrary string into the Tag property
of any control. For your purposes, it can be used to hold some kind of code
indicating how the control should be dealt with.

3. The Type property and the TypeOf operator. Each control is of a
particular type (e.g. MSForms.TextBox) and you can find out what type it is
using the TypeOf operator.

4. The Name property. Each member of the Controls collection has a name,
which is a string which corresponse to the name you give the control at
design time.

Now, having created the appropriate data in the registry and put the
appropriate tags in the you can do something like this in some code included
in your UserForm

Dim oControl As Control

For Each oControl In Me.Controls
If TypeOf oControl Is MSForms.CheckBox Then
'do checkbox stuff here
ElseIf TypeOf oControl Is MSForms.TextBox Then
'do textbox stuff here
End If
Next oControl

I hope this helps!


--
Regards
Jonathan West - Word MVP
MultiLinker - Automated generation of hyperlinks in Word
Conversion to PDF & HTML
http://www.multilinker.com
 
L

Laurent

in
microsoft.public.word.vba.beginners
Another solution I was thinking of was creating a class instead of
using an array for my set of variables, but this is much too advanced
for me, and I am maybe even wrong as I don't understand yet fully the
class and object concept after only two weeks :)

Afterall, classes aren't so difficult.


Module Class1

Public Blue As Boolean
Public Name As String
Public Zoom As Integer


Module Test
Sub Dummy()

Dim MySet(1 To 20 As New Class1
MySet(1).Blue = True
MySet(1).Name = "Screen 1"
MySet(1).Zoom = 5
MySet(2).Blue = False
etc.

One multidimensional instance wich can hold whole my data.


But it doesn't change much the lenght of the code for many operations, I
have to go now one by one by property. One example between many possible:

Savesetting "appname", "section", "set"&str(i), "Blue", &MySet(i).Blue
Savesetting "appname", "section", "set"&str(i), "Name", &MySet(i).Name
.... etc.
again for the 30 variables now turned into properties.


unless I can do a loop seeking for name of properties in the object.
Something like (pseudo-code below)

for each binary properties in my object
Savesetting "appname", "Section, Propertyname, propertyvalue
next


Is this possible in VBA ?
 
L

Laurent

Hello and thanks again for your time

in
microsoft.public.word.vba.beginners
So for instance, code that is only used to control the
UserForm should generally be in the UserForm as Private routines. OKay

1. Do you really need to have an intermediate array of variables?
Could you assign directly between the properties of the UserForm and
the settings in the registry, like this

TextBox1.Text = GetSetting("My app", "My key", "My value")

That could be. I didn't think at all that way. My first idea was to limit
disk writing operation to minimum which I though could slow down the
program, but after testing, it seems it isn't the case. I might give I a
try. Some marginal functionnalities of the program maybe be difficult to
maintain with this method, but it could work.
2. If you do need the array of variables, you can have different
elements being of different types You do this by defining a
user-defined type, like this.

Private Type uSettings
Name as String
Blue as Boolean
Paragraphs as Boolean
Type as Integer
ZoomValue as Single
End Type

That code would go before the first Sub or Function in the UserForm or
module. Then, within a routine, you can define an array of that type
and use it like this

Dim tSettings(10) as uSettings

tSettings(0).Name = "name of item zero"
tSettings(0).Blue = True

Great! This is much more practical !
You could use an array of user-defined types as I described above.
That is useful where you have data of different types to deal with.

Yes, you just point me out a valuable function.
I don't think you need a class for this. An array of user-defined
types is simpler. A class is useful if as a result of changing one
property, you have consequential changes to something else, so you
have code associated with the data in the class. (A UserForm is
actually a kind of class module, where Microsoft has kindly built a
user interface into it for you.)

You're right. In the last hours of the evening, I played with this idea (I
posted something about it), and find the class valuable, but not much for
this case.
Yes, there are two array swap techniques you haven't come across yet. :)

First, you can assign the whole of a user-defined type to another in a
single assignment operation, like this

tSettings(1) = tSettings(0)

That assigns *all* the properties in tSettings(0)

That would be terrific!

Another way of working is that you can use a Variant to hold an array,
so you can have something like this


Dim v1 As Variant
Dim v2 as Variant

v1 = Array("Zero" "One", "Two", "Three", "Four")
MsgBox v1(1)
v2 = v1
MsgBox v2(2)

Another thing I have to explore.

I tried to avoid variant because in most documentation available it is
writen it is always better to have appropriate data type. As my main
motivation to write the add-in was to learn good VBA practices, I decided
to strictly follow this advice. Now, I maybe went a bit too far in
following the guidlines, I now tend to I agree :)

You can fix this problem by using constants. Put the following at the
start of the module before the first routine

Private Const cZoom As Long = 2
Private Const cShading as Long = 3

Then those 2 lines of code could be

myiArray(cZoom ,iSetNb, iSubSet) = (the zoom value)
myiArray(cShading ,iSetNb, iSubSet) = (the field shading value)

That's it ! Yes, that's it. Using constants to identify the index in
larger private type 3d array, while still being able to easily identify the
index by name. Simple as that. I go right in this direction ! I predict
some line reduction ahead.

Thanks a lot for all your advices.

One last word: VBA is fun and my productivity might be lost a bit in the
last days because as spend too much time "under the hood" rather than
driving the car, but in the long term, I have now an unvaluable knowledge
and productivity will crash the roof. The experience of learning VBA is
also exhilarating, and having experienced people like you to take time to
answer contribute to that feeling. I will try to rewrite the code in the
upcoming days, and I'll tell you how it went. Thanks again

Best regards
 
L

Laurent

in
microsoft.public.word.vba.beginners
That's it ! Yes, that's it. Using constants to identify the index in
larger private type 3d array, while still being able to easily
identify the index by name. Simple as that. I go right in this
direction ! I predict some line reduction ahead.

Not private type array. Just four 3d array based on datatype. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top