Thursday, 21 December 2017

Auto create forms and data modules does not quite work

I've recently moved to Delphi 10.1 Berlin Update 2 from using XE and XE5. One thing that caught me out was that I forgot to turn off the 'Auto create forms and data modules' in the 'Form Designer' options. This setting means that when you add a form or data module to a project it gets auto-created at run-time.



The problem with this setting is that it does not fully work. I have a project where I do not want to auto create any forms or data modules, I have a class that is responsible for doing the initial creation of the required forms and objects, but if I add a form or data module to the project it always gets added to the auto create list in the project. This option assumes you want just one form in the list, once one is added it does not add anymore.

This problem could occur in all versions of XE and the now the more recent versions (DX), I plan to check this with XE and will hopefully soon be updating to the latest version of Delphi.

Tuesday, 5 December 2017

Delphi interesting skinning issue with .NET COM wrapper

I've come across an interesting issue with skinning and invoking a .NET Windows form and messagebox. I have a test Delphi application that is skinned using either Alpha Skins or setting the custom styles, I then invoke a .NET wrapper DLL developed in Visual Studio 2015 (COM interop)  and call the method to show a Windows form, this does not get skinned. However on the Windows form there is a button that shows a Windows messagebox, this does get skinned, see image below.


It would be nice if the Windows form also was skinned to match the Delphi application and it seems strange that the Windows messagebox which is invoked by the Windows form is skinned.

Thursday, 21 September 2017

TObjectList vs TObjectDictionary

When I need to store a list of objects I usually use a TObjectList (Generics.Collections), but I was speaking to another developer who uses TObjectDictionary to store a list of objects. I searched the net for when to use a TObjectDictionary instead of a TObjectList and vice-versa, but could not find any definitive answers. I decided to write a little test app to understand the difference, this simply did the following:

  • Created 1 million objects for each list and added them to the list.
  • Cleared the list of the 1 million objects.
  • Populated the list with 1 million newly created objects.
  • Found 100 objects in the list.
Below are the results, time is in ms:


ObjectList Clear - 47
ObjectList Populate - 344
ObjectList Find - 78
ObjectList Find - 78
ObjectList Find - 78
ObjectList Find - 79

ObjectDictionary Clear - 265
ObjectDictionary Populate - 579
ObjectDictionary Find - 0
ObjectDictionary Find - 0
ObjectDictionary Find - 0
ObjectDictionary Find - 0

As you can see the TObjectDictionary was slower to clear and populate the list, but was faster finding the 100 objects. This is most likely due to the TObjectDictionary being derived from TDictionary which is a hash table and is optimised for lookups.

So, generally from these results I am happy sticking with TObjectList, but if I need to store a lot of objects and also need to find a lot of objects then using a TObjectDictionary is a better choice. 

Tuesday, 25 July 2017

Windows 10 Notification

Here is a quick and simple example of a notification procedure. On a form I added a 'TNotificationCenter' component from the 'System' tool palette.

procedure TForm.DoNotification(aName, aTitle, aBody: string);
var
    appNotification: TNotification;
begin
    appNotification := NotificationCenter1.CreateNotification;
    try
        appNotification.Name := aName;
        appNotification.Title := aTitle;
        appNotification.AlertBody := aBody;
        NotificationCenter1.PresentNotification(appNotification);
    finally
        appNotification.Free;
    end;
end;

procedure TForm.ShowTestNotification;
begin
    DoNotification('Test Name', 'Test Title', 'This is an example');
end;

There is an event in the NotificationCenter component called 'OnReceiveLocalNotification', which is useful if you want to display a notification and when a user clicks on the notification the software performs an operation. 

I did this example in Delphi 10.1 Berlin, it is a very limited example due to Delphi being very limited with what can be done with Windows Notifications. Looking at the documentation you can do more with iOS, I am not sure why they have made it so limited. I have briefly looked at some C# examples and you can do more with notifications like custom sounds, icons, images and actions. 

One thing I have noticed is that when I run this example the notification is shown and then when it goes away it is automatically removed from the action centre. One thing I would require is that if the user does not acknowledge the notification, it should stay in the Action Centre list. For me it would be really useful to have the following:
  • Custom image.
  • Custom sound.
  • FireDate to work in Windows 10.
  • Option for the notification to stay in the Action Centre.

I would like to see Embarcadero expand on what these notifications can do, obviously they are limited to what the OS can do. If they could persist in the Action Centre until clicked then I can think of multiple uses for notifications.  

Monday, 24 July 2017

Delphi 10.1 Berlin fix for issue when opening older project

I recently had an issue with a project that had been updated from XE to Delphi 10.1 Berlin. The issue was when opening the project it could not find some components on the main form (3rd party), however the correct packages are installed and if I started a new project I could add the components from the tool palette without any issues.

One work-around I found was having adding these components to another project, then when I opened Delphi 10.1 Berlin I opened this project and then closed it before opening the project that had problems with the components, and it all worked fine.

In the end the solution I found was the following:

  1. In the package itself, find the unit with the 'Register' procedure.
  2. Check 'DesignInf' is in the uses clause.
  3. At the top of the 'Register' procedure add 'ForceDemandLoadState(dlDisable);' 
  4. Install the package again.
What this does is disables the IDE from using the smart loading of installed packages, this means when the IDE starts it always loads the installed packages.

Thursday, 20 July 2017

How to create GUID at runtime

To create a GUID (Globally Unique Identifier) at runtime I have two different ways of doing this.

First Method
Add ComObj unit to the uses clause, if not already there, then to create the GUID as a string just do the following:
FThisID := CreateClassID;

Second Method
This method you need the SysUtils in the uses clause. The do something like the following:
var
  newGUID: TGUID;
begin
  SysUtils.CreateGUID(newGUID);
  FThisID := GUIDToString(newGUID); 
end;

Friday, 2 June 2017

How to dock a form to a panel programmatically

This is a quick solution to a question I had on how to have a form that is set so it can dock into another form's panel. I have a main form with a panel on the right with the 'Docksite' property set to true. I have another form with the 'DragKind' property set to dkDock and the 'DragMode' set to dmAutomatic. I create the docking form at runtime and wanted it so that by default it was docked into the right panel. The quick solution that I found hard to find on the net is after I create the form do the following:

DockingForm.ManualDock(PanelRight);
DockingForm.Show;

Thursday, 25 May 2017

Frame Inheritance - TabOrder: Property does not exist - Issue Fixed

I've been working on an application recently and it has a few frames (TFrame), I noticed that there were common properties and methods to all the frames, so I decided that they should derive from a base class. I created the base frame class and then in code derived the other frames from the base frame. It all seemed to work fine a build and ran OK, however after closing the project and then coming back to it a few days later, when I tried to open a frame I received an error stating the TabOrder property does not exist, I ignored it and received more property does not exist errors (I did not save any changes). The solution to this problem was simple, in the .dfm file at the top was:

object TestFrame: TTestFrame 

This needed to be changed to:

inherited TestFrame: TTestFrame

The ideal way to do this is at design time when designing the structure of the application, if there is an abstract or concrete frame class then this should be added to the repository so when a developer needs to create a new frame, they can simply inherit the frame from the one in the repository.

This is a very similar problem to inheriting datamodules, I've also had to do something similar. 

Wednesday, 19 April 2017

Only use ShowMessage() function for debugging

I always use the ShowMessage function for debugging while doing development and always exclude it from release code for the following reasons:


  1. MessageDlg or MessageBox functions are much better as a popup dialog than ShowMessage, they have more options and the presentation is better giving the user a visual icon categorising the meaning of the popup.
  2. It is easy to search through all the code and check there is no 'ShowMessage' operational before checking in the code. I had a customer call me once saying they have a popup (ShowMessage) which just says 'Hello', this was because the developer forgot to remove or comment out the ShowMessage before checking in.
  3. Because there is no icon with a ShowMessage, some testers and end users assume the popup is an error dialog. I have had the same but to a lesser extent with MessageDlg, the icon helps to show when it is information, warning or confirmation.
Some developers seem to use ShowMessage to quickly display information to the user, they are using ShowMessage because they think it is quicker to use, but in GExperts there is a Message Dialog tool that creates the code based on the options the developer sets.

There is the argument not to use these popup at all, and in some applications they cause issues. I think that popups can be annoying to the user if used too much, but I do not have a problem with them if they are used sparingly and they do not cause problems with the operation of the application.

I imagine some developers might say you should never use ShowMessage to debug and use breakpoints while debugging, which in most cases is the true, but there are occasions when some testing by the developer might be done in a non-development environment, in which case you cannot use the debugger. 

Wednesday, 15 March 2017

Why are semi-colons sometimes not required?

Over the 20 years I've been developing commercial software one question I still have with the Delphi language is why was the language designed so semi-colons are not required at the end of methods? For example:

function DoSomething: boolean;
begin
  if FDoSomethingElse then
  begin
    DoSomethingElse;
  end
end; 

I believe the reason why the compiler does not complain about this, is because the semi-colon in Delphi is a statement separator and not a terminator. If I modify this function for example:

function DoSomething: boolean;
begin
  if FDoSomethingElse then
  begin
    DoSomethingElse;
  end;
  DoOtherStuff
end;

I obviously need to now add in the semi-colon, but I do not need to add in the semi-colon after 'DoOtherStuff'. I currently cannot see any benefit of leaving the semi-colon out. I would not normally code this way and always add in the semi-colon for 2 reasons:

  1. Adding the semi-colon makes the code more consistent.
  2. It means later on when the code is modified the semi-colon does not have to be added.
I have come across Delphi developers who will not put in the semi-colon if it is not required, but cannot see any real benefit from leaving it out. Maybe I have answered my own question, and that is the semi-colon is a separator and when the language was developed they did not see any pros or cons of just allowing the semi-colon not to be there.


Thursday, 23 February 2017

Strict Private Problem with Code Completion

Some time ago I noticed something annoying in Delphi XE with 'Strict Private' and code completion.

Here is an example:

TMyClass = class(TObject)
strict private

public
    property Test: string read FTest write FTest;
end;

When I do CTRL + Shift + C to add the private member to the class it does the following:

TMyClass = class(TObject)
strict private

private
    FTest: string;

published

public
    property Test: string read FTest write FTest;
end;

It adds FTest to the private section of the class and also adds the published section, which is not what I want, but if I remove 'strict' it does not add the 'published' section. Because of this I develop my classes  as 'private' and then once I have added all the require properties then I add 'strict' if required. I am not sure if this behaviour is some setting somewhere that I am not aware of, or whether this is something that might have changed in more recent versions of Delphi. Is there a valid reason to have a 'published' section when there is a 'strict private' section?

Friday, 27 January 2017

Should constants be uppercase?

In a recent blog someone pointed out to me that boolean values true and false should be uppercase because they are constants. This made me think, usually I put constants in uppercase, however when I code true and false they are always lowercase, so it raises a question about my code style, should all constants be uppercase? If so then true, false and nil should also always be uppercase, but for me this does not look correct. Should constants not be uppercase? One reason why constants are uppercase is so they stand out from variables, but is this the only reason?

When I develop in C#, I follow the recommendation for constants not to be uppercase, so I am starting to feel that maybe this should be the same in Delphi and constants should be camel case for local constants and pascal case for public ones.