Embarcadero’s Newest Dog Food

January 18th, 2012

I’ve been a developer now for 19 years….wow!  A lot has changed over that time, but one constant has always been  that software companies who use their own products internally produce better software.  It’s called DogFooding in the industry.

When .NET first came out, a lot of developers were opposed to writing .NET code for performance reasons including load time, as well as the size of the executables, and the speed of the code.  One of the arguments against using .NET was that none of Microsoft’s own products were written in .NET.  Of course a lot of this had to do with the fact that their products were large legacy codebases and .NET gradually crept into the mix.

When WPF first came out a lot of developers thought it was interesting technology, but no one was using it, and the GPU overhead required newer video cards.  WPF usage gradually increased as Microsoft frameworks leveraged it, and then Expression Blend, and Visual Studio 2010 shipped showing that Microsoft was dogfooding their own technology.

The thought occurred to me today, that having to use FPC with XCode for the Mac can’t be a long term solution.  EMB has said they will be shipping their own compiler for the Mac, but what about the IDE?  Any conditional compilation will eventually require code editing in XCode which doesn’t have code completion for Pascal and all the other features of the Delphi IDE.  In addition, at some point requiring two machines (or a VM) when you’re targeting one platform will just become a pain.

I wonder if EMB will follow Microsoft’s example, and use their own UI framework that would have the added advantage of enabling EMB to make the IDE X platform.  That would be the ultimate showcase for their compiler and FireMonkey.

Why DealFind isn’t such a Find

December 25th, 2011

A friend of mine bought a Dealfind voucher for rug cleaning from MarkItClean after staying with me for a while. He purchased the voucher directly from DealFind and forwarded it to me.

After emailing MarkItClean in August, I got busy and didn’t follow up for a while. When I finally got time to chase MarkItClean down I called their number and left a message. A week or so later I called again and got told their voicemail was full. I contacted Dealfind saying the vendor was unresponsive and I couldn’t even leave another message. They gave me an alternate number. I called that and again got voicemail. I left a message, and as expected they did not get back to me in the 3-5 business days they indicated.

In December, I contacted Dealfind again, and asked for a refund. Natalie, asked for the same information I had already supplied in the Dealfind contact form, saying they would contact the vendor on my behalf. I told her that after 6 months of trying to redeem this voucher I wasn’t about to wait any longer, and that Dealfind should have intervened on my behalf the first time I spoke to them. Natalie wasn’t in so Quiin took over, and led me to believe that I would get credited with Dealfind dollars in my account since my friend had paid for it on his credit card. This in fact, was not correct. My friend got credited with Dealfind dollars (he didn’t even get the choice), since he was the original purchaser of the voucher.

At this point I got frustrated and asked to speak to a supervisor, and was transferred to Natalie. She seemed to understand my perspective but claimed there was nothing they could do. Their accounting system and department would not allow them to credit anyone other than the original purchaser, even if they contacted the original purchaser and got permission/confirmation that the voucher was intended for me, and that any credit should also be given to me. I found this unacceptable, and I told her so. I told her that my circumstances were not unique, and that they were leaving their customers unprotected.

Dealfind’s compliance department did not follow up after initial complaints to ensure the vendor provided the service, and they didn’t offer any kind of refund until I refused to accept anything else. The refund + 10% wouldn’t have covered the time I spent chasing Dealfind and MarkItClean down trying to get the service. After a long conversation Natalie said she would talk to her accounting department and follow up with a few other people and get back to me the following day. She did not. Quinn called me the next day, to offer to contact my friend to let him know the vendor did not render the service. Again, a waste of my time at work and of my cell air time. If you cannot count on a manager in a company to do what they undertake, I wouldn’t bet you can rely on the company.

The problem is that Dealfind vouchers are completely transferrable, but there is no protection for the recipient of the voucher in the event that the vendor does not render the product or service as advertised. If you buy someone a voucher as a gift, beware that you may be giving the gift of frustration. Dealfind has rigid business processes that no one there seems willing or able to modify even if it benefits their customers, and ultimately their business.

The TJvWizard

December 16th, 2011

The TJvWizard makes implementing an application wizard about as simple as it can be, but it suffers from the same problem as a lot of the Jedi library components; a lack of documentation.  There are a lot of hidden Gems in the JVCL if you have the time to find them, and figure out how to use them.  You always have to have it in the back of your mind that a commercial component may be a better solution, both in terms of support and functionality that meets your requirements.

The TJvWizard is not part of the MegaDemo, and the on-line documentation is sparse.  Most of it appears to have been generated, and there is no general usage suggestions.  I’m not a big fan of the JEDI help system, preferring the EMB on-line class centric help format. The format matters little though without the necessary content.

With a plethora of events exposed, it’s challenging to decide what one to use for what purpose.  I thought I would share my findings in the hope that I might make it easier for others to use this component, and because I might forget at some point ;-)

The OnXXXButttonClick events of the TJvWizardInteriorPage class have a Stop boolean parameter passed by reference.  If you leave the wizard buttons enabled, this is a good event to ensure that the data collected on the wizard page is valid before proceeding.  The downside is that you need to provide some visual feedback to the user to explain why clicking on the ‘Next’ button doesn’t take them to the next step in the wizard if the data is invalid.  I use TJvValidators to show error indicators beside UI elements with invalid data.

You can of course disable buttons when entering a page and dynamically update the buttons when OnChange events occur in other controls triggering validations.  The problem with this approach is that if the Back button is used those change events won’t be triggered and the buttons may not be updated appropriately.  Testing all possible navigation pathways for appropriate behaviour is a must.

I use the TJvWizardInteriorPage.OnExitPage event to collect any data from the UI and populate my domain layer, or other structures if I am not relying on the current value of the UI controls.

The TJvWizardInteriorPage.OnEnterPage event is a good place to initialize controls whose content is dependant on the previous wizard page.  The OnPage event may be even better, but I haven’t tried it.  Both the OnEnterPage and OnExitPage events have a FromPage :TJvWizardCustomPage parameter so you can use the transition context to decide if you want to initialize your controls, or leave them be if the Back button has been used.

Finally I use the TJvWizard.OnSelectNextPage and OnSelectPriorPage events to control page transitions, both forward and backward.  My wizards don’t have overly complicated routes.  I have not used the WizardRouteMap components to date.

IMHO the TjvWizard is another JEDI gem, but if you find it hard to use because of a lack of documentation, or are concerned since the last updates appear to be in 2003, there are other choices out there such as the delphiwizardframework.

TADOStoredProc Parameter Returns wrong Value

December 9th, 2011

The other day I was trying to make a stored procedure call using hcOPF and kept getting a ‘parameter object is improperly defined’ error.  It has to be the most frustrating ADO error I’ve ever encountered, and I’ve come across a few in my time.  What makes it so frustrating is that the error message gives no indication of what parameter is the source of the problem.

As a developer, I would recommend you be as specific as possible when raising exceptions.  Imagine a stored proc taking 30 parameters, and the time it would take to figure out all the possible reasons why any one of them might cause this error when you have absolutely no idea why an API call you have made a thousand times before without a problem, now fails.

What I ended up doing is making the call manually in SQL Management Studio (SMS) to verify the back end was functioning,  Then I wrote a test application in Delphi that simply made the same call using a TADOStoredProc component with the same parameters I used in SMS.  The call succeeded, but one of the input/output values was not what I expected.  After experimenting at length, and discussing this situation with a co-worker, I discovered that the input/output parameter in question was a string defined as a varchar(max) in SQL Server, and it was being truncated at the length of the string I initially populated it with as an Input.  Once I verified this hypothesis, I filed a QC report (101665).

I probably should also have filed a report to fix the generic exception I encountered initially but I had already spent enough time isolating and filing the first bug report.  At some point you have to actually get some work done instead of helping fix someone else’s bugs ;-)

hcOPF - WYSIWYG with the DevExpress Quantum Grid

November 15th, 2011

When using the DevExpress grid with ObjectLists, there is currently a limitation that you must assign a root object to the hcUIObjectBinder.BoundObject property and this root object needs to expose the ThcParentedObjectList to present in the grid as a child of the root object that exists when the root object is constructed at design-time.  It may be empty, but it must be present, and provide a ListName in order for the object inspector to provide it as a discernible option.  Hopefully sometime in the near future I will have time to make it possible to bind to a standalone object list.  Here is a code snippet that I use in the AfterConstruction override:

  FLoggedInUsers := TftEmployeeList.Create(Self);
  FLoggedInUsers.Name := 'LoggedInUsers';
  AddChild(FLoggedInUsers);

While the ThcObjectDataSource does the majority of the heavy lifting for you in terms of presenting objectlists in the Developer Express Quantum Grid, there are times in which you need to programmatically modify the domain objects and inform the grid to update itself.  This can be achieved by calling:

GridTableView.DataController.CustomDatasource.DataChanged;

or the DataChanged() method of the TcxGridMediator.

In order to ensure the domain objects are updated immediately, I always set the ImmediatePost property of all grid editors to True.

Another thing to look out for are changes that are pending in the grid that have not been committed to the underlying objects.  For this I use the following code on the TAction.OnExecute event handler in my forms:

  if GridTableView.DataController.IsEditing then
    GridTableView.DataController.Post(True);

Of course you still have to persist the data if you’re using ThcParentedObjectLists that use CachedUpdates.  By default lists do not use CachedUpdates, so the grid Posts the data when you scroll to a different row, and the object layer writes the object data to the database.  The code above also results in a database update for for the current object in such lists.

If you use these tips, using hcOPF objects with the DevExpress Quantum Grid can actually be easier in some cases than a dataset.

hcOPF Object Bindings

October 22nd, 2011

Binding an object graph poses a few challenges.  Namely that an object may have children including lists, and if a parent object is refreshed, the child lists may have a different number of items and each item is a different object instance.

hcOPF uses an MGM pattern supporting design-time bindings.  The core component in this mechanism is the ThcUIObjectBinder.  At design-time the developer populates a collection of ThcUIBindingItems that specify the MediatorClass, and the bound Control.  Additional properties may be specified that are dependant on the class of the mediator, such as the attribute name if the mediator is a ThcAttributeMediator descendant.

At run-time, when the ThcUIObjectBinder has been streamed in from the DFM, it’s Loaded method gets called.  The binder checks that all bindings are correct by calling the Mediator’s IsValid method.  If one or more bindings are invalid, an error message will be displayed and the program terminated.  This prevents scenarios like in typical Delphi apps where a field name changes and nothing shows up in the db-aware controls.  No error is raised, so the developer has no idea that the form is not working as expected unless they happen to notice the absence of display item.

Immediately after the Mediator’s Loaded method, the Initialize method is called.  While this method is not currently used in hcOPF I felt it might be of some valid for initializing controls using values from the database, and that is why it takes a FactoryPool argument.

The Mediator’s Bind method is then called.  This method is used by ThcObjectLists and descendants who bind to their controls using a complex mechanism.  Simple attribute binding is accomplished by pairing the control to the attribute in the mediator which is accomplished by the streaming system, followed by setting the mediator’s Subject and calling Mediator.Bind() which is done by the ThcUIObjectBinder.

Now that I’ve spent a great deal of time on Object Binding, EMB has introduced LiveBindings.  After I get some time to evaluate that technology I might add support to hcOPF for LiveBindings.  Time to listen to the CodeRage presentations…

Shakespeare was Wrong? (Warning May be Offensive)

October 21st, 2011

What is in a name?  Apparently quite a bit, even for an open source ORM framework.  In a global world it appears Shakespeare’s words no longer ring quite as true.  Perhaps I should have googled hcOPF before now because I might have considered a different name, because hcOPF is already known as something a little different ;-)

I hope it’s my acronym that Delphi Developers think of first…

TcxEdit Validation

October 21st, 2011

If you assign the Min and Max Value properties of a TcxEdit descendant, the control will call the OnValidate event handler indicating there is no error, but a subsequent attempt to convert the displayvalue into the target datatype and check if the result is within the range specified will result in an exception being raised. If you’re using something like Eurekalog or MadExcept to trap all unhandled exceptions this can be a major pain in the derrier.

There are only two ways I’ve found to deal with this issue:

1) Tell MadExcept or Eurekalog not to process this exception type

2) Do not define the Min and Max values in the control. Instead let the hcOPF validator deal with this, and I populate the OnValidate event with the following code to prevent an earlier exception from being raised (this code is for a cxDateTimeEdit):

if Error then
begin
  MessageDlg('The Date Entered is not Valid!',mtWarning,[mbOk],0);
  DisplayValue := DateTimeToStr(Now);  //reset the display to a valid value
  dtpBirthDate.SetFocus;
  Error := False;  //don't raise an exception since we've handled the error
end;

How Can Rendering a project Uncompilable be ‘As Designed’?

October 19th, 2011

I’m sure everyone out there has found themselves modifying the DPR source at some point to add some conditional logic for a clean shutdown for instance if a user cancels out of a Login form, or to add conditional directives to account for differences in the Delphi compiler or VCL.

This happened to me most recently when upgrading from Delphi 2007 to Delphi 2010.  The unit generated from the WSDL of a Web Service I am calling changed with DXE so I added an {$IFDEF} to control which unit was compiled.  Then I added a new unit to my project, and suddenly, DXE would no longer compile my project.  It seems it re-wrote the uses list, overwriting my {$IFDEF} block and was trying to use the D2007 version of the web service code.  This kind of thing has been a problem for as long as I remember, which is back to D7 at least, so I submitted a bug report hoping this might finally be fixed.  Instead, the ticket (96783) was closed with ‘As Designed’.

I’ll admit, I’m a simple country boy who after living in the city for 20 years is still a bit naive.  I consider Delphi to be a testament to good design and for the most part implementation.  It’s longevity in an ever changing IT world is a testament to this IMHO.  I have a hard time imagining Anders Hejlsberg and the other original team members thinking to themselves that it was okay to wipe out developer written code and make a Delphi project uncompilable.  If this was the only example I could provide it wouldn’t be as concerning, but there is also QC #99381 which at least didn’t get dismissed with ‘As Designed’. Can someone explain this to a naive country boy?

Delphi XE2 - Bug Free?

September 6th, 2011

If you are using Delphi XE2, and have found a bug, you might be surprised to find there is no where to report it.  The Quality Central Windows Client doesn’t list XE2 as a selectable version.  I noticed this the day XE2 was released, but thought it would be corrected shortly.  Apparently, EMB is very confident that such a major release has no issues, or they have overlooked QC updates in their release process.

Also interestingly enough, XE Update 1 (v15.1) is in the list even though I am running 15.0 and checking for updates reports ‘No updates available’.  I assume an update to XE will soon be released.