We had a memory table control based on an array and when adding a new row, we needed to move each column of the table into its corresponding structured array fields. Unfortunately TableSave only works with browsing table controls, so I had to create my own function to move each field. Well when I got to the second table control in the project that needed a similar feature, I decided it was time to figure out how to do it dynamically and created a generic function.
As we explore this code, we will cover several advanced subjects, such as Enumeration of controls, Indirection, Dynamic Code Execution, and Structured Arrays. So read on ….
I work with structured arrays a lot. I love structured arrays. The Dashboard Class for TeamAlogy.com has over 250 structured arrays in it. OK, you might even say I have a Structured Array addiction!!!!
So I often have tables controls based on structured arrays. This might be a local structured array I created just to make the management of a memory table easier, or it might be one of Andy Stapleton’s File Manager classes, which if you grossly over simplify them are fancy structured arrays.
WX has a great function called TableSave that will move the values from a Table to their source. But depending on what I am doing I sometimes need to code the logic myself, so I can do additional things, like make calls to the File Manager Class methods to actually update the database.
There is a lot to take away from today’s code, even if you don’t have a need for the exact function, the concepts it employs, Enumeration, Indirection, Dynamic Code Execution, Structured Arrays, can be use to solve many programming challenges that use to be difficult to down right impossible to handle.
So let’s start with what is a structured array, in case you haven’t worked with them. Well a structured array is an array of structures. There that should clear that up 🙂
A structure is just a complex data type. Let’s look at the structure shown below.
The first thing to understand is that isn’t a variable it is a variable type. Just like string or int are variable types. For those of you from the Clarion world it would be called a Group.
So at this point all that code has done is declared a variable type. It isn’t usable until you declare a variable of that type.
Once we have a variable of that type then we can work with the Individual components of the structure or with the structure in its entirety as you see in the following code
Very useful and a great way to work with chunks of related data, but once you realized that its a true variable type, you realize you can do really crazy things with it like create an array of your new variable type.
In the above code we define our structured array. And then we add one entry, myQuestion which is a variable defined as a QuestionStr (the structure), to the structured array. That is a quick overview of structured arrays. If you want to learn more search the online help for structured arrays or even better, one of the articles in the voting for Uncle Pete’s corner is a Structured Array Primer, so go vote for it.
Today’s example we are going to be using the File Manager classes, which make it a little harder to see the structured array code, but trust me its really what it is.
So now on to the example …
A simple edit in place table, but because I am using the File Manager Classes, I need to make some method calls as part of the updating of the array. But again, don’t get hung up on the why or how I am using the function. The real point of this article is to explore some building blocks that can be used for many different things.
So lets take a look at the code in question. This code handles moving the data from the table to the structure array (File Manager class in this case) and saving it to the database (via File Manager method calls).
It loops through the table control and based on if the colOrderDetailSysID column has a value or not, it does either an insert or update. In the update code it does two method calls to the File Manager class, RefreshByID to fetch the record, so the file pointer will be correct for the update, and then RecSave to update the database. The insert code only needs to do a RecSave.
Notice the 4 assignment statements that are repeated.
Not the end of the world but as I get older (and crouchetier), I am always looking for ways to reduce code. Reduced code = Reduced Maintenance.
That is 4, or actually 8 because of the duplication opportunities for me to mess up and assign the wrong variable. Or even worst later on add a column to the table and forget to add another assignment line to the code.
Sounds like the perfect opportunity for a generic function that will take any table control and do the assignment from every column in that table control to the variable it is linked to. I wouldn’t even attempt something like that in most languages but in WX its 16 lines of code!
First lets replace those lines of code with a call to our new function.
The function was the name of the table. Notice instead of hard coding it as a string I use the ..Fullname property. This way the full name including the window name and any container controls will be included, and if someone renames the table, my code won’t break. The second parameter is the structured variable to use for the assignment. Remember what we are doing is having a function write those 4 lines of code for us.
And now for the function
There is a lot happening in that 16 lines of code!
Line 1: The function accepts 2 parameters the name of the table control, and the structured variable that we want to update. Important to understand this isn’t the array but just an instance of the structure. So in our examples from above it would be myQuestion, not myQuestions. We pass in the name of the table control because this function is going to be generic and work for any table control based on a structured array. So whats with the <useful> ? That is a compiler directive that tells the compiler that even though it doesn’t detect the use of the parameter it is indeed need. With out it, the compiler would show a warning that inStructure is not being used. It can’t detect its use because we are only using it in dynamically generated code. So in an effort to be kind to the next programmer, I used the useful tag so that there isn’t a compile warning and anyone that comes behind me knows the variable is used and doesn’t arbitrarily delete it.
In our example:
TableName = frmOrderHeader.tblDetails
tmpStructure = OrderDetailRec
Line 3: We get the name of the array the table control is based on. We are using indirection so that we can use the table name dynamically. This statement is equivalent to:
ArrayName is string = frmOrderHeader.tblDetails..BrowsedFile
But again by using indirection our function is generic.
In our example:
ArrayName = :Rec.OrderDetails.arrRec
Where did it get that value? From the content tab of our table control.
Line 4, 5, and 6 are declaring a few variables.
Line 8: Sets up a Loop of all of the columns of the table control. Two things to note about this line. First it uses the TableCount function to get the number of columns. Second it uses the _TO_ syntax instead of just TO. The reason for this is with out the underscores the TableCount function would be evaluated with each loop. Since we know it isn’t going to change during the loop, we can use the _TO_ syntax so that it is only evaluated once during the initial loop. The WX editor even gives us a hint about this at design time, if we don’t use the _TO_ syntax.
In our example we have 6 columns to loop through
Line 9: Gets the name of a column, using one of the WX enumerate functions. There are several enumeration function that let you programatically access window controls.
In the first pass of our example:
ColumnName = colOrderDetailSysID
Line 10: Get the name of the field the column is linked to. We are using indirection, the column name retrieved from Line 9, and the FileLink property to accomplish this.
In the first pass of our example:
FieldName = :Rec.OrderDetails.arrRec.OrderDetailSysID
Where did that value come from? From the link tab of our table control, for this column.
If you have a keen eye you might notice something wrong with this variable name however. That isn’t a valid variable name. It is referring to an element of an entry in the array, but it isn’t referring to a particular entry. For that to be a valid variable name it would have to read like this:
Where Subscript# would be the subscript of the particular array entry. The table control is doing some black magic stuff to translate that into the “current entry” of the array as it populates the table. Our next few lines of code will handle that for us.
Line 11: Checks to see if the column is indeed linked to a source variable. If not then we don’t need to do an assignment statement. The extended column is an example of this. It isn’t linked to the array, we calculate it on the fly in the Row Display Event.
Line 12: This handles the whole array versus instance issue discussed on Line 10. The goal of this function is to assign the values from the table columns to a structure variable we provided. So it uses the Replace function and replaces the Array Name portion of the Linked Field with the Structured Variable Name. Now you know what line 3 was for!
In our example after this statement executes for the first pass:
FieldName = inStructure.OrderDetailSysID
Line 13: Is building up our assignment statements.
After the first pass of our example:
AssignmentStatment = inStructure.OrderDetailSysID = frmOrderHeader.tblDetails.colOrderDetailSysID
And after all the loops are completed Assignment statement contains
Looks pretty similar to our original assignment statements doesn’t it? They are prefixed with inStructure, but because we passed OrderDetailRec by address into inStructure we are really updating OrderDetailRec. You may notice that we only had 4 assignment statements in our code and there are 5 here. That is because it is also doing the assignment of OrderDetailSysID, which we didn’t worry about in our hand code because we knew it would not change. A small trade off for not having to hand write the code and worry about the maintainability.
Line 16: Is where the real magic happens. It executes the dynamic code we generated and therefore makes the assignments. The ability to execute dynamic code is what makes my Business Rules Class, my generic File Import, and many other bits and pieces of my foundation code work.
And that is all there is to it. Like I said whether you use the function as is or just pick it apart to learn and experiment with some of the concepts it exposes, I think there is a lot to take from that 16 lines of code.
Be sure to go over to wxLive.us and watch the Uncle Pete’s corner webinar that is the companion to this article.
Uncle Pete’s Corner is webinar series on all things WX, to watch the recorded version of this webinar, many other WX related webinars or to watch future ones live go to wxLive.us