hcOPF - Dynamic Attribute Properties

July 6th, 2010

One pending improvement in hcOPF is that mediators need to take on the responsibility to get the control to indicate in a consistent fashion that an attribute is required, read/write or readonly.  Currently this is handled using RTTI by the ThcUIObjectBinder when the object is bound.

Objects are bound only once when a form is shown, so each control is only ever updated once to reflect the properties of an attribute.  This can be a problem when you have an attribute whose value you generate a default for in new objects, but allow the user to change it, and for existing objects is ReadOnly.  The way to handle this currently in hcOPF is contrary to the way I’ve typically seen it handled in a Delphi application with non-data aware controls.  Normally the developer would interrogate the object or attribute for it’s status and change the UI control appropriately.

In hcOPF the framework does this for you, most of the time, but in this scenario it cannot.  The reason is that ThcAttributeDef.Properties contain the ReadOnly property and they were never designed to be changed dynamically.  The ThcAttributeDef has no knowledge of the attribute to which it’s attached to prevent circular references, and minimize memory usage.  All ThcAttribute instances created from the ThcAttributeDef have a pointer to the ThcAttributeDef so they can defer calls for information contained in the definition to that object, making it a many to one relationship.  I could have added a multicast event in a ThcAttributeDef.SetProperties method to notify all attributes using the ThcAttributeDef that a property change had occured, and then they could call their Modified method, but this would consume more memory and complicate things for essentially an edge case.

What’s the solution?

First of all, the ThcAttributeMediator you’re using must update the state of the control in the UpdateControl method.  None of the current mediators in the framework do so.  That’s about to change.

Then you need to update the ThcAttributeDef.Properties to include or exclude the apReadOnly property and force an update of the UI control attached to the attribute.  You can do this with a call to

hcObject.ObserverIntf.NotifyOfSubjectUpdate;

OR

UIObjectBinder.GetAttributeBinding(’SomeAttribute’).Mediator.UpdateControl;

OR

since Mediators references are stored in the Tag property of each UI control, this would also work:

(TObject(edSomeControl.Tag) as ThcAttributeMediator).UpdateControl;

The last two calls will be more efficient as it will only update a single control, where the former updates all controls attached to the subject.

In addition you will have to toggle the Enabled property of the control (if applicable) since the current implementation of ThcUIObjectBinder toggles it to False for ReadOnly attributes.

Expect some major changes in the ThcUIObjectBinder and mediators soon…

Open Source Take 2

June 28th, 2010

CJH made some valid points about my last article.

I guess I really wasn’t that clear on how my story showed the benefits of Open Source.  Let me attempt to rectify that.

Open Source can mean different things.  You can read most of the VCL source, and that has numerous benefits that could not be achieved otherwise.  The VCL source is a kind of restricted open source since the EULA restricts you from doing all that much with it.  It’s still very useful in that you can find out what method to override to achieve the desired functionality, or find a more efficient algorithm.  You’re not reliant on documentation that can be grossly incomplete or incorrect.  Nothing is better at teaching you how to code, than reading good code, and if it makes it into a commercial release it should be good right?  Well perhaps not as good as it could be, because how else would you ever be able to get over an order of magnitude of a performance gain.  So point #1 is that without the ability to read the RTL/VCL source an add-in like DelphiSpeedUp would have been more difficult to craft.

I have collected many Delphi books over the years, and some of them like the “Hidden Paths of Delphi 3″ by Ray Lischner really opened up the Open Tools API.  Information collected by experimentation, talking to Borland engineers, and no doubt reverse engineering that was shared within the community helped make the IDE a platform for innovation.  Out of that information came products like CodeRush and Castalia.  Open Source projects like GExperts, CN-Pack produced add-ins that provide functionality that should be part of the commercial offering.  So point #2 is that EMB should recognize the benefits that Open Source has brought to developers who use their product, and facilitate these projects/products so they can thrive, and integrate better with the IDE or toolset.  CodeGear started down this path by making FastMM4 the default memory manager, but there is so much more than could be done.  This might involve shipping with, and being part of the native Delphi install.  This might mean having their own open source hosting service for Delphi projects.  If it makes sense for RemObject or Gurock software why not EMB?  What about dedicated people to work on open source projects and incorporate the results into the VCL?

Point #3 is that  having the VCL source code likely prompted some of the Delphi developers to read it and say “we can do better”, leading to projects like FastMM4, FastCode and KOL.  Delphi Speedup leverages the FastCode project to do it’s magic.

It would be nice if EMB would open up more areas, and accept contributions directly from the community, but I don’t see how that is possible unless they entertain a free version.  Not too many people run Darwin on their Mac, and no one will contribute code for free and allow someone else to benefit financially from it without giving back.

That was the whole point I was trying to make (albeit indirectly).  I think EMB needs to open source the compiler leveraging LLVM (look at Apple’s XCode 4), standardize and publish the language specification for all to use, and open up the IDE so tools like Castalia, and GExperts don’t have to implement their own code parser.

Borland realized some of the benefits of Open Source by releasing most of the VCL source code.  It didn’t hurt their business. Even Firebird helped increase the number of developers using the technology, and when they wanted features like SMP support and wire encryption, they turned to Interbase.  Developers are a unique audience when it comes to Open Source.  We can actually contribute in a meaningful fashion, and EMB doesn’t have the resources or time they need to hit the necessary targets.  If EMB wants to see Delphi everywhere they are going to have to compete with Apple’s XCode 4, and provide a 64 bit compiler ASAP.  I think they can use all the help they can get, and that makes Open Source more appealing than ever.

The Power of Open Source

June 28th, 2010

I have always enjoyed the speed of Delphi’s compiler.  It definitely sets Delphi apart from other native toolsets like Visual C++.  Over the last several weeks work on my latest project was grinding to a halt because for some reason the Delphi 7 compiler was taking any where from 3 to 3m22s to compile my project.

I’m sure many people out there would simply say “upgrade” to solve the problem.  That’s where it gets a little complicated.  I had evaluated Delphi 2010, and Delphi 2007 early on during project development.  The existing product version is in fact written with Delphi 2007, but I chose to use Delphi 7 instead.  Why?  In one word, CodeRush.  CodeRush is a very old product, but provides templating and refactoring support that is still beyond what is offered in the native IDE.  I’m also very comfortable with it.  The problem with “upgrading” is of course all the components used.  I have source for them, but migrating all of that, including design-time code can be rather onerous.

Besides, as a programmer, you need to find out why.  My other projects including hcOPF compile lightning fast as always.  So I contemplated what made this specific project so unusual.  Currently it only contains 64 units, uses the DevExpress grid suite (of course), the JCL/JVCL, hcOPF and a smattering of other small components.  One of the major differences was using the JCL/JVCL, and I know that it sucks in quite a large number of units the way it’s organized, but I still didn’t think that could be the cause.

I finally decided to post on the embarcadero newsgroups.  The Delphi community is still pretty vibrant, and there are lots of folks out there who have been using the product from day 1.  I got several suggestions including:

Put only .dcu, .dfm, .res, .inc file in the LIBRARY PATH, and .pas files only in the Browsing Path.

This did seem to help, but compilations were still about 3 minutes.

Then Marius suggested I look at Delphi SpeedUp.  I had ran across it earlier in my googling for performance improvements, but never considered it a solution to my problem because my performance problem was project specific.   Was I ever wrong!  Compilation times went from over 3 minutes to 11 seconds.

So this is the second time I must thank Andreas Hausladen; once for updating the JCL/JVCL to use the LIBSUFFIX directive to make it easier to manage my hcOPF packages for different Delphi versions, and now for Delphi SpeedUp.  Thanks also to the contributors to the FastCode project.

This just goes to show what Open Source can do for commercial products.  I think Embarcadero could leverage Open Source more effectively to build out the VCL, and improve the IDE.  Even the compiler could benefit from Open Source projects like LLVM.  Apple seems to have prospered by leveraging Open Source…

hcOPF - Using DataSets for Display Purposes

June 22nd, 2010

As I’ve already mentioned, I don’t think any good OPF should force the developer to choose to implement their entire UI using objects.  Sometimes it’s easier and requires less CPU time to simply use a dataset.

Today I found myself in just that situation.  I needed to add amounts owed on account for a particular reason in a dialog.  The dialog also displayed all previous amounts owed for the same reason in a read only grid.  I didn’t want to load a list of amounts owed, and then have to filter it to those owed for the reason in question, or load a separate list at some point for all amounts owed regardless of the reason.  This would have consumed more memory and CPU resources than necessary, and the benefit of the OPF is the ability to bind to the UI controls, validate the object, and persist it with minimal effort in a database independent fashion.  An added benefit of using an OPF is that because all updates & inserts are done through the objects, you don’t need to refresh them from the database unless they could become stale.  If you’re not going to update the data, there is little point in using an object list.

The challenge is that I still want to be database independant so I use the IhcQuery interface rather than creating an instance of a query for the DAL I am employing directly.  So I needed a way to attach a dataset I loaded using a SQL statement through the IhcQuery interface to a DataSource which only accepts a TDataset class.  Since Delphi 7 doesn’t provide the ability to cast an interface to the object that implements it, I did a quick google to find this.  The Delphi Magazine was a great read.  Thankfully I bought the CDs and there is still a lot of the material on the Net.  Thanks to Hallvard VassBotn, this code is now part of hcInterfaceUtils.

Wouldn’t it be Nice?

June 18th, 2010

I tried to get together with Nick Hodges to discuss some editor enhancements I would like to see in Delphi while I was in San Fran for WWDC2010.  Most of these features are based on my experience with CodeRush (yes I’m an addict).  Unfortunately, the timing didn’t work out this time, but it got me started looking for suggestions, and once you’re in the mindset, they’re endless.

For instance, have you ever added new units without adding them to a project file and got the message:

[DCC Warning] HengenOPFCore.dpk(101): W1033 Unit ‘hcFloatAttribute’ implicitly imported into package ‘HengenOPFCore’

Wouldn’t it be nice to be able to Rt. Click on the message and choose ‘Add to Project’?

I am making a list in case Nick and I can hookup sometime in the future.  What Editor enhancements would you like to see in the base product?

If Imitation is the sincerest form of flattery, what is copying?

June 17th, 2010

I ran across this post today and was outraged that it’s exactly what I posted here two days earlier.  I like that it’s on another Delphi blog, but why copy my content?  Looks like the “author” may be pilfering other people’s posts on Delphi subjects as well….so beware.

When DataSets are Painful

June 4th, 2010

Today I was working on a legacy product that required new functionality and came across a use case where an OPF was a far easier solution than using TDataSet descendants.  The current product was implemented using ADO datasets and the new one, using hcOPF.  I needed to add the same functionality to both versions with minimal changes.

I decided to use datasets because the current product needed to be deployed before the one in development, and it used datasets.  The form I needed to implement contained a grid where users could toggle a single boolean field - a simple UI.  I thought there was no reason to add the overhead of hcOPF into the mix.  I was wrong!

The first wrinkle was that the data came from a SQL Server view and the column that was to be updated  belonged to a table added to the view by an outer join.  So an actual record in the source table may not exist.  While the view was updateable in SQL Server, ADO considered the TField ReadOnly.  I thought I would add a calculated field to the dataset, update the calculated field in the cxGrid, and use that value to call a StoredProc to update the underlying database column (it handles the problem whether the record exists or not).  Unfortunately, calculated fields are considered ReadOnly as well.  Using the OnCalcFields event was also problematic because I had to initialize the calculated field to the value of the column in the view to start with.

The solution was to use define a new ThcObjectList class with a custom Load method, and use a query to load the object list.  I also implemented a custom Write method in the ThcObject descendant used in the objectlist that checked if the original source column was Null.  If the source column was Null I knew I needed to insert a record into the source table, and if not, update the table with the value of the boolean attribute.

I’ve heard developers say they don’t like ORMs or OPFs because you have to learn how they work in order to leverage them effectively and there is always some scenario where the OPF boxes you in.  I would agree that there is a learning curve as with any component suite or library, but I don’t agree that an OPF limits you in any way.  You should be able to use a mix of an OPF with datasets, choosing whatever approach makes more sense.  It’s been my experience that an OPF provides a lot of necessary and valuable functionality that can shorten up front development time, and most importantly reduce maintenance over the lifetime of the software.

Maybe they were just using the wrong OPF…

XPlatform Object Pascal

May 23rd, 2010

I’ve followed FreePascal for some time now.  FreePascal has always been interesting in that their compiler architecture has supported multiple CPU targets for a long time, yet only now is Embarcadero working on such support.  Unfortunately, it looks like Embarcadero is writing their own from scratch rather than leveraging FPC, so a free, open source, and fully compatible compiler is not going to be available any time soon.

FPC supports multiple compatibility modes, and fpc libraries use similar if not the same names as library units from Delphi.  You would think porting a Delphi application to use FPC in order to get 64 bit Windows support or porting an application to Mac OS/X or Linux would be trivial.

The sad truth of the matter is that the moment if you want to port a Delphi app from Windows to a different OS you have to deal with UI toolkit differences and even on Windows porting to FPC can be a challenge because of the limited UI components and compiler compatibility.  That’s slowly changing with visual component frameworks like fpGUI, wxForms, and VGScene (assuming you used their framework to begin with).  Other components are being supplemented by a growing community via the Lazarus CCR, and vendors like REMObjects, and BRAIN PATCHWORK DX, LLC (the maker of DXSocks).  Lots of open source projects also support FPC, and the number seems to be growing.  If you’ve ever questioned the need for a cross platform Object Pascal compiler I think the community has already given it’s answer.

Tonight I tried to compile hcOPF using the latest version of FPC (2.4) on Mac OS/X.  I used the instructions on Donald Shimoda’s blog and it was absolutely painless.  I installed the Lazarus package for Mac OS/X and fired it up.

Initially I had some problems in my hcOPF port, figuring out why FPC couldn’t find my include files.  When I found out where to plug in the include file path, I thought I was off to the races until I got a “Could not find identifier TDate” error message.  It appears that on Mac OS/X FPC has a Controls unit, and so does Lazarus, but the FPC Unit is first in the path.  Once I got past that error, I discovered that the DBConsts unit in Delphi is named DBConst in FPC and it’s missing a number of resource strings.  After adding a whole bunch of IFDEFs, I hit a show stopper.  FPC doesn’t support the implements feature of the Delphi compiler to delegate the implementation of an interface to another object.  I use this in ThcObject to implement the IObserver interface.  This is a well known problem as documented here.  Unfortunately until this issue is solved, some of the best libraries will not be available to FPC developers.  I mean of course, the JVCL  ;>)

Inheriting Problems

May 5th, 2010

Recently I was working on a project in which I dropped a ThcUIObjectBinder component from the hcOPF framework onto a TFrame.  I put the frame onto a TForm container and kept getting a dialog in Delphi complaining that a ‘ThcUIBindingItem cannot be assigned to a ThcUIBindingItem’.  The error message doesn’t make much sense, but I had seen similar dialogs in the past, so I figured it came from the Assign() method.  I had not implemented an override in my ThcUIBindingItem class so it was using the default in TPersistent which generates the error dialog I had witnessed.

I checked out the DFM file for my form instance, and found that the bindings were empty, yet in the frame, I had two binding items.  This was also consistent with my supposition that it had to do with the Assign method.  When a component on a DFM effectively inherits it’s properties from a component in another DFM, it makes sense that the component is Assigned the properties of the “ancestor”  and then these properties are over written or augmented by the property values in the “descendant” DFM.  Of course the problem is that Assign() is virtual so you have no idea that the method must be overridden in some circumstances to get the behaviour you would expect because it isn’t documented in the help.

Sure enough, I added my own Assign() override and all is well.

hcOPF - Now Supports Design-Time DevExpress Grid Bindings

March 22nd, 2010

hcOPF is continually being improved as I use it on different projects, and other contributors send me code.  I posted a while ago on the design-time binding for attributes that I added.  Since then, I’ve had a requirement that the current DevExpress grid bindings could not satisfy.

The client required the same grid to be shown on 2 different forms in the application.  You wouldn’t think this would be a problem, but the grid bindings are a global registry keyed on the ObjectClass and ListClass passed to the BindViewToList() method.  You could certainly bind more than one grid to the same list, but when the ThcGridObjectList (or any other cxGrid aware list) instance attempted to locate the TcxGridTableView to which it was bound, it would only find the first view binding registered.  After contemplating extending the grid binding registry key employed to identify the binding, I decided rather than trying to hack a solution for now, it would be best to implement design-time binding.

Ultimately, I would have liked to enabled design-time data binding capabilities to the DevExpress component editor so developers could select an object attribute in the data binding the same way they select a database field in a data bound view.  I’m not even sure if that approach is feasible, and it certainly would not have been consistent with the design-time approach for ThcObject attributes.  Sometimes coding is as much an exploration as a discipline, but I needed to limit the possibility of a throw away design both because I needed to get on with the project work, and design-time coding can be very time consuming since you just can’t step through it in the debugger.  CodeSite is a very useful tool, but you still have to be looking in the right place and sending sufficient information to figure out what is going on.

What I decided to implement was a new ThcMediator descendant that would mediate between a ThcObjectList or descendant and a TcxGridTableView.  Currently, only table views are supported, but it shouldn’t take much to support other view types as well.  I re-factored the ThcMediator, creating an abstract class; ThcMediator and two descendants; ThcAttributeMediator and ThcListMediator.  Then I created a new ThcListMediator descendant (ThcTcxGridMediator) to mediate between a ThcObjectList and the selected cxGrid view.  The ThcObjectList is identified at design-time by it’s Name, which is then used at run-time to attach to the list.  Here is a screenshot depicting how you bind the list to the gridview:

The TcxGridViewMediator manages a ThcDevExpressItemBindingCollection in which you can specify the Item (normally a grid column) to object attribute pairing as shown below:

The downside is that the grid view column must first be created in the cxGrid component editor, before it can be paired with the AttributeName.

This functionality is still considered “in development” as it needs to be tested more thoroughly, but I thought there might be a few interested parties who would want to know of it sooner rather than later.  It certainly beats writing code to bind a list to a grid view, and it eliminates the need to conditionally compile any design-time business objects in order to install them into the IDE without sucking in all sorts of DevExpress code.

Check out the ObjectListMgrDemo application to see it in action (only D2010 and D7 packages are currently up to date).