Sorry, just questions, no answers

Lately it has been happening more and more often.

Someone writes a blog post articulating an idea that has been stewing in my mind for some time…

Now Uncle Bob has done it.

Over the years it has never ceased to amaze me that software developers are not held to higher standards.

How is it that we find it acceptable to deliver products that contain an unknown number of unknown bugs?

I can’t think of any other product where people wouldn’t be outraged if it contained the number of flaws that we seem to accept as “inevitable”.

How is it that we have found and established all manner of best practices, yet we face all kinds of resistance in applying them? And not just from “management”, but from our own rank and file as well?

We like to call ourselves professionals, yet we allow ourselves to be bullied or cajoled into delivering substandard work to please the short term goals of whoever pays us. Often we even seduce ourselves into doing that. We seem to find the immediate fix of getting stuff of our plate a lot more gratifying than doing it right and keeping that stuff from getting back on our plate.

Uncle Bob sees us as “a disorganized band of rumpled hapless vagabonds” and wonders “Will we continue on our undisciplined course, blown by the chaotic winds of business and government, until one of us finally blunders badly enough to wake the sleeping giant of government regulation?”

The medical professions, the airline industry, merchant marines, construction industry, accountants, lawyers, and many others all have professional organizations, codes of conduct and ethics, minimum standards etc to sift the wheat from the chaff. And to allow clients some form of redress when someone managed to swim through a loophole in the profession’s safety net.

How is it that nobody has forced something similar upon us yet?

Is Uncle Bob right in warning us? In encouraging us to band together and to get organized before people who don’t understand the first thing about software development feel the need to forcibly organize us?

What would it take for people to get outraged enough to force us to work according to well-known best practices and make us sue-able for malpractice if we don’t?

Do we want to find out?

Is Uncle Bob’s “The Obligation of the Programmer” enough to rid our profession of unprofessional individuals?

Or are we too much of a disorganized band of rumpled hapless vagabonds to do it ourselves?

Do we need an independent association that oversees the professionality and conduct of its members?

Will that be enough?

Or do we need the protection of a law stipulating that one cannot call oneself “software developer” unless one passes the association’s exams and conducts business according to the association’s code of conduct and ethics?

Uncle Bob’s “The Obligation of the Programmer” is a start. A professional code of conduct may give well intentioned developers some backup against the pressures to and the lures of going faster than we should.

My nascent optimist nature hopes we can convince people only to do business with developers that have taken such an oath. And that naming and shaming of those that don’t live up to it would be enough to drive them out of the profession.

My cynical nature says “yeah right”. History has shown that it takes associations, safety regulations, building codes, bar exams, disciplinary committees and whatever else we have come up with, to keep businesses and individual practitioners “honest”.

I hope my nascent optimism wins.

Posted in Software Development
Tags:

How do I test an interface? Should I even do that?

Every so often this comes up: I have an interface. It has methods. I should unit test everything. So I should test this interface. But how? It doesn’t have an implementation…?!

Right…

Well…

Hmm…

No. Of course you don’t test the interface. You can’t. It has no implementation.

But…

You do want to test each and every class that implements this interface.

To check that any class that implements the interface meets the expectations of the clients of that interface.

Okay…

So how do you go about that?

After all, you want to keep your tests DRY. So you certainly don’t want to write all the tests with regard to the interface all over again for each new implementer of the interface.

It depends…

Yeah I know. Sounds like a cop out. But in this case it really depends on the unit test framework you use and what it enables you to do.

What holds true regardless of which language and framework you use is that you create a single test class to hold all the tests that you want to run for all implementers of your interface. And you code all the methods in that test class using a reference to the interface (instead of a reference to a specific class).

Sounds a bit complicated, but it is fairly straightforward.

Say you have an interface ISomeInterface with a single method SomeMethod.

  ISomeInterface = interface(IInterface)
    function SomeMethod(const aInput: string): Boolean;
  end;

And you want to ensure that any implementer of ISomeInterface returns false when it is passed an empty string. Somewhat like:

  CheckEquals(FInterfaceReference.SomeMethod(''), False);

NUnit

Using C# and NUnit you would just write a single generic class, give it the test methods you need to ensure implementers meet the desired behavior and then decorate it with [TestFixture(typeof(SomeImplementation))] attributes. One attribute for each implementing class. That’s all.

[TestFixture(typeof(MyImplementerOfSomeInterface))] 
[TestFixture(typeof(MyOtherImplementerOfSomeInterface))] 
public class TestsOfISomeInterface<T> where T : ISomeInterface, new() 
{ 
    ISomeInterface impl; 

    [SetUp] 
    public void CreateISomeInterfaceImplementation() 
    { 
        impl = new T(); 
    } 

    // Use impl in your tests 
} 

DUnitX

In Delphi with DUnitX I expect you could do much the same thing as DUnitX is modelled on NUnit.

(I haven’t had the pleasure of using DUnitX yet. Please chime in with a comment if you know for sure.)

DUnit on generics

If you are using a Delphi version which supports generics, you can do much the same thing using DUnit. You start by declaring a generic test class for the tests on implementers of ISomeInterface.

type
  TSomeInterfaceContractTests<T: TInterfacedObject, constructor> = class(TTestCase)
  strict private
    FInterfaceUnderTest: ISomeInterface;
  protected
    procedure SetUp; override;
  published
    procedure SomeMethod_Input_EmptyString_ShouldReturn_False;
  end;

procedure TSomeInterfaceContractTests<T>.SetUp;
begin
  inherited;
  Supports(T.Create, ISomeInterface, FInterfaceUnderTest);
  Assert(Assigned(FInterfaceUnderTest), 'Generic type does not support ISomeInterface');
end;

procedure TSomeInterfaceContractTests<T>.SomeMethod_Input_EmptyString_ShouldReturn_False;
begin
  CheckEquals(FInterfaceUnderTest.SomeMethod(''), False);
end;

I am sure you could do some clever magic using extended RTTI and put attributes on this class to register specific test classes for your classes that implement ISomeInterface. But in that case you are probably better off switching from DUnit to DUnitX. Besides which it is hardly any more trouble to register your implementing classes to be tested without using attributes. All you need do is:

  RegisterTest(TSomeInterfaceContractTests<TMyImplementerOfSomeInterface>.Suite);
  RegisterTest(TSomeInterfaceContractTests<TMyOtherImplementerOfSomeInterface>.Suite);

DUnit straight up

If you are using a Delphi version which does not support generics, then you need to do a bit more work yourself, but the basics are the same.

You create a “contract” test case class that contains the test methods to ensure an implementer of ISomeInterface meets the expected behavior.

type
  TSomeInterfaceContractTests = class(TTestCase)
  strict private
    FInterfaceUnderTest: ISomeInterface;
  published
    procedure SomeMethod_Input_EmptyString_ShouldReturn_False;
  end;

TSomeInterfaceContractTests.SomeMethod_Input_EmptyString_ShouldReturn_False;
begin
  CheckEquals(FInterfaceUnderTest.SomeMethod(''), False);
end;

To instantiate the proper class for your test methods you have several options. The one I like best uses the beauty of Delphi’s meta classes. For this you add a virtual abstract method to your contract test case.

type
  TSomeInterfaceContractTests = class(TTestCase)
  strict protected
    function GetImplementingClass: TInterfacedClass; virtual; abstract;

And use this in your SetUp override or in some other method you use to instantiate the class under test:

TSomeInterfaceContractTests.SetUp;
begin
  inherited;
  Supports(GetImplementingClass.Create, ISomeInterface, FInterfaceUnderTest);
  Assert(Assigned(FInterfaceUnderTest), 'Implementing class does not support ISomeInterface');
end;

Derived test classes must then of course override the GetImplementingClass method and return the class of the implementing class they are intended to exercise.

type
  TMyImplemterOfSomeInterfaceTest = class(TSomeInterfaceContractTests)
  strict protected
    function GetImplementingClass: TInterfacedClass; override;

TMyImplemterOfSomeInterfaceTest.GetImplementingClass: TInterfacedClass;
begin
  Result := TMyImplementerOfSomeInterface;
end;

Now all that is left to do is to register your derived test case(s) so that the test runner will execute all the test methods declared in the contract tests.

  RegisterTest(TMyImplemterOfSomeInterfaceTest.Suite);

And when you have another class implementing the ISomeInterface interface. All you need to do to have it tested for its implementation of that interface is:
– derive a test case class for that specific class from TSomeInterfaceContractTest
– override the GetImplementingClass method
– register the test case with DUnit.

Posted in Software Development
Tags: , , , , , , ,

TL;DR version of Name Value Pairs in ComboBoxes and Kinfolk

Did you read Name Value Pairs in ComboBoxes and Kinfolk?

Too long?

Yeah. You’re right. I realized that after I published it.

Here’s the TL;DR version of it.


You have been tasked with adding a simple ComboBox to a form and load its list of values from a configuration file containing a list of name value pairs.

Getting the name value pairs from the file into a string list is a “been there, done that” breeze kind of thing. Getting a ComboBox to display the contents should be just as simple. It takes a TStrings for its Items property after all.

begin
  // Simulate loading from some configuration file
  FMyNameValuePairs := TStringList.Create;
  FMyNameValuePairs.Add('Jansen=100');
  FMyNameValuePairs.Add('Petersen=200');
  FMyNameValuePairs.Add('Gerritsen=300');
  FMyNameValuePairs.Add('Dirksen=400');
  FMyNameValuePairs.Add('Karelsen=500');

  // Assign the "configuration" contents of the string list to the ComboBox
  ComboBox1.Items := FMyNameValuePairs;
end;

Yay! That’s another item of your todo list.

ComboBox naively filled with name value pairs

Oops?!

Why are those numbers showing? They are not supposed to be there. Doesn’t a ComboBox know to use only the names when its Items holds a list of name value pairs?

In one word? No.

In more words? No, it doesn’t. The DrawItems method of TCustomComboBox simply draws the entire contents of its items:

FCanvas.TextOut(Rect.Left + 2, Rect.Top, Items[Index]);

Bugger

So how do you get around this unfortunate limitation?

StringList gold

Something very well known to Delphi “old hands”, but which comes as a surprise to many less experienced Delphi developers is that a TStringList is not “just” a list of strings. It actually is a list of records that hold both a string and an object reference.

To make that little bit of gold work to your advantage, all you need to do is

  1. store the value of your items in some object instance, and
  2. remove the name-value separator and the value from the items in your StringList

before you assign your StringList to the Items property of a ComboBox, or any other control that handles StringLists.

To store the values of your names in a StringList’s Objects, you need a class that can at least hold a string value. String because when you read configuration files into a StringList, the values come in as strings. Any conversion to the actual type of the values can be done later or elsewhere.

That said, when your Delphi version has extended RTTI or generics, why not put it to good use?

For my TValueObject class I have opted to use the TValue record that comes with extended RTTI. You could also use generics, but then you would have to code all the “To String” conversions yourself. I prefer to leverage the built-in conversions of the TValue record.

type
  TValueObject = class(TObject)
  strict private
    FValue: TValue;
  public
    constructor Create(const aValue: TValue);
    property Value: TValue read FValue;
  end;
  
  { TValueObject }

constructor TValueObject.Create(const aValue: TValue);
begin
  FValue := aValue;
end;

Storing the value of each name and stripping everything but the name part from the items in your StringList is pretty simple:

  // Convert the contents so both the ComboBox and Memo can show just the names
  // and the values are still associated with their items using actual object
  // instances.
  for idx := 0 to FMyNameValuePairs.Count - 1 do
  begin
    FMyNameValuePairs.Objects[idx] := 
      TValueObject.Create(FMyNameValuePairs.ValueFromIndex[idx]);

    FMyNameValuePairs.Strings[idx] := FMyNameValuePairs.Names[idx];
  end;

Getting the name and value for an item is just as easy:

begin
  Name_Text.Caption := FMyNameValuePairs.Items[idx];
  Value_Text.Caption := TValueObject(FMyNameValuePairs.Objects[idx]).Value.AsString;
end;

When you now set your ComboBox’ items to your StringList, the ComboBox will only show your names but it will also have all the names’ values available.

Bear in mind that setting your ComboBox’ items to your StringList does an Assign – a shallow copy – so you have to ensure that the ComboBox can no longer access its items’ values before you free your StringList.

Oh and of course you want to instantiate your StringList setting its OwnsObject property to True so that when you free it, all instances stored in its Objects are freed automatically.

Putting it all together to show just the names in your ComboBox and still have access to each name’s value:

procedure TGold_Form.ComboBox1Select(Sender: TObject);
begin
  Name_Text.Caption := ComboBox1.Items[ComboBox1.ItemIndex];
  Value_Text.Caption := 
    TValueObject(ComboBox1.Items.Objects[ComboBox1.ItemIndex]).Value.AsString;
end;

procedure TGold_Form.FormCreate(Sender: TObject);
var
  idx: Integer;
begin
  // Simulate loading from some configuration file
  // We set OwnsObjects to True so that any object instances we add will be
  // freed automatically when we free the string list.
  FMyNameValuePairs := TStringList.Create({OwnsObjects}True);
  FMyNameValuePairs.Add('Jansen=Aquarius');
  FMyNameValuePairs.Add('Petersen=Capricorn');
  FMyNameValuePairs.Add('Gerritsen=Pisces');
  FMyNameValuePairs.Add('Dirksen=Libra');
  FMyNameValuePairs.Add('Karelsen=Scorpio');

  // Convert the contents so both the ComboBox and Memo can show just the names
  // and the values are still associated with their items using actual object
  // instances.
  for idx := 0 to FMyNameValuePairs.Count - 1 do
  begin
    FMyNameValuePairs.Objects[idx] :=
      TValueObject.Create(FMyNameValuePairs.ValueFromIndex[idx]);

    FMyNameValuePairs.Strings[idx] := FMyNameValuePairs.Names[idx];
  end;

  // Show the contents of the "configuration" in a memo
  Memo1.Text := FMyNameValuePairs.Text;

  // Load the "configuration" contents of the string list into the combo box
  ComboBox1.Items := FMyNameValuePairs; // Does an Assign!
end;

procedure TGold_Form.FormDestroy(Sender: TObject);
begin
  // Ensure the ComboBox can no longer reference the items in FMyNameValuePairs
  ComboBox1.Clear;

  // Free the "configuration" string list.
  // As OwnsObjects is True, we don't need to free the Objects ourselves.
  // It is done for us.
  FMyNameValuePairs.Free;
end;

ComboBox and Memo filled with names of name value pairs allowing values of any type

Enjoy!

Posted in Software Development
Tags: , , , , , , ,

Name Value Pairs in ComboBoxes and Kinfolk

A couple of hours after I published this, I realized that it was very long. If you just want the punch line then check out
TL;DR version of Name Value Pairs in ComboBoxes and Kinfolk


You have been tasked with adding a simple ComboBox to a form and load its list of values from a configuration file containing a list of name value pairs. [1]

Getting the name value pairs from the file into a string list is a “been there, done that” breeze kind of thing. Getting a ComboBox to display the contents should be just as simple. It takes a TStrings for its Items property after all.

begin
  // Simulate loading from some configuration file
  FMyNameValuePairs := TStringList.Create;
  FMyNameValuePairs.Add('Jansen=100');
  FMyNameValuePairs.Add('Petersen=200');
  FMyNameValuePairs.Add('Gerritsen=300');
  FMyNameValuePairs.Add('Dirksen=400');
  FMyNameValuePairs.Add('Karelsen=500');

  // Assign the "configuration" contents of the string list to the ComboBox
  ComboBox1.Items := FMyNameValuePairs;
end;

Setting a ComboBox’s items does an assign. [2]

Yay! That’s another item of your todo list.

ComboBox naively filled with name value pairs

Oops?!

Why are those numbers showing? They are not supposed to be there. Doesn’t a ComboBox know to use only the names when its Items holds a list of name value pairs?

In one word? No.

In more words? No, it doesn’t. The DrawItems method of TCustomComboBox simply draws the entire contents of its items:

FCanvas.TextOut(Rect.Left + 2, Rect.Top, Items[Index]);

Bugger

So how do you get around this unfortunate limitation?

OwnerDraw?

One way around it is to set the Style of the ComboBox to csOwnerDrawFixed and implement the OnDrawItemevent to draw only the names of the items:

procedure TForm1.ComboBox1DrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
begin
  ComboBox1.Canvas.TextRect(Rect, Rect.Left, Rect.Top, ComboBox1.Items.Names[Index]);
end;

It works, but I don’t like it. I don’t like using events for something I will probably end up using in more places.

Custom descendant?

You could create a TCustomComboBox descendant and override the DrawItem method. It is virtual so that certainly is an option.

Still, I don’t much like it. The DrawItem method contains more than just the TextRect call and you would have to duplicate that code in order for your descendant to work as expected. Yuck. Duplicating code raises red flags in and of itself. In this case you would have to check your DrawItem implementation with each new Delphi version to ensure that it stays in line with its ancestor except for the intended difference of using Items.Names[Index] instead of Items[Index] so other code can keep treating your descendant as any TCustomComboBox.

Personally, I also have an aversion to dealing with the intricacies of graphics drawings. I’d much rather leave the graphics nitty gritty to Delphi and third party component / control vendors so I can concentrate on business functionality and more abstract presentation issues.

Stripping down to the names

An obvious solution is to strip the name-value separator and the value parts from the StringList’s strings before you assign the StringList to the ComboBox’ items.

I’d strongly advise against this. Been there, done that. Didn’t like it one little bit.

It means you have to ensure that every item is found at exactly the same index in both the StringList and the ComboBox. To do so, you have to either severely limit the experience of your users – keeping them from re-ordering, inserting and deleting the ComboBox’ items, or you have to jump through hoops to keep the two lists in sync.

But, to get the ComboBox to display just the names, you have no option but to strip the name-value separator and the value parts.

So, how do you go about this and still leaving your hoops in the attic?

TStringList to the rescue

Something which is known very well to Delphi “old hands”, but which comes as a surprise to many less experienced Delphi developers is that a TStringList is not “just” a list of strings. It actually is a list of records that hold both a string and an object reference.

Exactly what we need as it provides the means to keep our names and values together without jumping through hoops and without limiting our users in what they want to do with the list.

StringList bronze

If the values in your name value pairs always are integer numbers, you can get away with a very simple “hack”. Hack because it (ab)uses the object reference to hold the integer value (instead of an actual object reference).

procedure TBetter_Form.FormCreate(Sender: TObject);
var
  idx: Integer;
begin
  // Simulate loading from some configuration file
  // OwnsObjects = False is the default, but we state it explicitly here 
  // because we are not really storing object instances, but are (ab)using each Item's
  // Object property to hold the integer value from the configuration file.
  FMyNameValuePairs := TStringList.Create({OwnsObjects}False);
  FMyNameValuePairs.Add('Jansen=100');
  FMyNameValuePairs.Add('Petersen=200');
  FMyNameValuePairs.Add('Gerritsen=300');
  FMyNameValuePairs.Add('Dirksen=400');
  FMyNameValuePairs.Add('Karelsen=500');

  // Show the contents of the "configuration" in a memo
  Memo1.Text := FMyNameValuePairs.Text;

  // Load the "configuration" contents of the StringList into the ComboBox
  for idx := 0 to FMyNameValuePairs.Count - 1 do
    ComboBox1.AddItem(FMyNameValuePairs.Names[idx], 
      TObject(StrToIntDef(FMyNameValuePairs.ValueFromIndex[idx], 0)));
end;

OwnsObjects [3]

ComboBox filled with name value pairs relying on values being integers

Yes. We have lift off.

Coming back down isn’t too bad either.

When you need to get the value of a string selected in the ComboBox, all you need to do is cast the object reference back to an integer.

procedure TBetter_Form.ComboBox1Select(Sender: TObject);
begin
  Name_Text.Caption := ComboBox1.Items[ComboBox1.ItemIndex];
  // Integer cast of TObject in this case is safe even on 64-bit because we
  // ourselves are putting in Integer values.
  Value_Text.Caption := IntToStr(Integer(ComboBox1.Items.Objects[ComboBox1.ItemIndex]));
end;

As said, this hack requires all values are integers. So what to do when your values are something else instead?

StringList silver

When the values in your name value pairs always are not integer numbers but strings or whatever else comes your way, you need to do a little more work, but not much.

As a StringList is a list of string-object associations, the solution is to wrap your values in an object instance before adding the name and that instance to the ComboBox.

If you are using a Delphi version that has neither generics [4] or extended RTTI [5] , you will have to create specific classes for each type you need to wrap. Otherwise you only need to declare a single class. I have opted for using extended RTTI because that has built-in “to string” conversion. Using generics you would have to code that yourself.

type
  TValueObject = class(TObject)
  strict private
    FValue: TValue;
  public
    constructor Create(const aValue: TValue);
    property Value: TValue read FValue;
  end;
  
  { TValueObject }

constructor TValueObject.Create(const aValue: TValue);
begin
  FValue := aValue;
end;

The way you use this approach is pretty straightforward and comparable to the integer hack. All you would have to change is the instantiation of the StringList to set OwnsObjects to True and the loop over the FMyNameValuePairs StringList to read as:

  // Load the "configuration" contents of the StringList into the ComboBox
  for idx := 0 to FMyNameValuePairs.Count - 1 do
    ComboBox1.AddItem(FMyNameValuePairs.Names[idx], 
      TValueObject.Create(FMyNameValuePairs.ValueFromIndex[idx]));

Just one problem. The ComboBox doesn’t take ownership of the instances that are added as Objects for its items. The way to get around that is to ensure that FMyNameValuePairs is instantiated with OwnsObjects set to True; add the instances as Objects for the FMyNameValuePairs‘ items and then just pass them to the ComboBox.

  // FMyNameValuePairs should be instantiated with OwnsObjects = True
  for idx := 0 to FMyNameValuePairs.Count - 1 do
    FMyNameValuePairs.Objects[idx] := TValueObject.Create(FMyNameValuePairs.ValueFromIndex[idx]);

  for idx := 0 to FMyNameValuePairs.Count - 1 do
    ComboBox1.AddItem(FMyNameValuePairs.Names[idx], FMyNameValuePairs.Objects[idx]);

As the ComboBox doesn’t take ownership of the Objects, it means that the Objects of the ComboBox’ items hold references to the value objects actually contained in FMyNameValuePairs. So you had better make sure that the value objects in FMyNameValuePairs live longer than the ComboBox’s items. This can be achieved by clearing the ComboBox before freeing FMyNameValuePairs.

procedure TAnyValue_Form.FormDestroy(Sender: TObject);
begin
  // Ensure the ComboBox can no longer reference the items in FMyNameValuePairs
  ComboBox1.Clear;

  FMyNameValuePairs.Free;
end;

ComboBox filled with name value pairs allowing values of any type

When you need to get the value of a string selected in the ComboBox, all you need to do is get it back out of its wrapper:

procedure TAnyValue_Form.ComboBox1Select(Sender: TObject);
begin
  Name_Text.Caption := ComboBox1.Items[ComboBox1.ItemIndex];
  Value_Text.Caption := 
    TValueObject(ComboBox1.Items.Objects[ComboBox1.ItemIndex]).Value.AsString;
end;

StringList gold

But why stop there? Now that you have discovered the capability of stripping a name value pair list down to its names while retaining the association with its values, there is nothing stopping you from limiting that to a ComboBox.

To achieve that, you change the FMyNameValuePairs a bit:

  // Convert the contents so both the ComboBox and Memo can show just the names
  // and the values are still associated with their items using actual object
  // instances.
  for idx := 0 to FMyNameValuePairs.Count - 1 do
  begin
    FMyNameValuePairs.Objects[idx] := 
      TValueObject.Create(FMyNameValuePairs.ValueFromIndex[idx]);

    FMyNameValuePairs.Strings[idx] := FMyNameValuePairs.Names[idx];
  end;

Of course you need to do that before you hand it to any control capable of showing the contents of a StringList. And doing that now actually is as simply as you initially thought it should be:

begin
  // Set the contents for the ComboBox
  ComboBox1.Items := FMyNameValuePairs;
end;

ComboBox and Memo filled with names of name value pairs allowing values of any type

Enjoy!


Code for the gold approach

type
  TValueObject = class(TObject)
  strict private
    FValue: TValue;
  public
    constructor Create(const aValue: TValue);
    property Value: TValue read FValue;
  end;

{ TValueObject }

constructor TValueObject.Create(const aValue: TValue);
begin
  FValue := aValue;
end;

procedure TGold_Form.ComboBox1Select(Sender: TObject);
begin
  Name_Text.Caption := ComboBox1.Items[ComboBox1.ItemIndex];
  Value_Text.Caption := 
    TValueObject(ComboBox1.Items.Objects[ComboBox1.ItemIndex]).Value.AsString;
end;

procedure TGold_Form.FormCreate(Sender: TObject);
var
  idx: Integer;
begin
  // Simulate loading from some configuration file
  // We set OwnsObjects to True so that any object instances we add will be
  // freed automatically when we free the string list.
  FMyNameValuePairs := TStringList.Create({OwnsObjects}True);
  FMyNameValuePairs.Add('Jansen=Aquarius');
  FMyNameValuePairs.Add('Petersen=Capricorn');
  FMyNameValuePairs.Add('Gerritsen=Pisces');
  FMyNameValuePairs.Add('Dirksen=Libra');
  FMyNameValuePairs.Add('Karelsen=Scorpio');

  // Convert the contents so both the ComboBox and Memo can show just the names
  // and the values are still associated with their items using actual object
  // instances.
  for idx := 0 to FMyNameValuePairs.Count - 1 do
  begin
    FMyNameValuePairs.Objects[idx] :=
      TValueObject.Create(FMyNameValuePairs.ValueFromIndex[idx]);

    FMyNameValuePairs.Strings[idx] := FMyNameValuePairs.Names[idx];
  end;

  // Show the contents of the "configuration" in a memo
  Memo1.Text := FMyNameValuePairs.Text;

  // Load the "configuration" contents of the string list into the combo box
  ComboBox1.Items := FMyNameValuePairs; // Does an Assign!
end;

procedure TGold_Form.FormDestroy(Sender: TObject);
begin
  // Ensure the ComboBox can no longer reference the items in FMyNameValuePairs
  ComboBox1.Clear;

  // Free the "configuration" string list.
  // As OwnsObjects is True, we don't need to free the Objects ourselves.
  // It is done for us.
  FMyNameValuePairs.Free;
end;

Footnotes

  1. Name Value pairs, or key value pairs, seem to have a ubiquitous nature. Come to think of it, there is a whole data storage pattern based on it. The Entity-Attribute-Value pattern.
    When applied to relational databases, the Entity-Attribute-Value pattern is considered an anti-pattern. More details and discussions can be found through the following links:

  2. Actually, the setter does an Assign when the internal FItems member has already been assigned. Otherwise it just sets its FItems member to the supplied instance. This is because TCustomCombo allows descendants to give FItems a reference to TStrings descendant of their liking, but still keeps FItems completely under its own control by declaring it private. Which forces descendants to go through the setter. The TCustomComboBox descendant of TCustomCombo does exactly that. In its constructor it sets the Items property to a newly instantiated TStrings descendant. Which means that when you come to use the Items property of a TComboBox, the FItems member is already assigned and when you set the Itemsproperty, the Assign method is used to copy the contents.

  3. In old(er) versions of Delphi TStringList does not have the OwnsObjects constructor parameter. Means that you need to loop over the list yourself to free all the instances stored in Objects before you free the StringList itself.
  4. Generics were introduced in Delphi 2009 but are generally considered usable only as of Delphi 2010.
  5. Extended RTTI was introduced in Delphi 2010.
Posted in Software Development
Tags: , , , , , , ,

20 resources on (migrating to) Unicode with Delphi

Are you maintaining an application that has been around for more than 5, 6 years? Using a pre-Unicode Delphi version (pre D2009) to do so?

Would you like to switch to a newer Delphi version to gain the advantages of Unicode, generics, extended RTTI, 64 bit, the REST client library and other such niceties? So you can build new features for your users in ways that are not possible, or not cost effective, with your current version? Or perhaps so you can offer your application as a multi-platform solution?

Or, are you already on a Unicode enabled Delphi version and now faced with having to deal with textual data coming at you from all sorts of sources in all sorts of different character encodings (ASCII / Ansi being just one of them)?

Is having to deal with the Ansi/ASCII to Unicode conversion holding you back?

Do you dread having to deal with all the string types when reading or writing your data?

You shouldn’t.

Strings are still strings albeit with a different encoding.

As long as you haven’t done any fancy tricks, or (ab)used arrays of chars where you should have used arrays of bytes, your application should make the transition from the pre-Unicode world without too much hassle.

The additions to the RTL to support Unicode make dealing with files using different character encodings and Unicode transformation formats relatively straightforward.

Below are 20 resources to help you deal with your data in a Unicode world.

1

Computerphile provides a thoroughly enjoyable explanation of why Unicode came to be in the first place. He also illustrates the “greatest hack” which nowadays is Unicode’s most ubiquitous transformation format, UTF-8:

Characters, Symbols and the Unicode Miracle – Computerphile (video)

2 and 3

The number one and two resources on Unicode and Delphi are Cary Jensen’s white paper Delphi Unicode Migration for Mere Mortals: Stories and Advice from the Front Lines (direct link to the pdf), and Marco Cantù’s white paper Delphi and Unicode (direct link to the pdf)

Both include a technical overview on how Delphi implements Unicode support and what parts of your application may be affected by it. Marco’s was the original white paper that accompanied the Delphi 2009 version. Cary’s was published about a year later and has the benefit of including advice based on practical experience.

4

A list on Unicode resources isn’t complete without a reference to Joel Spolsky’s The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

A must read for every developer, even if you are not that technically minded and leave the nitty gritty of reading and writing characters to your component and database vendors or colleagues that are more technically inclined.

5, 6 and 7

Nick Hodges wrote a triad of articles on Delphi and Unicode that accompanied the Delphi 2009 launch. They are not as comprehensive as Marco’s and Cary’s white papers. They do give a quick overview of Delphi’s Unicode capabilities and introduce pre-D2009 version users to a couple of interesting additions to the RTL.

8, 9, 10, 11, 12 and 13

Delphi’s Unicode implementation comes with a gotcha. Many text functions come in two flavors: the plain one and the Ansi one. For example CompareText and AnsiCompareText. CompareText compares text without giving any thought to the locale in which the text is used. AnsiCompareText is locale sensitive.

When these were introduced there names seemed like a good idea. After all, the way to deal with locale issues was to use the Ansi “extension” of the ASCII character encoding.

With the introduction of Unicode support it became obvious that naming locale sensitive functions for the implementation of that locale sensitivity wasn’t the brightest idea. Especially as the names needed to be kept for backwards compatibility reasons.

In the Unicode world, where you need to deal with the difference between Unicode and ASCII/Ansi character encodings, having to use Ansi named functions for locale sensitivity, is confusing to say the least.

Another couple of interesting tidbits and useful experiences can be found in these posts:

14

If converting to and from Unicode is something that you need to do a lot, then the DIConverters library (LGPL open source) may be of help to you. It is a Delphi character conversion library that provides conversion functions for a dozen dozen character encodings.

15

Working with Unicode in XML files can present some challenges. Guidelines by the World Wide Web Consortium (W3C) can be found in Unicode in XML and other Markup Languages.

16, 17, 18, 19 and 20

The Unicode specification is incredibly extensive. You can quite literally get lost in there. The pages and “entry points” I have found most useful are:

Bonus

The Unicode specification contains not only characters, but also punctuation marks, diacritics, mathematical symbols, technical symbols, arrows, dingbats, emoji, etc. Version 7.0 provides codes for 112,956 characters from the world’s alphabets, ideograph sets, and symbol collections.

A few examples from the character code charts are: braille, dingbats, chess, domino tiles, Mahjong tiles, playing cards, musical symbols, cuneiform, technical symbols, transport and map symbols, mathematical symbols and operators, and much much more.

You name it and Unicode probably has it. Just have a look through the Miscellaneous Symbols And Pictographs chart. It would seem that there is hardly anything left that you can’t depict with a single Unicode character.

Just one caveat: the font of your choice needs to support them if you want to use Unicode characters to display them.

Cases in point for example are a pile of poo and a dove of peace. My Chrome doesn’t render these characters so it is just as well that the webpage provides a server generated image.

Posted in Software Development
Tags: , , , , , , ,

How to get the location of the user’s “My Documents” folder

Your application allows your users to create and save files. You help your users by remembering where the last file was stored they don’t have to navigate their entire folder structure every time they want to save a file.

procedure TMainForm.SaveButtonClick(Sender: TObject);
begin
  SaveDialog1.InitialDir := UserSettings.LastUsedFolder;
  SaveDialog1.Execute();
  UserSettings.LastUsedFolder := ExcludeTrailingPathDelimiter(ExtractFilePath(SaveDialog1.FileName));
end;

So far so good.

First time experience

To make you application even better, you want to improve the experience of saving something for the very first time after installation. Right now, as the value for the “last save location” is still an empty string, the save dialog takes you straight to the user’s “Documents” library. [1, 2, 3]

While not bad, what you really want is to send your users straight to the “Your Beautiful App” folder in their “My Documents” folder.

You have already changed your save code to look like:

procedure TMainForm.SaveButtonClick(Sender: TObject);
begin
  if UserSettings.LastUsedFolder = '' then
    SaveDialog1.InitialDir := IncludeTrailingPathDelimiter({PathToMyDocuments}'') + 'Your Beautiful App'
  else
    SaveDialog1.InitialDir := UserSettings.LastUsedFolder;

  if SaveDialog1.Execute() then
    UserSettings.LastUsedFolder := ExcludeTrailingPathDelimiter(ExtractFilePath(SaveDialog1.FileName));
end;

All you need now is a way to find the correct path for someone’s “My Documents” folder.

So where is this thing?

Trouble ahead?

Well… that happens to depend on a the Windows version and localization that your application is running under. Different Windows versions have put the user’s “My Documents” folder in different locations. It has travelled from C:\Documents and Settings\Marjan\My Documents (XP and earlier) to C:\Users\Marjan\My Documents (Vista and up). [4]

Then there is localization. What you know as the My Documents folder is the Mijn documenten folder on my Dutch Windows.

And roaming profiles for network users where the entire user profile isn’t even on the C: drive, but somewhere on their network.

Nah…

Luckily, you don’t need to worry about all this. Windows has it all covered for you. All you need to do is ask Windows for the path you need. Depending on the Delphi version you use, that is a breeze or a slightly stiffer wind.

Delphi XE5 and up

If use Delphi XE5 or later, you are home free. Getting the location of the user’s My Documents folder is a simple matter of calling the GetDocumentsPathmethod of the TPath record in System.IOUtils:

Memo1.Lines.Add('Home path (%USERPROFILE%): ' + TPath.GetHomePath);
Memo1.Lines.Add('Documents (My Documents): ' + TPath.GetDocumentsPath);
Memo1.Lines.Add('Pictures (My Pictures): ' + TPath.GetPicturesPath);
Memo1.Lines.Add('Music (My Music): ' + TPath.GetMusicPath);
Memo1.Lines.Add('Movies (My Videos): ' + TPath.GetMoviesPath);
Memo1.Lines.Add('Temporary files (TEMP): ' + TPath.GetTempPath);

The TPath record in XE5 and up has support for a whole slew of platform agnostic folders such as the “Home”, “Pictures”, “Music”, and “Temp” folders. On the Windows platform these are conceptually known as Known Folders. Quite nice stuff. You can even add custom folders to Windows’ default set.

The platform agnostic folders that TPath supports are documented on
Standard RTL Path Functions across the Supported Target Platforms

Pre XE5

If you are using an earlier Delphi version, then you will have to call the Windows API functions yourself.

On Windows 7 and up you can use SHGetKnownFolderPath. On earlier Windows versions you will need to use its predecessor SHGetFolderPath or SHGetSpecialFolderPath

But … SHGetFolderPath is marked deprecated on MSDN. And SHGetSpecialFolderPath is marked “not supported. Instead, use SHGetFolderPath”.

Eeeks. Now what?

Hang on to your hat. Deprecated is not the same as “gone”.

In fact, Delphi (XE6 in this case) doesn’t yet include an external function declaration for SHGetKnownFolderPath. It only declares SHGetFolderPath in the Winapi.SHFolder unit. And indeed the TPath methods in XE5+ use that function as well..

What’s more Delphi 2009 doesn’t even have the SHGetFolderPath declaration yet. It only has a declaration for SHGetSpecialFolderPath. And that still works when used on Windows 7. [5]

If all your customers are on Windows 7 and up, you could go with SHGetKnownFolderPath and ignore the older functions. If however some of your users are still on Vista, then you will have to use SHGetFolderPath (as well) to support them.

I have opted to just go with SHGetFolderPath as newer Windows versions still support it and it is preferred over SHGetSpecialFolderPath in the MSDN documentation.

If you are on a Delphi version that does not provide an external declaration for SHGetFolderPath it is easy enough for you to add it yourself. You need a declaration

function SHGetFolderPath(hwnd: HWND; csidl: Integer; hToken: THandle; dwFlags: DWord; pszPath: LPWSTR): HRESULT; stdcall;

and an “implementation”:

function SHGetFolderPath; external 'SHFolder.dll' name 'SHGetFolderPathW';

Instead of just adding these to some “utils” unit and calling them directly where I need to get a folder location, I prefer to isolate my code from API’s and third party libraries. So I have created a “helper” unit with a record to hide the nitty gritty of calling the Windows API. You could, of course, also opt for a “static” class (so you don’t have to instantiate it).

Benefits

  • When Microsoft does decide to remove the SHGetFolderPath API, you only need to change a single unit. All your other code can remain unchanged.
  • You create code that is better unit testable.
    As unit tests should be independent of outside influences, you would also have to use some mock, stub or shim to “fake” the dll that provides the API.
unit FolderHelper;

interface

type
  RFolderHelper = record
  strict private
    class function GetFolder(const aCSIDL: Integer): string; static;
  public
    class function GetMyDocumentsFolder: string; static;
    class function GetMyMusicFolder: string; static;
    // ... any other folders in which you are interested
  end;

implementation

uses
  ShlObj, // Needed for the CSIDL constants
  Windows;

  function SHGetFolderPath(hwnd: HWND; csidl: Integer; hToken: THandle;
    dwFlags: DWord; pszPath: LPWSTR): HRESULT; stdcall;
    forward;
  function SHGetFolderPath; external 'SHFolder.dll' name 'SHGetFolderPathW';

class function RFolderHelper.GetFolder(const aCSIDL: Integer): string;
var
  FolderPath: array[0 .. MAX_PATH] of Char;
begin
  SetLastError(ERROR_SUCCESS);
  if SHGetFolderPath(0, aCSIDL, 0, 0, @FolderPath) = S_OK then
    Result := FolderPath;
end;

class function RFolderHelper.GetMyDocumentsFolder: string;
begin
  Result := GetFolder(CSIDL_PERSONAL);
end;

class function RFolderHelper.GetMyMusicFolder: string;
begin
  Result := GetFolder(CSIDL_MYMUSIC);
end;

  // ... similar implementations for the other folders

end.

Just as with classes the fact that the GetMyDocumentsFolder is declared as a class method means you can call it without an instance variable:

SaveDialog1.InitialDir := IncludeTrailingPathDelimiter(RFolderHelper.GetMyDocumentsFolder) + 'Your Beautiful App'

Enjoy!


Notes on the FolderHelper unit

  • A convention my colleagues and I adopted some time ago with regard to record declarations is to prefix their names with an “R” rather than “T” as is the custom in Delphi. I find it very convenient that the type name makes it obvious that it is a record not a class. It means I don’t have to Ctrl-Click through to the declaration of that type to check whether or not the lack of calls to construct or free it is as it should be.
  • class methods in records require that you mark them with static as well. I have no clue as to why. The compiler simply demands it. Funny quirk that.
    I have always found that class methods in Delphi should not have been named “class methods” as it introduces a lot of confusion when talking about the methods of classes.
    It would have been so nice if they had been named “static methods” just like other languages have done.
    Unfortunately, in Delphi, static was used to name the default binding method where the declared (compile-time) type of the class or object variable determines which implementation to activate. In other words, in Delphi, all methods that are not marked as virtual or dynamic are understood to be static.
    So why do class methods in a record need to be explicitly declared as static? It’s the default after all? And last time I checked records do not support inheritance, so virtual and dynamic binding is not even in the picture for them?
  • I follow the “always initialize your variables” rule.
    May seem strange then that the GetFolder method is lacking a Result := ''; statement.
    But it isn’t. Not really.
    The compiler does it.
    The result is of type string.
    A string is a managed type and managed types are always initialized by the compiler.

Footnotes

  1. This is the default on Windows 7. On earlier Windows versions this may have been different. Though I don’t know when the desktop became the default, IIRC it once defaulted to the current dir, which of course is the location of your application’s executable unless you start that through a short cut with a different “start in” location.
  2. When you run with debugging, opening the save dialog for the first time is incredibly slow. Probably because of the debug hooks and the number of dll’s being loaded to support the save dialog. The second time you open the dialog is is a lot faster than the first time, but to all intents and purposes it is still slow.
  3. When you run with debugging, the first time you open the save dialog it goes straight to the … Desktop ?! wtf? Interestingly, when you cancel the dialog and reopen it, it does go to the user’s “Documents” library… ?! But with a non blank value in InitialDir it keeps going to the desktop regardless of how many times it has been opened?!
  4. Even though Windows reports my “My Documents” folder as C:\Users\Marjan\Documents, it actually lives on my D: drive… :) But Windows handles that in a completely transparent manner for all applications so you don’t need to worry about it.
  5. Interestingly, the ShlObj unit in D2009 does have a declaration for SHGetFolderLocation (which gets the location of a folder relative to the desktop) and mentions SHGetFolderPath in a comment.
Posted in Software Development
Tags: , , , , , , , , ,

How to find all instantiated forms in your project at run time – Take two

Boy, do I feel stupid!

As David pointed out in the comments to take one of this subject, there is a much easier way to get all the instantiated forms at run time:

var
  CustomForm: TCustomForm;
  idx: Integer;
begin
  for idx := 0 to Screen.CustomFormCount - 1 do
  begin
    CustomForm := Screen.CustomForms[idx];
    Memo1.Lines.Add(Format('%s (%s)', [CustomForm.Name, CustomForm.ClassName]));
  end;
end;

Forgot all about Screen.CustomForms existence. And it’s been there since at least D5… My only excuse is that I haven’t done much UI programming in the last, what, seven years.

There is also Screen.Forms, but that only lists TForm descendants. While Screen.CustomForms lists all TCustomForm descendants including for example property pages.

Caveat
There is a caveat in using either. According to the docs, the order in which instantiated forms appear in both lists is affected by the Z-order of all forms. So while you are iterating over one of these lists, you shouldn’t do anything that might affect the Z-order, such as creating and/or activating another form in your application, as that would change the contents of the list!

If you need to do anything like that, then your best bet is still to use Application.Components like I showed in take one.

Posted in Software Development
Tags: , , , , , , ,

How to find all instantiated forms in your project at run time

For reasons quite unfathomable by me, you would like to get your hands on a list of all forms in your project. (*)

But wait. Do you want this at design time? Or at run time?

Oh, at run time. Pfffew. Ok. I can do that. (**)

But wait. Do you want a list of the forms that are instantiated? Or all the forms?

Please say instantiated. I can do that. It’s easy.

All forms is harder. Unless… you are using D2010 or higher.

Let’s ease into this. Let’s start with finding the instantiated forms first. I’ll cover finding all forms in a future post (if I forget, feel free to remind me).

All instantiated forms

**Edit**
As David mentioned in the comments, there is a much easier way of getting all instantiated forms in your application at run time. It has a caveat though. See take two on this subject for more information.

Finding the instantiated forms should be easy. Just iterate over the Application’s components and check each to see whether it is a descendant of TCustomForm.

Why the check? Because the Application’s component list can include components that are not forms. Any data modules that you have instantiated are in there. As well as any other components that you have instantiated passing the Application as their owner.

Why not check for descendants of TForm? Because there is nothing that says that a forms should descend from TForm. TForm just happens to be the class that forms created through the IDE descend from. And all it does when compared with TCustomForm is give a whole slew of properties published visibility so the Object Inspector can see them. (For the nitpickers amongst us: it also adds a class constructor and destructor to register and unregister a style hook.)

Our first try would thus look like:

var
  Component: TComponent;
  idx: Integer;
begin
  for idx := 0 to Application.ComponentCount - 1 do
  begin
    Component := Application.Components[idx];
    if Component.InheritsFrom(TCustomForm) then
      // Whatever you want to do with them. Me, I am just adding them to a Memo.
      Memo1.Lines.Add(Format('%s (%s)', [Component.Name, Component.ClassName]));
  end;
end;

That should get you a list of all instantiated forms in your application.

[Grumble]
By the way, I hate that we still have to use an ordinary for loop here. I would much prefer to use a for-in loop:

for Component in Application.Components do

I really don’t understand why the powers that be haven’t seen fit to enable this. All it takes is an enumerator and it would make a lot of code that much more readable and less (off-by-one) error prone.
[/Grumble]

But wait…

The code above will only get you the instantiated forms that have Application as their owner. That is forms instantiated by either:

Application.CreateForm(TMyForm, MyForm);

or

MyForm := TMyForm.Create(Application);

It won’t get you any of the forms that were instantiated without an owner:

SomeForm := TMySpecialForm.Create(nil);

Neither will it get you forms that were instantiated with a component other than Application as their owner:

SecondForm := TSecondForm.Create(FirstForm);
ThirdForm := TThirdForm.Create(MyDataModule);

You could dig down from Application and examine the entire component tree recursively.

procedure DigDownComponent(const aComponent: TComponent; const aList: TStrings; const aPrefix: string); forward;

procedure DigDownApplicationComponents;
var
  FormList: TStringList;
begin
  FormList := TStringList.Create;
  try
    DigDownComponent(Application, FormList, '');
  finally
    FormList.Free;
  end;
end;

procedure DigDownComponent(const aComponent: TComponent; const aList: TStrings; const aPrefix: string);
var
  idx: Integer;
  Current: TComponent;
begin
  for idx := 0 to aComponent.ComponentCount - 1 do
  begin
    Current := aComponent.Components[idx];

    if Current.InheritsFrom(TCustomForm) then
      aList.Add(Format('%s%s (%s)', [aPrefix, Current.Name, Current.ClassName]));

    if Current.ComponentCount > 0 then
      DigDownComponent(Current, aList, aPrefix + '  ');
  end;
end;

That is a lot more complete. It will get you all forms instantiated with either Application or another component as their owner.

It still won’t get you the forms that were instantiated without an owner though.

There isn’t much you can do about that. Not unless you are prepared to do some heavy lifting in the realms of hooking, enumerating handles, posting and responding to messages, or some other means of getting your hands on otherwise elusive players.

I’d say it would be far easier to set up some form factory or registry of your own.

Assuming you are gunning for some consistency in your forms, you will be using your own base form already anyway, so getting all forms to register themselves upon instantiation should be simple (and may be the subject of a future post).

Footnotes

(*) What does “in your project” mean exactly? Just the forms in units that are included in the dpr’s uses clause? Or also forms in units that are included in uses clauses of other units? But what about forms included in the VCL and RTL then? Or in third party libraries for that matter? Or perhaps in your own libraries? Where do you draw the line? And how would you be able to recognize that line in code?

(**) Getting all the forms in your project at design time is possible too of course. It does need a different approach though. One I would have to do quite some research on. Research that isn’t of much interest to me at the moment as I don’t do a lot of UI development nowadays. That said, I might still dig into it out of curiosity. Couple of gentle pushes from the community might help :)

Posted in Software Development
Tags: , , , , , , ,

How do you know your software works? And keeps working?

Which scenario do you like better?

You wake up in the morning. The sun shines brightly through your window, but for all you care it might as well be pouring with rain. The bug reports are stacked high and low on your desk. The minute you walk in someone from support will be on your back about clients X, Y and Z who have effectively been unable to use your software for days now, making it impossible for them to serve their clients. You hope the coffee machine is working. Otherwise you will have to go down to the floor housing sales and product management. They will jump at the chance to ask you how features A, B and C are coming along. Features they have been waiting on for months now. Is Gary back from holiday today? Darn. That means that you will have to explain why it took so long to fix the export to Excel thingie you thought would only take half a day. Gary is talking about hiring more developers to regain some speed. Nice and all, but where are you going to find the time to train them? The prospect of all this weighs you down, draining all the energy you regained while asleep and then some. You drag yourself out of bed and to work. Not because you enjoy it, but because you need money on the table to pay your bills.

You wake up in the morning. The sun shines brightly through your window. You squint a little clearing the sleepiness from your head. Today is going to be a good day. You are going to finish that cool new feature client C requested two weeks ago. It was an interesting challenge. You learned a lot in the process. It isn’t the last. Plenty more features in the pipeline that will be fun to build. Justin, the product manager, will be around this afternoon to discuss a couple more ideas. Sales have a few prospects that are requesting special features and Gene, the support manager, would like some extra reporting so he can detect hot spots before they become a problem. Oh and Bill is coming over for lunch. Lunches with Bill are the best. Bouncing around ideas and coming up with better ways to do stuff always gets both of you going. Would be interesting to take the new hire along. See how he responds. The prospect of a good day gets you out of bed with eager anticipation.

Me?

I am absolutely partial to the second scenario.

Yesterday I ran across a post on software quality.

It struck a chord with me. It articulates exactly what I feel.

I care deeply about software quality. Not to alleviate my innate fear of failure. Not out of perfectionism. Not to do things “right”.

I care deeply about software quality because it keeps my work fun and interesting.

I’d much rather be building new features than fixing bugs. I prefer to work without the pressure of “fixing” things as fast as possible because a client is screaming hell and bloody murder.

Building quality software is the only way I know how to make this happen and to keep it happening.

All other ways of building software may seem to have very big advantages, but those advantages only manifest in the short term.

Like delivering results quickly.

I like delivering results quickly just as much as anybody else.

Even if you need to take shortcuts to do that.

Shortcuts are fine only as long as you redo them properly immediately after delivery of the feature. If you don’t, those shortcuts come back to haunt you.

Unless you address them immediately and ruthlessly, every shortcut, whether in code, in tests or in documentation, makes new features a teensy bit harder, less reliable and more work to implement.

Which slows down delivery of new features.

And which can introduce subtle bugs that you will have to fix at a later stage when you are no longer familiar with the subject.

Fixing bugs also slows down delivery of new features.

Taking one shortcut may not seem like a big deal. The effect is limited. But unless you fix it, that tiny bit of extra work needs to be done every time you deal with that code. It cumulates quickly.

You think you can take that hit. It is in some part of your code that doesn’t get changed often, so the effect remains limited. No biggie.

However, there is more to accepting a shortcut than accepting the hit on effort required for future changes.

It sets a standard.

That shortcuts are somehow ok.

Soon you won’t have one shortcut but two, then three, five, nine and more.

The more shortcuts you take, the bigger the effect.

After some time of taking shortcuts, the speed at which new features are delivered really starts dropping.

Bug lists get longer. Implementing new features requires more and more knowledge that hasn’t been made explicit anywhere. New hires take a long time to get up to speed. Seniors are stretched beyond breaking point implementing new features only they have the experience with your code base to do properly and guiding less experienced colleagues.

Parts of your code will be in dire need of refactoring.

There will be parts of your code you don’t dare touch for fear of introducing bugs or inadvertently disabling features that you no longer even know exist but that your clients rely on.

The calls for a rewrite will start to be heard and grow in frequency and intensity.

The first scenario looms large overhead.

The only way I know how to avoid getting into that first scenario is relentless attention to and striving for quality software by everyone on the team. Just like Bob describes it in the last paragraph of his post

Posted in Software Development
Tags:

How to get a ListView to display your values in columns

Can’t get a ListView to display the values from your database in columns properly?

You want to display information from your database. You would like it to be displayed in a list with columns. A ListView can do that.

Should be as easy as 1-2-3.

Just set up a query, loop through the records and add the field values to the ListView. Something like this:

    begin
      SQLQuery.SQL.Text := 'SELECT FirstName, LastName, Country FROM Famous_Actresses';
      SQLQuery.Open;
      while not SQLQuery.Eof do
      begin
        ListView.AddItem('First name: ' + SQLQuery['FirstName'], nil);
        ListView.AddItem('Last name: ' + SQLQuery['LastName'], nil);
        ListView.AddItem('Country: ' + SQLQuery['Country'], nil);
      end;
    end;

Right?

Wrong.

When you do it like this, it comes out as a long list of values all in a single column.

ListView displaying values as a list despite vsReport ViewStyle

That’s not what you want.

You want a nice orderly list in which every field has its own column. Just like how Windows Explorer displays files in its details view.

How Windows Explorer displays files in Details view style

Relax. You’re half way there.

Just two things left to do:
– Tell the ListView about the columns you want it to display.
– Add the values from your database to their specific columns.

Setting up columns for a ListView

You can add columns to a ListView in the visual form designer by double clicking the Columns property of the ListView and adding the columns you need. While easy to do, you now have tied that ListView to a specific set of columns. And I bet you that you will have plenty more lists to show in any application worth using.

When you know how to do those things in code, you give yourself the option of reusing a single form for many lists.

So, let’s do it in code:

    var
      Column: TListColumn;
    begin
      // Set up columns
      Column := ListView.Columns.Add;
      Column.Caption := 'First Name';
      Column.Alignment := taLeftJustify;
      Column.Width := -1;

      Column := ListView.Columns.Add;
      Column.Caption := 'Last Name';
      Column.Alignment := taLeftJustify;
      Column.Width := -1;

      Column := ListView.Columns.Add;
      Column.Caption := 'Country';
      Column.Alignment := taLeftJustify;
      Column.Width := 140;
    end;

Tips
– Set a column’s Width to -1 to make the column display its entire text.
– Set a column’s AutoSize to true to make it resize proportionally when the Width of the ListView changes.

Adding values to specific columns

Now that you have the columns set up, you are all set to make the ListView display your information as you want it. The only thing left to do is to add the values from your database in the way the ListView expects it for the vsReport ViewStyle.

You already got most of the code right in your first try. Just change the lines where you added items to the ListView to:

    var
      Item: TListItem;

      begin
        Item := ListView.Items.Add;
        Item.Caption := SQLQuery['FirstName'];
        Item.SubItems.Add(SQLQuery['LastName']);
        Item.SubItems.Add(SQLQuery['Country']);
      end;

And now you have a well behaved ListView that displays your database values in columns properly.

ListView with Proper Columns

Posted in Software Development
Tags: , , ,

Like this?

Subscribe and get more posts like this delivered straight into your inbox.
* = required field