Thursday, July 5, 2018

DivvyStats: An Excercise in Architecting and Full Stack Development

My Objectives

Let me begin by saying that this project is a non-revenue project. I haven't been paid for the work. However, I felt that the benefits would justify the effort. Here's what I wanted to accomplish.
  • Architect an entire system, taking the design from conceptualization to the specification of technologies used for implementation.
  • Develop an entire system, e.g. become a "full stack" developer able to start at the beginning and work through to the actual deployment and launch.
  • Learn and develop some proficiency in a number of technologies that were to me, mostly mysterious acronyms before I began to learn and employ them.
I leave it to the reader to decide how well I succeeded in my effort. My personal feeling is that I did rather well given my somewhat inexperienced starting point. Clearly, I have more to learn; but learning more about what you don't know is part of the benefit of the exercise. Learning about things you don't know you don't know is an even greater epiphany!

Foundation Elements

Most systems are built around some fairly known and widely used technologies. This effort was no different. Here's my foundation technology list. All other technologies complimented these:
  • Delphi and its related technologies such as FireDAC, LiveBindings and FireMonkey.
  • Interbase RDBMS along with a couple of user functions (written in Delphi) used to make things a bit easier and more maintainable.)
  • Node.js, your savior or your nemesis depending on your viewpoint.
  • HTML5, an absolute must if you're doing modern web development.
  • CSS3, another "must" for modern web development.
  • JavaScript, because if you use Node it's pretty hard to use anything else. It's ubiquitous despite all of its nay-sayers.
Of course, there were lots of ancillary technologies used to support the effort, among them uniGUI (FMSoft's web application development framework), npm (Node Package Manager), Express (a Node based framework) and Bootstrap (that helps trying to style responsive websites.) For a more complete list and discussion, see the website (link below.)

Major Topic for Development

One of the decisions to make for a project of this kind is what I call "topic." Just what do you choose for development? The choice should be:
  • Complex enough to be challenging and interesting
  • Interesting and relevant to a reasonably sized audience
  • Solvable using available technologies and skill level of the project team (in this case me)
  • Related to something already of interest.
I finally settled on DivvyBikes, Chicago's bike sharing system. Not only does it satisfy my criteria, but it also offers:
  • A large repository of data free for the taking (for any legal purpose)
  • Serious possibilities for logistic complexity
  • The possibility of having an impact on urban life and transportation
  • Personal interest since I'm a Divvy user and to say I'm enthusiastic about it would be an understatement. (I recently received an award from Divvy for being their champion senior rider.)

The Results

The result of this effort consists of two major deliverables:
  • The DivvyStats website, that you can visit here: DivvyStats. Note that the link uses a nonstandard port.
  • The DivvyStats Dashboards, that you can visit here: DivvyStats Dashboards. Note that this link also uses a nonstandard port.
The website provides a lot of detail and imagery about the system, how it is architected, what technologies are employed and screen shots of expected outputs and displays. This includes things like the system overview diagram, a list of technologies with links for more information, and an E/R Diagram for the database used by the system.

The dashboards are a uniGUI application. The various displays available are explained in greater detail by the website page "Dashboards" accessible from the System Architecture dropdown of the main menu.

Effort

The entire effort occupied about 600 hours. Unfortunately, I did not track "learning curve" separately from application of technology. In some cases, much of the time spent was actually trying to learn a new technology in order to apply it effectively. I developed a half-dozen or more separate, small projects along the way to test various ideas before actually implementing something in the final product. Here are the major time investments, from most time-intensive to least:
  • The maintenance and administrative program, written using Delphi Tokyo, FireMonkey and LiveBindings. Also included were new experiences accessing Divvy APIs, using the Zip components to unzip files and then process the .csv files that came as a part of the zipped download. Along the way I developed a crude ORM and gained some practice in using some of the more common design patterns. Data cleansing was also a bit of a time-consuming effort as the Divvy data covers several years, and the files generated by Divvy early on differ in some ways from files in later years. No external files were used. The Divvy data was downloaded into RAM, unzipped, and the .csv files converted to my mini ORM entirely without writing to external media. This enabled me to achieve about a 700 row/second insertion rate on the Interbase database installed on another server locally, accessed via a gigabit LAN connection. Overall, lots of details occupied time with this program.
  • Next most time consuming was the uniGUI application, a totally new technology for me. While there is some documentation for uniGUI that is helpful, it still could use greater detail and more comprehensive content. Much of what there is to learn must be learned by reading the code and samples, and by reading and learning about the Sencha components that uniGUI deploys.
  • The actual website was next, but since the website is basically a static site, not much effort was required beyond what I had already acquired from other efforts. One of the more interesting pieces of the website was the sending of email used by the "Contact Me" page.
  • Data analysis and research followed website development time-wise. This involved determining data content, examining relationships and generally trying to clean up a few things that were ambiguous.
  • The monitor service (a Windows service program that periodically takes a snapshot of the entire Divvy system) was next. Mostly this was simply learning the most efficient way to update the database. You download 574 records and pop them into the database every X number of minutes. Some editing is required along with data conversion. (I store everything UTC, the only reliable way to store timestamps, but Divvy provides data in local format that of course suffers from all the deficiencies of daylight savings.)
  • A few other minor time chunks were taken up by architecting, database design and administration, deployment, graphics (using Adobe Illustrator) and version control (Atlassian and GitHub.)

Conclusion

Building a complete "system" is amazingly time-consuming and requires a broad base of technology familiarity. It has given me a much greater understanding of just what it takes to bring an idea from concept to Minimum Viable Product.

My suggestion is to go through the website and try the uniGUI application. I hope it provides you with a greater appreciation for the depth and variety of tasks it takes to realize a vision using IT resources.

Good luck with your own efforts!

Wednesday, April 11, 2018

MeWe: A Place for Delphi and Pascal Developers?

MeWe—The Next-Gen Social Network

First, some background on MeWe. It has actually been around for some time. Here's a link to a March, 2016 article that provides the genesis information:


MeWe Right Now

MeWe said it had 200,000 members when it launched out of Beta in 2016. When I installed the MeWe app on my Android phone, today April 11, 2018, the PlayStore showed 100,000 downloads and indicated the app was the number 2 trending social app.

So MeWe does have some traction. With all of the recent brouhaha over Facebook's cavalier treatment of its users' information and recent attempts to censor content, MeWe reports a 4,000% membership increase and a 5,000% week-to-week of invited contacts from existing members. All of this while at the same time being ad-free! Here's an article about all of this:


What Makes MeWe Different and Attractive to Some Users?

MeWe "resembles" Facebook in many ways, but it does have some very distinctive differences. Their biggest selling points seem to be:
  • Privacy. They don't collect and sell your data. Plus you have extensive control over who can see the data you post.
  • Cost. MeWe is free. Further, it has NO advertising of any kind. (To see how MeWe makes money, see the link below.)
  • Lack of Censorship. MeWe doesn't censor your content. You are expected to refrain from using the platform for illegal purposes and if reported the content will be deleted. However, they don't "check up" on you for this purpose. Also, Group owners may censor their own groups. In the large, however, MeWe is a very open and unrestrained platform for the exchange of information.
More information about MeWe is available at


A Delphi and Pascal Developers Group

All of this sounds pretty appealing to me and I got to wondering if MeWe would be a good place for a Delphi and Pascal Developers Group. Since the platform is free for anyone, the best way to test this idea seemed to be to simply start a group and see if people find it valuable. So that's what I've done.

Here's the link to join the MeWe Delphi and Pascal Developers Group:


If you don't already have a MeWe account you'll have to sign up, but it's quick and easy to do so, and you don't even have to give them your contact list! 

So you can take this as your official invitation to give MeWe a try and also see if a Delphi and Pascal Developers group is a good place for you to exchange information with other developers.

Right now, no approvals are required to join the group. You'll be automatically subscribed to the group as soon as you attempt to join. You will probably want to adjust your own settings for the group to suit your preferences, but that's pretty easy to do. The group does have some rules that I thought would be appropriate:
  • Posting must be in English. 
  • Vendor promotions welcomed if they are relevant to Delphi or Pascal. Spam will not be tolerated. This also means that excessively repeating the same posting is discouraged. 
  • Also welcomed are general IT postings of interest to the community but not necessarily directly positioned for Delphi or Pascal.
It goes without saying that personal attacks or other kinds of rude or anti-social behavior will also be dealt with severely.

Hope to see you giving this a try. I'm very interested in seeing how MeWe fares in the world of giants like Facebook and Twitter.

Sunday, March 25, 2018

Part 4 of 4—View—Using Delphi LiveBindings With TObject and TObjectList

Source Code

The source code in the form of a Delphi Project Group is available at:
The project was developed using Delphi RAD Studio Tokyo (10.2.2)

The README file contains additional useful information.

VIEW Details

This is the simplest part of the demo. This is in keeping with our objectives of making the VIEW as trivial a possible to facilitate testing. It also results in a very clean separation of concerns, effectively removing all data manipulations from the VIEW. The use of LiveBindings further isolates the VIEW from other parts of the application.

Create and Destroy

The FormCreate and FormDestroy events are used to create and free the MODEL. The pointer to the MODEL is saved as a property of the form.

Binding the UI Objects to the MODEL

LiveBindings is used to connect the data elements of the MODEL to the UI objects seen by the User. The Visual LiveBindings used to accomplish this appear in the following illustration. There are no other bindings or code fragments used to display the data. The entire VIEW is handled by LiveBindings without needing any other code. (A full-sized PDF of this diagram is also to be found in the Docs folder of the Github project or can be downloaded here.)

Delphi LiveBindings Demo Visual LiveBindings Screen Shot
Delphi LiveBindings Demo Visual LiveBindings
Several things should be observed in order to achieve this result:
  • Work from the VIEW, not the MODEL. In other words, be sure you have VULBO.pas, the Form, selected in the IDE. Then invoke "Bind Visually."
  • Be sure you have specified MDULBO (your Model Unit Name) in the VIEW (VULBO) Uses clause. This will enable the Visual Designer to access the necessary TBindSource instances.
  • The Items in the Branch TListView have been customized by setting the ItemAppearance property to DynamicAppearance and adding the Item.Text1, Item.Text2 and Item.Text3 fields. This discussion does not address how that is accomplished and I assume you already know how to do that or you can find a way to learn about it.
  • For bindings that bind to ObjectList<T> properties, be sure to connect the TListView Synch property with the Asterisk (*) item of the TListBindSourceAdapter. In the diagram above, these are named MDLBO.absEmployees and MDLBO.absBranches respectively. This will permit scrolling operations on the lists to take place correctly. The remaining item, MDLBO.absCorp is not a list; it is a single object. So it cannot be synched.

Conclusion

The discussions and techniques presented here should enable a developer to utilize LiveBindings effectively with both Objects and ObjectLists for real-world development. Of course, there are endless variations to the patterns shown, but regardless of the details of any particular implementation, the fundamental principles used to manage LiveBindings with Objects and ObjectLists remain; developers who understand the fundamentals will be able to expand and adapt the fundamentals to the particular needs of their application.

If you made it this far, thanks for reading and (I hope) understanding! Part 3 is especially daunting because of the number of things that must be managed and coordinated, all of which is compounded by the severely inadequate and confusing naming conventions (if they can be called conventions at all) of LiveBindings.

When all is said and done, it is pretty cool. Enjoy!


Using Delphi LiveBindings With TObject and TObjectList<T>—Part 3 of 4—Model Details<==Previous

Part 3 of 4—Model Details—Using Delphi LiveBindings With TObject and TObjectList

Source Code

The source code in the form of a Delphi Project Group is available at:
The project was developed using Delphi RAD Studio Tokyo (10.2.2)

The README file contains additional useful information.

MODEL Details

The MODEL functions as the main mechanism to manipulate the data for the application. In the case of an application that makes use of SQL or other persistent databases, we would expect to find FireDAC components managed by this class. In the present case, we are not using any kind of database that FireDAC supports, but never-the-less our MODEL will have data objects to manage. 

The Model Is a Data Module

The file MDULBO.pas specifies the MODEL part of the system. The model is a class named TMDLBO and descends from a TDataModule class. This permits the dropping of components on the MODEL design surface.

Instantiation

Normal Delphi instantiation has been disabled for this class. As a singleton class, a different mechanism was used that hides the customary Create constructor and provides instead a function named MDLBOGet that checks to see if the class has already been instantiated. If not yet instantiated, the class is instantiated and a pointer retained for future requests. In any case, the pointer for the instantiated class is returned to the caller.

The GDLBOGet routine invokes the Create constructor as needed. During the execution of the constructor, the database class TCorp is instantiated. As we described earlier, this results in a chain of constructor calls to the various classes of the database; the final result is an instantiation of the database data along with the instantiation of the MODEL (this class.)

Pay special attention to the TMDLBO.Create constructor. When the MODEL is instantiated, a call is made to this constructor. As a singleton, this occurs only once. Note that the constructor first calls the constructor for the database classes, to instantiate the entire database class structure prior to processing anything for the MODEL class. Following this it calls the inherited Create for itself that completes the instantiation of the MODEL.


It is important that this order be maintained because the OnCreate events of the TAdapterBindSource components must be able to access the already-instantiated database classes. Since the TAdapterBindSource components are created by the inherited Create call, instantiating the database data classes prior to the call to inherited Create satisfies this requirement.

LiveBinding Objects

LiveBinding Objects and ObjectLists behave similarly to the bindings for database objects. We note that the database components connect to a TAdapterBindSource just as do Objects and ObjectLists, but that is the extent of our involvement with database components in this discussion. We are only considering Objects and ObjectLists at this time, so any further discussion will not include references to database components (FireDAC, dbExpress, etc.)

In our current application we have three data sources to consider:
  • TCorp is a single object.
  • TBranch occurs multiple times and is managed by a TObjectList<TBranch>; property of TCorp.
  • TEmployee occurs multiple times and is managed by a TObjectList<TEmployee> property of its parent TBranch.
We customarily use a TDataGeneratorAdapter to create arbitrary data for use at design time. A corresponding TAdapterBindSource is needed for each generator to allow us to use LiveBindings to connect the data sources to other components, typically display components. With that in mind, the MDULBO.pas file that contains the MDLBO class appears as follows:

MDLBO Data Module from MDULBO.pas
MDLBO Data Module from MDULBO.pas
Values for the TCorp display components are generated by the dgCorp component. Values for the TBranch display components are generated by the dgBranches component. Finally, values for the TEmployee display components are generated by the dgEmployees component. Each of the data generator components has a corresponding TAdapterBindSource component that connects to its corresponding data generator. To force the TAdapterBindSource components to be a property of the data module, you must drop them on the form from the Tool Pallet and connect them each to the proper TDataGenerator component. If you use Visual Live Bindings to automatically create the TAdapterBindSource components, they will likely end up on the user interface Form—the VIEW. We want them as a part of the MODEL, not the VIEW, to achieve a separation of concerns. Hence, they should be manually placed to achieve our desired result.

In general, Objects and ObjectLists are connected to LiveBindings as illustrated in the following diagram (a full-sized PDF of this diagram is also to be found in the Docs folder of the Github project or can be downloaded here):

Live Bindings Using Objects diagram showing object relationships.
LiveBindings Using Objects
So far we have connected the data generators to their respective TAdapterBindSource components. Data generators are a special case, and the component contains both the generator and the TDataGeneratorAdapter needed to complete the configuration. This is the case illustrated by the rose colored combination in the diagram. Objects (goldenrod) and ObjectLists (blue) are different. The developer must provide both the data source (an Object or an ObjectList) and the developer must provide a TObjectBindSourceAdapter (goldenrod) or TListBindSourceAdapter (blue) as appropriate.

Only one of the paths to the TAdapterBindSource can be active at any given time; we begin by using the TDataGenerator to assist the design effort. But now, at run time, we are faced with replacing the design setup by the actual run-time data sources, either from a TObjectBindSourceAdapter or a TListBindSourceAdapter. This is where the Type Aliases defined in the DBLBO.pas file help. We can easily refer to CorpObjectBSWrapper, or BranchListBSWrapper, or EmployeeListBSWrapper and be confident of referencing the correct kind of components. The actual substitution of data sources (from TDataGenerator to TObject or TObjectList) takes place in the OnCreateAdapter event handler for each of the TAdapterBindSource components in the application.


In the following example, be sure to understand the different classes involved. Prior mention has been made of the draconian naming failures of LiveBindings. Refer to the previous diagram and the Type Aliases mentioned so that you understand what types are being instantiated. Examine the absCorpCreateAdapter event handler code in order to follow the following steps:


  1. You must first create a TObjectBindSourceAdapter for the TCorp instance. See the diagram above. This object behaves just like a component, but cannot be dropped on a form because of its generic nature. The code uses the Type Alias to create the object and save it as a property of the form, just as though it had been created at design time. Notice that during the create process, you must identify the specific object you're connecting, in this case the Corp instance of the TCorp class. The fact that it's a TCorp instance is inherent in the Type Alias, where TCorp is specified.
  2. After the CorpWrapper is created, it must be passed back to the TAdapterBindSource event so that creation of the TAdapterBIndSource can be completed.
These same two steps must be performed for each data source we want to connect to LiveBindings. There is an OnCreate event handler for each of the three data sources we are working with. Note that for Branches and Employees we specify the TObjectList properties as the data source rather than just a single object.

You should now also understand why instantiation of the actual data objects must precede the creation of the LiveBinding objects themselves; the data objects are referenced by the LiveBinding objects and hence must be available at the time the OnCreate event handler fires.

Handling Scrolling, Deleting and Inserting

We now consider how to handle child ListView displays when the parent list's current item changes. A change in the currently selected Branch occurs when the item focus changes. This can be caused by
  • Clicking on another list item or moving to another item using the TNavigator component.
  • Inserting a new Branch. The focus will change to the newly inserted Branch.
  • Deleting a Branch. The focus will change to one of the remaining Branches or, if the list has become empty (zero Branches) will not have any currently focused entry.
The TListBindSourceAdapter has an extensive set of event handlers that are used for handling these conditions. In our case, the particular component we are interested in is identified by the Type Alias BranchListBSWrapper. When we create this object, we must also provide method pointers to the object so that the needed events are handled. In addition, we must provide the actual event handler code that will be executed when the event fires. First, take a look at the event handler code. Note that the signature is a requirement of BranchListBSWrapper.

Two things must take place when the Branch TListView Item focus changes:

  1. We must identify the current TListView object, that contains the TObjectList<TEmployee> property for the currently selected Branch.
  2. We must update the Employee wrapper to point to the TObjectList<TEmployee> property identified in the first step.
Note that the special case of an empty Branch list results in setting the Employee wrapper to a nil value. (No items are displayed.)

The first step is satisfied by locating the TBranch object using an expression with two casts to "dig down" into the Branch TBindSourceAdapter that contains a pointer to the current entry.

The second step updates the EmployeeWrapper (saved as a property of the TMDLBO class) with a pointer to the current Employee TObjectList<Employee> extracted from the Branch obtained in the first step. The SetList method is used to do this. It changes the pointer to the data that will be displayed by the TListView.

Finally, we reposition the Employee List selection to the first item and set the EmployeeWrapper to Active to display the Employee List contents.

There are some additional considerations when a master/detail relationship between two ObjectLists exists. When the focus of the master changes, LiveBindings must be updated to reflect the new detail list that is related to the master. This is shown by the following CreateAdapter event for our Branches list. After the BranchListBSWrapper has been created, three Event Handlers are specified so that synchronization will take place when the Branch Item focus changes. In all other respects, this OnCreate Event is identical to the one previously described, except, of course, it refers to objects appropriate to Branches.



This completes the examination of the MODEL code. There are quite a few "moving parts" to the entire solution:

  • Data classes
  • Data generators
  • LiveBindings components
  • Objects to be displayed by LiveBindings
  • ObjectLists to be displayed by LiveBindings
  • Considerations about creation order
  • Creation of LiveBindings objects at run time and linking them into other LiveBindings components along with specifying needed event handlers
  • Handling TListView scrolling in cases of master/detail relationships
  • Event handler code to implement desired behaviors when TListView entries scroll, are created or deleted
The number of lines of code required to achieve this is not particularly large (except for the actual data objects in your application, which will vary depending on the complexity of your data.) It does require that you "get organized" and perhaps develop a check list of required steps to ensure that you include all the necessary code.

Next we consider the final (and much simpler) piece of the puzzle, the VIEW.

Using Delphi LiveBindings With TObject and TObjectList<T>—Part 2 of 4—Database Details<==Previous
Next==>Using Delphi LiveBindings With TObject and TObjectList<T>—Part 4 of 4—View

Part 2 of 4—Database Details—Using Delphi LiveBindings With TObject and TObjectList

Source Code

The source code in the form of a Delphi Project Group is available at:
The project was developed using Delphi RAD Studio Tokyo (10.2.2)

The README file contains additional useful information.

Database Details

The application database is defined by three classes contained in the file DBLBO.pas. When instantiated, they form a tree structure not unlike a relational database having two master/detail relationships. Diagrammatically, they appear as follows (a full-sized PDF of this diagram is also to be found in the Docs folder of the Github project or can be downloaded here):

Delphi Livebinding Objects Data Structure Database Diagram
Delphi Livebinding Objects Data Structure

The Database Classes

TCorp

This class is instantiated a single time and contains information about the fictitious corporation used in the sample application. In addition, it contains a TObjectList<TBranch> property that is used to store all of the instances of Branches owned by the corporation.

TBranch

This class is instantiated once for each Branch owned by the corporation and that appears in the TObjectList<TBranch> property of the instantiated TCorp.

TBranch also contains a TObjectList<TEmployee> property that contains a list of the instances of TEmployee for all of the employees who are assigned to the corresponding branch.

TEmployee

This class is instantiated once for each Employee assigned to the Branch and who appears in the TObjectList<TEmployee> property of the corresponding TBranch.

Instantiation Hierarchy

The Create constructor of TCorp not only instantiates the TCorp object; it also creates three arbitrary Branches by invoking the TBranch.Create constructor for each of the branches.

Each invocation of the TBranch.Create constructor instantiates the branch and provides arbitrary properties, but also invokes the TEmployee.Create constructor for each of the employees assigned to the corresponding Branch.

In this was, as single call to TCorp.Create will result in the instantiation of the entire database.

Arbitrary Data

Each of the database object Create constructors provides for the specification of arbitrary data when invoking the constructor. In this way, the invoker provides the data needed for the instantiation.

Overloaded Creates

The constructors for TBranch and TEmployee have overloaded versions. In one version, the data used to populate the fields of the instance is provided by the invoker. This is the form used when the database objects are first instantiated.

The second form of Create does not provide for any values for the properties of the new object. This is the form used when the TBindNavigator components process the Add (+) button. 

Type Aliases

The DBLBO.pas file also specifies three Type Aliases:
  • CorpObjectBSWrapper that is a TObjectBindSourceAdapter<TCorp>.
  • BranchListBSWrapper that is a TListBindSourceAdapter<TBranch>.
  • EmployeeListBSWrapper that is a TListBindSourceAdapter<TEmployee>.
Using these Type Aliases solves a couple of problems.
  1. The naming conventions used by LiveBindings are incredibly bad. Class names like TObjectBindSourceAdapter, TListBindSourceAdapter and TAdapterBindSource make repeated use of the words "Source," "Bind," and "Adapter" with no clear indication of what a given class is supposed to do. Using a TypeAlias to change the name to something meaningful (they really are classes that wrap around Objects and Lists) makes it much easier to develop and understand the code.
  2. The objects referenced by the Type Aliases are, in fact, generic in nature as the Type Alias indicates. This is what prevents their specification as a component in the first place. By using a Type Alias the cumbersome generic notation disappears from view, again facilitating cleaner, more easily understood code.
This completes the examination of the database objects. Next, we'll consider the Model Details.

Using Delphi LiveBindings With TObject and TObjectList<T>—Part 1 of 4—Overview<==Previous
Next==>Using Delphi LiveBindings With TObject and TObjectList<T>—Part 3 of 4—Model Details

Part 1 of 4—Overview—Using Delphi LiveBindings With TObject and TObjectList

Source Code

The source code in the form of a Delphi Project Group is available at:
The project was developed using Delphi RAD Studio Tokyo (10.2.2)

The README file contains additional useful information.

Overview

Need

Working with another project, I had a need to import a JSON object with a number of properties, one of which was an array of JSON objects. This lends itself to a hierarchy of Objects and ObjectLists, so I set about constructing the needed data objects and parsing my input JSON (that comes from a file) to populate the objects I had created.

I also wanted to display the contents of the data thus imported using TListView components in a FireMonkey application using LiveBindings.

I realized that I didn't have much understanding about all of this so I set about learning a thing or three. What I share with you now are the results of those explorations.

Demo Project

The Demo Project is named LiveBindingObjects.exe and when run, produces the following UI (a full-sized .png of this screen shot is also to be found in the Docs folder of the Github project or downloaded here):

LiveBindingObjects.exe User Interface Screen Shot
LiveBindingObjects.exe User Interface

Launching the Program

After cloning or downloading and unzipping the source project group (if necessary) make the project LiveBindingObjects.exe the active project. Choose the Target Platform if necessary. Note that this project is only intended to be a Windows desktop application. Press F9 or the Run button of the IDE.

User Interface Structure

  • At the top of the UI there is a panel that provides static information about a fictional corporation.
  • Beneath the top panel are two panels containing the following:
    • The left hand panel contains a Navigator control at the top while a TListView occupies most of the lower part of the panel and displays information about the corporation's branches.
    • The right hand panel contains a Navigator control at the top while a TListView occupies most of the lower part of the panel and displays a list of employees working at the currently selected branch from the left hand panel.

Using the Program

  • You can navigate to any of the displayed entries by clicking on the desired list item.
  • You can also navigate to any of the displayed entries by clicking on the Navigator buttons to move the selection.
    • The first four buttons in each navigator, Top, Up, Down and End move the corresponding List's selected item.
    • The next two buttons, Insert and Delete execute the corresponding actions in their corresponding List.
    • the final four buttons, Edit, Post, Cancel and Refresh respond, but in the present implementation seem to have no effect. They have been left for completeness although you can easily hide them since they currently serve no useful purpose.
  • When the selected item for Branches (the left ListView) changes, the Employees ListView (the right ListView) changes to display only the employees at the newly selected Branch.
  • Clicking the Add button (+) will insert a new item into the corresponding list using arbitrary values as placeholders. Each new entry has a number that advances by one for each new entry inserted.
  • When a new Branch is inserted, a blank Employee list is displayed as there are no employees for that branch. New employees may be inserted, however, for the newly inserted Branch.
  • Clicking the Delete button (-) will delete the corresponding selected entry after you respond Yes to a safety confirmation prompt. If you delete a Branch, all of its employees are deleted as well.
After a few changes, the UI might appear to be something like the following (a full-sized .png of this screen shot is also to be found in the Docs folder of the Github project or downloaded here):

LiveBindingObjects.exe User Interface After Some Data Changes User Interface
LiveBindingObjects.exe User Interface after some data changes.

Program Structure

This is a simple program, but it does have structure. 

MVVM, MVP and MVC

There are endless explanations, debates and disagreements on the internet about these three design paradigms. Yet all three paradigms seem to share the same fundamental functionality:
  • The presentation layer, or "VIEW" should contain as little logic as possible. They are difficult to test, and hence should be kept as simple as possible.
  • The data layer is handled by something called the "MODEL" that manages the data used by the application. It essentially hides the concerns about data from the VIEW. 
  • Between the VIEW and the MODEL is something that is variously called the VIEW-MODEL, the PRESENTER or the CONTROLLER. These three variations give the paradigms their distinctive names. It acts as an intermediary between the VIEW and the MODEL.
Arguing about the exact functioning or relative merits of these (and perhaps other similar) design paradigms is about as useful as arguing about how many angels can dance on the head of a pin. What seems to be universally true, however, is that if you bear in mind that the ultimate objective of these and other similar design paradigms is the separation of concerns, you won't be far off with designing well-structured, maintainable code.

Thus, for example, if you are building an application that screens loan applications, you will probably want to separate the code that queries the major credit bureaus from the code that updates the local database with the loan status. It makes no difference whether you call this a VIEW-MODEL, or a MODEL, or  CONTROLLER so long as you maintain a clean separation concerns you will probably end up with an easily maintained, testable application.

I would further submit that Delphi and Delphi's RAD is not well-suited to the ways that these current paradigms are understood, anyway, and attempting to put Delphi's round peg into a square hole will result in worse, not better structure and certainly a lot more code, much of it unnecessary. Just maintain separation of concerns and avoid putting code in the VIEW and you'll be well along toward producing quality, maintainable, testable code.

The demonstration program has two major areas of concern:
  1. Model and Database that manage the data used by the program.
  2. View that is the User Interface that presents data to the user and receives input from the user that is then used to drive the Model to perform the desired functions.

Model and Database

The MODEL is implemented by the file named MDULBO.pas that contains a single class that descends from TDataModule. It is a TDataModule descendant to permit dropping components on its design surface. The Delphi automatic form creation mechanism for this object has been disabled. Creation and Destruction is handled by the application code, not the Delphi provided automatic code.

Ancillary to this basic MODEL is file DBLBO.pas, that defines the three classes (all descended from TObject) that define the actual data used by the program. By creating these classes, the database is created. Also a part of DBLBO.pas are an enumeration (not used by the demo program) and three TypeDefs that will be explained in a later blog. The constructors for the database classes are also responsible for populating the created objects with appropriate data. Thus, database definition and initial population are all contained in these three classes. There is a more comprehensive discussion of these classes in a subsequent blog.

View

The view is implemented by the unit contained in the file VULBO.pas. It contains a single class, descended from the customary TForm ancestor. The automatic form creation for the VIEW has not been disabled and functions as expected. The VIEW contains a number of component definitions, and a pointer to the MODEL, but other than that, it contains almost no code, making it quite easy to test.

This concludes the present discussion. In the next post, we'll take up the Database portion of the MODEL in greater detail.

FireMonkey String Grid Responsive Columns

FireMonkey String Grid Responsive Columns Code to Cause Column Widths to Change When String Grids Are Resized Overview I have a FireMonke...