Quantcast
Channel: TMS Software
Viewing all 1008 articles
Browse latest View live

Aurelius objects everywhere - Distributed applications using JSON

$
0
0

TMS Aurelius version 1.7, released a couple of weeks ago, introduced a new feature that was often requested by users: JSON support. You may ask yourself, why is that so important? Can't I "use JSON" already with Delphi itself using JSONMarshal class, or even by a 3rd party library like Super Object?

Well, yes and no. Actually there are already many people using Aurelius in distributed applications, using it with DataSnap, REST servers. etc.. My previous blog post is an example of that. If you have in your application simple entity classes like this:

  
  [Entity, Automapping]
  TCustomer = class
  private
    FId: Integer;
    FName: string;
    FCity: string;
  public
    property Id: Integer read FId write FId;
    property Name: string read FName write FName;
    property City: string read FCity write FCity;
  end;
Then it would work smoothly. One of the things I like most in Aurelius is your entity classes can be very simple. You can use simple types, simple objects, don't need to inherit from a specific class or override methods. So you can build your entity classes the way it will fit for your application, in this case, make them simple to transfer them between your client and server.

But you might also want to benefit from many Aurelius features: nullable types, lazy-loading, blob support, associations, lists. That's when the problem arises. Most JSON libraries don't handle them correctly. Some of them allow you to add some custom converters and attributes that maybe would make it happen, but it would pollute your class (why add extra meta information when your mapping is already done) and probably will require many hours of extra effort to make it work.

So, that 's the reason for this new feature - it takes advantage of the mapping information for the serialization and deserialization. Aurelius already knows which fields/properties are important (mapped), the custom types, the lists, associations, etc.. So for example, to build a DataSnap REST server that has methods returning objects, you can simply implement your server methods like this;
function TBugTrackerMethods.LoadIssue(Id: integer): TJsonValue;
begin
  Result := FSerializer.ToJson(FManager.Find<TIssue>(Id));
end;

procedure TBugTrackerMethods.SaveIssue(Issue: TJsonValue);
begin
  FManager.SaveOrUpdate(FDeserializer.FromJson<TIssue>(Issue));
  FManager.Flush;
end;

function TBugTrackerMethods.ListIssuesByProject(ProjectId: integer): TJsonValue;
var
  IssueList: TList<TIssue>;
begin
  IssueList := FManager.Find<TIssue>
    .SubCriteria('Project')
    .Where(TLinq.IdEq(ProjectId))
    .List<TIssue>;
  try
    Result := FSerializer.ToJson(IssueList);
  finally
    IssueList.Free;
  end;
end;
It doesn't matter how complex a TIssue class is built. Actually all the structure was built not only to make it easy to send/receive Aurelius objects, but also to mimic the behavior of TObjectManager class, if needed. So if you have an existing client-server Aurelius application, changing it to retrieve objects from a remove application server instead of database should be very simple and straightforward. Associations are fully supported, even with lazy-loading. Even from non-Delphi clients, like JavaScript, the client you will write will look very similar to a Delphi application using Aurelius. The following code is an example in JavaScript (using JQuery) that communicates with the DataSnap server mentioned above. It loads an issue from the server (using an ID provided by the user) and allows you to close or cancel the issue by clicking buttons:
var issue = null;
$(document).ready(function() {

  $('#issuediv').hide();

  $('#searchButton').click(function(event) {
    issue = serverMethods().LoadIssue(issueIdField.value).result;
    $('#issuediv').show();
    $('#idField').val(issue.Id);
    $('#subjectField').val(issue.Subject);
    $('#priorityField').val(issue.Priority);
    $('#statusField').val(issue.Status);
    $('#assignedToField').val(issue.AssignedTo.Name);
    $('#projectField').val(issue.Project.Name);
    $('#descriptionField').val(issue.Description);
  });

  $('#closeButton').click(function(event) {
    issue.Status = 'Closed';
    serverMethods().SaveIssue(issue);
    $('#issuediv').hide();
  });

  $('#cancelButton').click(function(event) {
    issue.Status = 'Canceled';
    serverMethods().SaveIssue(issue);
    $('#issuediv').hide();
  });
});
A final note: the serializer/deserializer can use any JSON parser library you want. Currently both Delphi (DataSnap) and SuperObject are supported, but architecture is build in a way that any other library can be used.


2012 at TMS, a retrospective

$
0
0

At the end of the year, it is always good to reflect on what the year has brought, to learn from it and to plan for the next year.

Product releases in 2012

In January, we released a new product, TMS WebGMaps, a set of components to add Google mapping capabilities in your Delphi applications.
That was not all, in January, we also released our new ORM for Delphi, TMS Aurelius. It was quickly a widely acclaimed, appreciated and quality ORM framework that we continued to give extensions and improvements throughout the year. And if you thought that was all for January, we also released TMS Metro Controls Pack. This is a set of controls that offer this flat new Microsoft Windows 8 look and feel to your Delphi applications. With respect to this Metro style, we also added Metro style support in the TMS Smooth Controls Pack.

In February, we released a new component for FireMonkey: TTMSFMXTableView, an iOS tableview inspired cross platform list control. Shortly after this, we also released TTMSFMXPopup, the iOS inspired popup window control. But there was more in February: we added Metro style support in the TMS ribbon and we released TMS Aurelius with a major new component TAureliusDataSet to make it easy to connect the ORM to existing DB-aware VCL controls. But without a doubt, the biggest release in February was TMS Flexcel v5. This long awaited and extremely comprehensive library now can natively handle XLS,XLSX,XLSM files on Win32,Win64 and Mac OSX.

In March, we were busy with more FireMonkey components, in particular TTMSFMXTileList. We also created TMS Grid Filters, the interface between TMS Flexcel and the TMS VCL grid components to offer seamless support for importing & exporting grid data from and to XLS and XLSX files. We were also happy to see TMS Aurelius seen choosen by Andreano Lanusse as project of the month.

In April, we launched a new product, TMS Pack for FireMonkey to give customers access to all our FireMonkey components with one bundle. We also released an update for our syntax highlighting VCL TAdvMemo with a list of new features that were added.

In May, TMS Flexcel for VCL/FireMonkey was released with the new APIMate tool that automatically shows the Delphi or C++ code needed to use to create the wanted effects in the XLS/XLSX file. We released TMS Aurelius 1.4 with the new capability to allow mapping of dynamic properties to database columns at runtime. A major update for our award-winning VCL Grid TAdvStringGrid was released with many new capabilities added. Meanwhile, our team continued development off several new FireMonkey components, with two new releases: TTMSFMXHotSpotImage and TTMSFMXSpeedButton.

In June, TMS DataModeler got an update with SQLite support added but also extended capabilities to integrate with TMS Aurelius. We launched in June also the TMS BI Pack, the bundle of TMS Aurelius, TMS Data Modeler, TMS Scripter, TMS Diagram Studio and TMS Query Studio, a bundle of tools to streamline and automate DB applications and DB application development. We also released a new product: TMS IntraWeb Cloud Pack: a set of components to allow seamless integration of cloud servvices such as Paypal, Twitter, Facebook, Windows Live with IntraWeb applications. There was more, TMS HTML5 Controls Pack got an update with XY scatter chart support added and TMS Aurelius was released with new support for Absolute databases and new capabilities for control over and customizing SQL expressions.

In July, after many months of hard work, we released a first version of our grid component for FireMonkey. All TMS Pack for FireMonkey users got now also this powerful and feature rich cross platform grid. We also worked on our website and added a feature voting system. All the time, we of course also continued to update our TMS Component Pack, getting all improvements and new components for VCL Win32 and Win64 development.

In August, we released TMS Cloud Pack v1.0. In this first version, we offered access to cloud storage services DropBox, SkyDrive and Google Drive from VCL applications. The TMS VCL Subscription was also extended with this new TMS Cloud Pack.

As Delphi & C++Builder XE3 was released in the beginning of September, we updated all our components with support for this new IDE and also released a new version of the TMS Pack for FireMonkey with support for the new Visual Livebindings in XE3. Another major release in September was TMS Scripter with new support to use the scripting engine from FireMonkey cross platform applications. Finally, a new VCL TAdvListEditor component was also released in September.

In October, a series of updates was released. TMS Component Pack with the new TAdvListEditor component, TMS Pack for FireMonkey with the new TTMSFMXCalendar and TTMSFMXCalendarPicker, TMS Smooth Controls with the new TAdvSmoothCalendarGroup. A major release was the update for TMS Flexcel and TMS Grid Filters with the new PDF export capability. This allows to generate high quality PDF files from Excel files or from data in TMS grid components. We also showed a very exclusive preview of Flexcel .NET code running on WinRT.

In November, there was yet another update for TMS Pack for FireMonkey, this time with native PDF export capabilities added for our FireMonkey cross platform grid. Also TMS Cloud Pack got a major update with new components for seamless access to cloud services Twitter, Facebook, Windows Live Calendars, Windows Live Contacts, Google Calendar, Wunderground weather service access and more. We showed a preview of the cloud components working on iOS as well.

And finally, in December, we released a major update for TMS WebGMaps with new support for showing directions, adding polygons, adding marker labels on the map, showing a directions list,... To conclude the year, TMS Component Pack 6.7 was released with 5 new VCL components and TMS T(DB)Planner got a free add-on TPlannerGCalendarExchange, TPlannerLiveCalendarExchange to import/export data from a Planner to cloud calendar services with the TMS Cloud Pack.

Events in 2012:

TMS software was also present on many events throughout 2012.

In March, Bruno Fierens gave presentations on TMS Components in Melbourne and Canberra at the ADUG Conference. In May, we were present at the Delphi Developer Day organised by Barnsten in the Netherlands and also in Frankfurt for the Delphi Developer Days organised by Marco Cantu & Cary Jensen. In June, Bruno Fierens gave a presentation on cross platform FireMonkey development on the cross platform conference in Moscow and also a session in Brussels on a Barnsten organised event. In September, TMS software was present at the CodeWay tour in Paris, France showing the latest developments for XE3. In October, Bruno Fierens received the Embarcadero MVP status and in November, TMS software was present at the Be-Delphi conference in Antwerp and Bruno Fierens gave a presentation about the FireMonkey grid.


I think we can conclude 2012 was a very busy year. We're already brainstorming on several things for 2013. We look forward to be at your service in 2013 and we look forward to hear about all your wishes, comments, feedback.

Cross platform grid with native .XLS/.XLSX/.PDF IO: TMS Grid for FireMonkey filters

$
0
0

Last summer we have released our powerful, feature-rich cross platform grid for FireMonkey. Shortly after we have also released a major update for TMS Flexcel. With TMS Flexcel v5.5 there is full native support for manipulating, creating, reading .XLS, .XLSX files as well as for generating .PDF files from cross platform FireMonkey applications.

Today we have released free bridge components between our grid for FireMonkey and TMS Flexcel to offer seamless export and import of .XLS, .XLSX files in our grid for FireMonkey and to generate .PDF files on Windows and Mac OS-X and soon also iOS from data in our grid. With these components, just dropping a few components on the form, interconnecting and 1 line of code allows you to load or create Excel files from our grid for FireMonkey or generate a PDF from the grid data.

Some of the features:

  • Fully native solution to import/export to .XLS, .XLSX without the need for Excel to be installed on Windows & Mac OS-X!
  • Fully native solution to export grids to .PDF, no extra tools or libraries required
  • Import/export wide range of cell properties: background color, alignment, font, wordwrap, ..
  • Import/export of cell images, multi cell images, checkboxes, hyperlinks
  • Import/export merged cells
  • Export of HTML formatted text in grid cells
  • Support for virtually unlimited nr. of columns & rows for import/export with new .XLSX file format

Note that the same solution is also available for our VCL grids, allowing you to generate .XLS/.XLSX/.PDF file seamlessly from TAdvStringGrid, TDBAdvGrid, TAdvColumnGrid or TAdvSpreadGrid with TMS Grid Filters.
Pictures say much more than words. Here are some screenshots from a sample app running on Windows and running on Mac OS-X and the .XLS file read and exported to .XLSX and .PDF

TMS Grid for FireMonkey running on Windows

XLSX file generated on Windows

PDF file generated on Windows

TMS Grid for FireMonkey running on Mac OS-X

XLSX file generated on Mac OS-X

PDF file generated on Mac OS-X


TMS Day: Apr 25, 2013

$
0
0

It's a fact, in cooperation with Be-Delphi, organizer of the Be-Delphi Delphi developer events, we will be organizing the first TMS Day on April 25, 2013 in Antwerp, Belgium.

The TMS Day will bring all TMS experts together: Bruno Fierens (Embarcadero MVP / CTO TMS software), Adrian Gallero (Product Manager TMS Flexcel), Wagner Landgraf (Product Manager TMS Aurelius, TMS Scripter, TMS Diagram, TMS Workflow) , Pieter Scheldeman (Tech lead FireMonkey products) for in-depth and personalized training on TMS Delphi & C++Builder products.

In order to ensure technical focus, participation during sessions and personalized information, the number of places will be very limited. We aim at 20 attendees with an absolute maximum of 25. Attendees will also be able to submit questions in advance so speakers can devote time to prepare topics for the TMS Day and sufficient time will be allocated for Q&A the day itself. In short, we want to make it a highly valuable event for TMS customers.

Some of the topics that will be covered are: TMS VCL grid, TMS FireMonkey components, TMS Aurelius, TMS Flexcel. Detailed program is being worked on.

Registration for the event will open soon. Prices will be kept at an absolute minimum just to cover organisational costs in the range of 200EUR/seat. Facilities for hotel rooms at the event will be available for international attendees. If you have any questions, don't hesitate to contact us.

Celebrating 18 years of Delphi

$
0
0

On February 14, Delphi turns 18 years. To celebrate 18 years of Delphi, we've searched through our archives and brought together quite some items that bring back memories and recall moments of these exciting 18 years.

Every day during February we'll publish a new picture of some item that defined something special in the history of Delphi on our Facebook page: http://www.facebook.com/tmssoftware.
We look forward to your comments and to hear how these items were relevant and affected your passion for Delphi.

We kick off on this first day of February with a series of TShirts from the big Borland conference held in various cities in the USA. Enjoy!







Crash Course TMS Aurelius – Getting Started

$
0
0

Even though TMS Aurelius provides extensive documentation, I sometimes receive requests to provide more examples, sample codes and explanations about how to accomplish some daily tasks. Thus, I will start a series of posts about how to use TMS Aurelius. Everything is already covered in the documentation, but here I will try not to provide complete, technical, “official” coverage of a feature, but plainly explain what it is for instead by giving real-world examples etc. - in summary, a different, hands-on way of showing things.

I will start right from the beginning. I want to show a small application using TMS Aurelius. Having an entity/a model class TCustomer, mapped as follows:

unit Customer;
interface
uses
  Aurelius.Mapping.Attributes;

type
  [Entity, Automapping]
  TCustomer = class
  private
    FId: integer;
    FName: string;
  public
    property Id: integer read FId write FId;
    property Name: string read FName write FName;
  end;

implementation
end.
This is a very small application that saves a customer instance derived from TCustomer in a SQLite database (I've removed try..finally blocks to simplify code):
program GettingStarted;

{$APPTYPE CONSOLE}

uses
  Aurelius.Drivers.Interfaces,
  Aurelius.Drivers.SQLite,
  Aurelius.Engine.DatabaseManager,
  Aurelius.Engine.ObjectManager,
  Aurelius.SQL.SQLite,
  Customer;

var
  Connection: IDBConnection;
  Manager: TObjectManager;
  Customer: TCustomer;
begin
  Connection := TSQLiteNativeConnectionAdapter.Create('test.db');
  Manager := TObjectManager.Create(Connection);
  Customer := TCustomer.Create;
  Customer.Name := 'First customer';
  Manager.Save(Customer);
  Manager.Free;
  WriteLn('Customer saved.');
  ReadLn;
end.
The purpose here is to show the very basics of how to start using Aurelius. What you need is:

1. A class to be persisted. This is your TCustomer class.

2. A mapping between the class and the database. This is accomplished by the [Entity] and [Automapping] attributes. In this case properties are mapped automatically to database columns, but you can set up a custom mapping if you want to. I will call object instances managed by Aurelius “entities”.

3. A connection to a database. This is the Connection variable, which implements an IDBConnection interface. In this case, we are connecting to a local SQLite database and need to make use of the TSQLiteNativeConnectionAdapter. I.e. we always need an adapter that connects the database world to the object world. In Windows you have to make sure sqlite3.dll is in a directory Windows can find. In Mac OS X and iOS, SQLite is already available.

4. An object manager to persist and manage your entities. The second line creates an object manager, which stores objects in the database specified by IDBConnection.

With all that, the code just instantiates the TCustomer object, fills its properties and saves it to the database. That is your first TMS Aurelius application!

As an additional note with regard to the code sample, if the file “test.db” does not exist, Aurelius will create it for you. However, you have to explicitly ask it to create the database structure for you, using the following code:
procedure CreateDatabase(Connection: IDBConnection);
var
  DBManager: TDatabaseManager;
begin
  DBManager := TDatabaseManager.Create(Connection);
  DBManager.BuildDatabase;
  DBManager.Free;
end;
This will create the file holding the database and the proper tables you need in database (in this case, table “Customer”).

Crash Course TMS Aurelius – AnyDAC or dbExpress?

$
0
0

In the example provided in the previous post, we saved a TCustomer instance in a local SQLite database which was accessed natively by TMS Aurelius. Let’s refactor that code a little bit:

procedure SaveCustomer(Connection: IDBconnection; CustomerName: string);
var
  Manager: TObjectManager;
  Customer: TCustomer;
begin
  Manager := TObjectManager.Create(Connection);
  Customer := TCustomer.Create;
  Customer.Name := CustomerName;
  Manager.Save(Customer);
  Manager.Free;
end;
With the procedure above, to save a customer in the SQLite database, we used a code similar to this (non-relevant lines removed):
uses
  {…}, Aurelius.Drivers.SQLite,   Aurelius.SQL.SQLite;

  Connection := TSQLiteNativeConnectionAdapter.Create('test.db');
  SaveCustomer(Connection, 'Jack');
What if we want to change it completely and save our objects in a MySQL database, using dbExpress to connect to it? That can be done this way:
uses
  {…}, Aurelius.Drivers.dbExpress,   Aurelius.SQL.MySQL;

  Connection := TDBExpressConnectionAdapter.Create(SQLConnection1, 'MySQL', False);
  SaveCustomer(Connection, 'Joe');
Note that besides the code that retrieves an IDBConnection interface, all other code remains the same. And that is true for any database you want to connect to, using any component, because all the object manager needs is an IDBConnection interface.

To retrieve that interface, we used a component adapter (TDBExpressConnectionAdapter, declared in unit Aurelius.Drivers.dbExpress) that takes our current dbExpress connection component (a TSQLConnection named SQLConnection1) and retrieves the interface. The second parameter indicates which database we are connecting to (more specifically, which SQL dialect Aurelius needs to use to execute SQL statements). That dialect, 'MySQL', is available after you use the unit Aurelius.SQL.MySQL. Finally, the third parameter (False) indicates that when the IDBConnection interface is destroyed, the adapted component (SQLConnection1) should not be destroyed. Optionally you can set it to true, which can be useful if you are creating the component only to be used by IDBConnection, so that the component is destroyed when interface is destroyed.

Now that Embarcadero has purchased AnyDac library and it will probably be provided natively in Delphi, using it instead of dbExpress will be a matter of changing a couple of lines:
uses
  {…}, Aurelius.Drivers.AnyDac,   Aurelius.SQL.MySQL;

  Connection := TDBExpressConnectionAdapter.Create(ADConnection1, False);
  SaveCustomer(Connection, 'Phil');
You might be missing the second parameter indicating that we are connecting to a MySQL database. This is because the adapters are able to automatically identify the database being connected to (Both TSQLConnection and TADConnection components have a property DriverName which Aurelius uses to identify the database). So the second parameter is optional.

Another thing is worth noting is that with this approach, code is very abstract and flexible. Aurelius doesn’t have any connection parameters that you need to configure like server name, password, etc. Everything is configured in the same components you already use. Any database connection configuration, including advanced ones, provided by each database access components, is still available.

So, if you don’t know if you should use AnyDac or dbExpress, you can use both and change them as you want to. Not only those, but at the current version (1.8) Aurelius also supports ADO components, Direct Oracle Access, ElevateDB, NexusDB, Absolute Database, FIBPlus, IBObjects, IBX components, SQL-Direct, UniDac and of course the native SQLite adapter. Aurelius documentation also provides the unit names and the name of adapter classes in its topic about component adapters.

As for the supported databases, you can use not only SQLite and MySQL, but also Firebird, MS SQL Server, Interbase, Oracle, PostgreSQL, Absolute Database, DB2, ElevateDB, NexusDB and SQLite. The names of units and SQL dialects are available in the topic “SQL Dialects” in documentation.

To conclude, I would like to mention that not only those databases and components are supported, but they are also extensively tested in each Aurelius release, with almost all possible combinations (dbExpress connecting to SQL Server, AnyDac connecting to PostgreSQL, and so on). You can check in the documentation which minimum versions of were used for tests for each combination. So most of little problems here are there with field types, SQL syntax, among other common problems that we usually find when switching components and databases are already solved, making Aurelius code effectively database/component agnostic, not only in theory, but also in practice.

Crash Course TMS Aurelius – Associations (Foreign Keys)

$
0
0

Besides mapping tables to classes and table columns to fields/properties, Aurelius also maps relationships (foreign keys) to object associations. One nice thing about Aurelius is that such associations are defined in a very simple way: just references to other objects. Consider the following classes with respective mapping:

type
  [Entity, Automapping]
  TCountry = class
  private
    FId: integer;
    FName: string;
  public
    property Id: integer read FId write FId;
    property Name: string read FName write FName;
  end;

  [Entity, Automapping]
  TCustomer = class
  private
    FId: integer;
    FName: string;
    FCountry: TCountry;
  public
    property Id: integer read FId write FId;
    property Name: string read FName write FName;
    property Country: TCountry read FCountry write FCountry;
  end;
Note that TCustomer has an association to TCountry, meaning that every customer has a country associated to it. The following code should how you would save a TCustomer object with an associated TCountry object:
function CreateCustomerWithCountry(Manager: TObjectManager): integer;
var
  Customer: TCustomer;
  USACountry: TCountry;
begin
  USACountry := TCountry.Create;
  USACountry.Name := 'USA';
  Customer := TCustomer.Create;
  Customer.Name := 'John';
  Customer.Country := USACountry;
  Manager.Save(Customer);
  Result := Customer.Id;
end;
Very simple and straightforward. Note that we didn't need to save Country object - when Customer is saved, Country is automatically saved because it's associated to it (this is the default behavior of automapping. You can fully configure the mapping to avoid Country to be saved automatically, if you want to).

It's also very simple to retrieve an object and its associations from database. Consider the following code that takes a customer id and returns the name of the country associated with the customer:
function GetCountryNameFromCustomer(Manager: TObjectManager; CustomerId: integer): string;
var
  Customer: TCustomer;
begin
  Customer := Manager.Find<TCustomer>(CustomerId);
  if Customer <> nil then
    Result := Customer.Country.Name
  else
    Result := '';
end;
The code retrieves a TCustomer object instance based on the id (such Find method will be topic for another blog post). To obtain the name of the country, all we have to do is to get the associated TCountry object instance (through the TCustomer.Country property) and return its Name property. Also very simple. When TCustomer instance was retrieved, its associated objects were also retrieved. You can also fully configure this, and you can even set up things so that the TCountry object is only retrieved when needed (also a topic for another post).

Associations are a core feature of any ORM framework and this small example is a very simple one. Aurelius has many features related to associations, many ways of dealing with them, saving, retrieving, etc. But the purpose of this blog post is just to explain the concept. Feel free to ask questions in comment about what else you would like to be better explained in a future blog post.

To make it even more clear, I will post here the SQL statements executed by Aurelius when the code above was executed, so you can easily relate the objects with the underlying database. The statements used here were executed in an SQL Server database (syntax will be different if using another database).

The following statements were executed to create the tables so you can have an idea of the database structure (code to create the database is not explicit in this post):
CREATE TABLE COUNTRY (
  ID INTEGER IDENTITY(1,1) NOT NULL,
  NAME VARCHAR(255) NOT NULL,
  CONSTRAINT PK_COUNTRY PRIMARY KEY (ID));

CREATE TABLE CUSTOMER (
  ID INTEGER IDENTITY(1,1) NOT NULL,
  NAME VARCHAR(255) NOT NULL,
  COUNTRY_ID INTEGER NULL,
  CONSTRAINT PK_CUSTOMER PRIMARY KEY (ID));

ALTER TABLE CUSTOMER ADD CONSTRAINT 
  FK_CUSTOMER_COUNTRY_COUNTRY_ID FOREIGN KEY (COUNTRY_ID) REFERENCES COUNTRY (ID)
When saving the TCustomer object instance (function CreateCustomerWithCountry), the following statements were executed (the content of parameters is displayed):
INSERT INTO COUNTRY (NAME) VALUES (:A_NAME);
A_NAME = "USA" (ftString)

SELECT CAST(IDENT_CURRENT('COUNTRY') AS INT);

INSERT INTO CUSTOMER (
  NAME, COUNTRY_ID)
VALUES (
  :A_NAME, :A_COUNTRY_ID);

A_NAME = "John" (ftString)
A_COUNTRY_ID = "1" (ftInteger)

SELECT CAST(IDENT_CURRENT('CUSTOMER') AS INT)
Finally, and the most interested one in my opinion, this is the SQL statement executed to retrieve back the TCustomer obejct instance. Note that in this example two different TObjectManager objects were used to force the SELECT execution. If a single manager had been used, manager would have retrieved the object directly from manager and would not need to execute an extra SELECT statement to retrieve the data.
SELECT A.ID AS A_ID, A.NAME AS A_NAME, A.COUNTRY_ID AS A_COUNTRY_ID, B.ID AS B_ID, B.NAME AS B_NAME
FROM CUSTOMER A
  LEFT JOIN COUNTRY B ON (B.ID = A.COUNTRY_ID)
WHERE  A.ID = :p_0

p_0 = "1" (ftInteger)



Crash Course TMS Aurelius – Blobs

$
0
0

Using blobs in Aurelius is very straightforward and yet very powerful. In summary, all you have to do is declare your field/property as TBlob (declared in unit Aurelius.Types.Blob.pas). This is enough to map it to an existing blob field in your table, and you will be able to save/load the blob content is several many ways. Consider the following mapping:

  [Entity, Automapping]
  TCustomer = class
  private
    FId: integer;
    FName: string;
    FDocument: TBlob;
    [Column('Photo', [TColumnProp.Lazy])]
    FPhoto: TBlob;
    [Column('Descr_Field', [], 65536)]
    FDescription: string;
  public
    property Id: integer read FId write FId;
    property Name: string read FName write FName;
    property Document: TBlob read FDocument write FDocument;
    property Photo: TBlob read FPhoto write FPhoto;
    property Description: string read FDescription write FDescription;
  end;
We have declared three blob properties in our class: Document, Photo and Description. I have used those to show slightly different ways of using blobs. Document and Photo are declared as TBlob, which is the recommended way. Alternatively, Description is declared as string, but manually mapped to database using a size greater than 65535. This tells Aurelius to also consider this field as a blob (memo/cblob to be more specific) instead of VarChar. You could also declare the property as a dynamic array of byte, but it's not recommended, since you gain nothing from doing it. Note that I have also used an unusual field name for Description (Descr_Field) just to show you how manual mapping works.

There is another interesting feature about blobs: Photo is declared as lazy (TColumnProp.Lazy). This indicates that Aurelius will not bring the blob from database when Customer data is retrieved. The blob is only retrieved when your code explicitly reads the content of Photo property.

The following code shows different ways of dealing with blobs (saving and loading):
function SaveCustomerWithBlobs(Manager: TObjectManager): integer;
var
  Customer: TCustomer;
begin
  Customer := TCustomer.Create;
  Customer.Name := 'John';

  Customer.Photo := TFile.ReadAllBytes('picture.bmp');
  Customer.Document.AsBytes := TFile.ReadAllBytes('document.pdf');
  Customer.Description := TFile.ReadAllText('description.txt');

  Manager.Save(Customer);
  Result := Customer.Id;
end;

procedure LoadCustomerAndExportBlobs(Manager: TObjectManager; CustomerId: integer);
var
  Customer: TCustomer;
begin
  Customer := Manager.Find<TCustomer>(CustomerId);

  TFile.WriteAllText('description2.txt', Customer.Description);
  TFile.WriteAllBytes('document2.pdf', Customer.Document);
  TFile.WriteAllBytes('picture2.bmp', Customer.Photo.AsBytes);
end;
As you can see, you can use TBlob.AsBytes property explicitly, or rely on the implicit conversion to TBytes. You could also use AsString property and streams:
  Customer.Photo := TBlob.Create(TFile.ReadAllBytes('picture.bmp'));
  Customer.Document.AsString := 'Some document';
  Stream := TFile.Open('picture.bmp', TFileMode.fmOpen);
  Customer.Photo.LoadFromStream(Stream);
  Stream.Free;
TBlob type also offers methods like IsNull, Clear, and also provides direct access to raw data to improve performance if needed.

As a final note: we have added TColumnProp.Lazy to the Photo blob. We can verify if the blob is loaded using the Loaded property. We can change LoadCustomerAndExportBlobs function to check it:
  Customer := Manager.Find<TCustomer>(CustomerId);

  Assert(Customer.Document.Loaded);
  TFile.WriteAllBytes('document2.pdf', Customer.Document);

  Assert(not Customer.Photo.Loaded);
  TFile.WriteAllBytes('picture2.bmp', Customer.Photo.AsBytes);
  Assert(Customer.Photo.Loaded);
After TCustomer instance is loaded, Document property is loaded, but Photo is not loaded, because we set it as lazy. After we access content of Photo to save it to picture2.bmp file, Photo.Loaded becomes true. You can also see it happening when you check the generate SQL statements:
CREATE TABLE CUSTOMER (
  ID INTEGER NOT NULL,
  NAME VARCHAR(255) NOT NULL,
  DOCUMENT BLOB,
  Photo BLOB,
  Descr_Field BLOB SUB_TYPE TEXT,
  CONSTRAINT PK_CUSTOMER PRIMARY KEY (ID));

CREATE GENERATOR SEQ_CUSTOMER;

SELECT GEN_ID(SEQ_CUSTOMER, 1)
FROM RDB$DATABASE;

INSERT INTO CUSTOMER (
  ID, NAME, DOCUMENT, Photo, Descr_Field)
VALUES (
  :A_ID, :A_NAME, :A_DOCUMENT, :A_Photo, :A_Descr_Field);

SELECT A.ID AS A_ID, A.NAME AS A_NAME, A.DOCUMENT AS A_DOCUMENT, A.Descr_Field AS A_Descr_Field
FROM CUSTOMER A
WHERE  A.ID = :p_0;

SELECT A.Photo As f0_
FROM CUSTOMER A
WHERE  A.ID = :p_0;
The first statement shows how the table is created (Firebird in this example). The INSERT statement creates the customer in the database, and saves the blobs. The first SELECT statement retrieves only Document and Description, but not Photo. The last SELECT statement is executed later, to retrieve the content of Photo field, only when the content of Photo property is read from code.

Crash Course TMS Aurelius – Inheritance and Polymorphism

$
0
0

Inheritance is one of my favorite features in Aurelius. One of benefits of using an ORM is abstracting the SQL and start thinking (almost) purely in OOP. Inheritance and polymorphism are fundamental features of Object-oriented programming, and if when designing your model you can't use it, then the "object-relational" mapping would just become a simple "property>column" mapping in the end.

Aurelius allows you to build a class hierarchy that can be persisted, and provides you with two strategies to persist it: joined tables and single table. The former will create a different table for each class and add the proper relationships, and the later will save the whole hierarchy in the same table. You can learn more about it reading the topic "Inheritance Strategies" in documentation.

Let me illustrate how it works. Considering the following classes and mapping:

type
  [Entity, Automapping]
  [Inheritance(TInheritanceStrategy.JoinedTables)]
  TPerson = class
  private
    FId: integer;
    FName: string;
  public
    property Id: integer read FId write FId;
    property Name: string read FName write FName;
  end;

  [Entity, Automapping]
  TEmployee = class(TPerson)
  private
    FSalary: Currency;
  public
    property Salary: Currency read FSalary write FSalary;
  end;
Note that mapping is also very straightforward, all you need to do is specify the strategy to be used in the base class of your hierarchy. Now you can save your objects in the same way we did in previous posts:
function SavePerson(Manager: TObjectManager): integer;
var
  Person: TPerson;
begin
  Person := TPerson.Create;
  Person.Name := 'John Person';
  Manager.Save(Person);
  Result := Person.Id;
end;

function SaveEmployee(Manager: TObjectManager): integer;
var
  Employee: TEmployee;
begin
  Employee := TEmployee.Create;
  Employee.Name := 'James Employee';
  Employee.Salary := 1999.99;
  Manager.Save(Employee);
  Result := Employee.Id;
end;
After calling the above methods, we have one TPerson object and one TEmployee object persisted in the database. We can use the following code to retrieve them using the generated id's:
procedure OutputPerson(Person: TPerson);
begin
  if Person <> nil then
    WriteLn(Format('Class: %s; Name: %s', [Person.ClassName, Person.Name]))
  else
    WriteLn('nil');
end;

procedure OutputEmployee(Employee: TEmployee);
begin
  if Employee <> nil then
    WriteLn(Format('Class: %s; Name: %s; Salary: %s',
      [Employee.ClassName, Employee.Name, FloatToStr(Employee.Salary)]))
  else
    WriteLn('nil');
end;

procedure CheckPersonAndEmployee(Manager: TObjectManager; PersonId, EmployeeId: integer);
var
  Person: TPerson;
  Employee: TEmployee;
begin
  Person := Manager.Find<TPerson>(PersonId);
  OutputPerson(Person);
  Person := Manager.Find<TPerson>(EmployeeId);
  OutputPerson(Person);
  Employee := Manager.Find<TEmployee>(EmployeeId);
  OutputEmployee(Employee);
  Employee := Manager.Find<TEmployee>(PersonId);
  OutputEmployee(Employee);
end;
This is what we get as the output:
Class: TPerson; Name: John Person
Class: TEmployee; Name: James Employee
Class: TEmployee; Name: James Employee; Salary: 1999.99
nil
Now you see polymorphism in action. The first two Find calls ask for a TPerson object. It happens that the first id is a TPerson object indeed, but the second is an id for a TEmployee object. Both are retrieved because a TEmployee is a TPerson. Also note that the retrieved object in second Find is actually a TEmployee object.

The last two Find calls ask for a TEmployee object. When the EmployeeId is provided, the correct TEmployee object is retrieved. But when we ask for a TEmployee object passing PersonId as Id, nil is returned - although the object is in database with that id, it's not returned because the object is not a TEmployee, but only a TPerson.

As in the previous posts, I will provide here some SQL statements generated by Aurelius, for a better understanding. When using joined tables strategy, Aurelius will create the following database structure (SQL Server syntax):
CREATE TABLE PERSON (
  ID INTEGER IDENTITY(1,1) NOT NULL,
  NAME VARCHAR(255) NOT NULL,
  CONSTRAINT PK_PERSON PRIMARY KEY (ID));

CREATE TABLE EMPLOYEE (
  ID INTEGER NOT NULL,
  SALARY NUMERIC(20, 4) NOT NULL,
  CONSTRAINT PK_EMPLOYEE PRIMARY KEY (ID));

ALTER TABLE EMPLOYEE ADD CONSTRAINT 
  FK_EMPLOYEE_PERSON_ID FOREIGN KEY (ID) REFERENCES PERSON (ID);
Each class will have its data saved in a different database, and retrieving a TEmployee object would execute the following statement:
SELECT A.ID AS A_ID, A.SALARY AS A_SALARY, B.ID AS B_ID, B.NAME AS B_NAME
FROM EMPLOYEE A
  LEFT JOIN PERSON B ON (B.ID = A.ID)
WHERE  B.ID = :p_0
To conclude this post, let's change the strategy to single table. This will make the mapping look like this (Salary property has to be nullable because all data will stay in a single table):
  [Entity, Automapping]
  [Inheritance(TInheritanceStrategy.SingleTable)]
  [DiscriminatorColumn('PERSON_TYPE', TDiscriminatorType.dtString)]
  [DiscriminatorValue('Person')]
  TPerson = class
  private
    FId: integer;
    FName: string;
  public
    property Id: integer read FId write FId;
    property Name: string read FName write FName;
  end;

  [Entity, Automapping]
  [DiscriminatorValue('Employee')]
  TEmployee = class(TPerson)
  private
    FSalary: Nullable<Currency>;
  public
    property Salary: Nullable read FSalary write FSalary;
  end;
Code will be exactly the same. Database structure will become just this:
CREATE TABLE PERSON (
  ID INTEGER IDENTITY(1,1) NOT NULL,
  NAME VARCHAR(255) NOT NULL,
  PERSON_TYPE VARCHAR(30) NOT NULL,
  SALARY NUMERIC(20, 4) NULL,
  CONSTRAINT PK_PERSON PRIMARY KEY (ID));
and this is how an employee is retrieved from database:
SELECT A.ID AS A_ID, A.NAME AS A_NAME, A.PERSON_TYPE AS A_PERSON_TYPE, A.SALARY AS A_SALARY
FROM PERSON A
WHERE A.PERSON_TYPE = :p_1
 AND A.ID = :p_0

p_0 = "1" (ftInteger)
p_1 = "Employee" (ftString)


Visual Data Binding using TAureliusDataset

$
0
0

When dealing with Aurelius and any ORM framework, one common task is to build a graphical user interface to edit/display the data. Delphi users are used to the TDataset component, which not only retrieves data from the database but also act as middle layer between the data and visual controls. When using Aurelius, you don't use any TDataset descendant to directly retrieve data - all business data are objects that are retrieved by Aurelius itself.

To bind your objects to visual controls, you could use the new Visual Live Bindings feature. But Aurelius also provides an additional way of doing that - you can use TAureliusDataset, a TDataset descendant which behaves as any other TDataset - the only difference is that entity objects are the "data" for this dataset.

Consider the following code:

var
  Customers: TList<TCustomer>;
  Dataset: TAureliusDataset;
{...}
  Customers := Manager.Find<TCustomer>.List;
  Dataset.SetSourceList(Customers);
  Dataset.Open;
The first line retrieves a list of TCustomer objects from the database. The second line tells the dataset that its data is coming from Customers list, and then third line just opens the dataset. Now if we want to display our data in a TDBGrid control, for example, we just do it the way we are used to: link the grid to a TDatasource, then link the datasource to the TAureliusDataset. Your customers will be displayed in the grid.

TAureliusDataset automatically maps each property to a field in dataset. So if your customer is declared like this:
type
  TCustomer = class
  {...}
    property CustName: string read FCustName write FCustName;
you will be able to read/write the property using this code:
CurrentName := Dataset.FieldByName('CustName').AsString;
Dataset.Edit;
Dataset.FieldByName('CustName').AsString := CurrentName + ' - sufix';
Dataset.Post;
You could also edit a single object directly, without needing to retrieve a list. This will also work if you just want to edit properties of a single object:
SpecificCustomer := Manager.Find<TCustomer>(CustomerId);
Dataset.SetSourceObject(SpecificCustomer);
Dataset.Open;
Some could say that you could use the new Visual Live Bindings. Yes, of course, you can. This is just another option, with pros and cons. But it has some interesting/different things:

1. You can use existing data-aware controls. Delphi is now 18 year-old. There are numerous existing controls that support TDataset, but not live bindings. Data-aware grids, planners, controls, etc.. All of those can be used and be bound to the objects.

2. TDataset provides a temporary cache/buffer. This means that until you effectively Post, objects are not changed. Remember this acts as a TDataset. While the dataset is being edited and field contents are updated, only the internal dataset buffer is updated. Data is effectively saved in the objects (the "data") only after Post. This gives you great flexibility when you need to build user interfaces where user can cancel changes, or only update data when clicking "Ok". If you use live bindings, you would have to do something else to achieve such behavior.

Not only that, TAureliusDataset is not just a property->field mapper. It's really powerful. Here is a list of many things TAureliusDataset can do and features it supports (I might write about these in a future post):

  • Fetch-on-demand (will talk about this in a future post)
  • Offline, paged fetch-on-demand (same as above)
  • Sub-properties (properties of associated entities)
  • Entity fields (fields representing an association)
  • Dataset fields (master-detail)
  • Supports inheritance/polymorphism (list of objects of different classes)
  • Enumerated types
  • Lookup fields
  • Locate/Lookup methods
  • Filtered data
  • Calculated fields
  • Design-time support
To conclude, here are two screenshots that illustrate how you can use TAureliusDataset at design-time:








Preparation for TMS Day April 25 in full force

$
0
0

Preparation of the TMS Day scheduled for April 25 is in full force now. We’re working hard on every little detail, going from papers for the hand-outs, evaluation forms, training certificates, a gift attendees will receive to of course most importantly the content of the sessions itself. The tentative day scheme will be:

Effectively using Flexcel for VCL & FireMonkey by Adrian Gallero, Flexcel architect

Adrian Gallero will give an allround overview of Flexcel and how the product can be used most effectively.

  • Intro: Technical background why OLE Automation doesn't cut it and why to use Flexcel instead
  • Details about the Flexcel API and using the APIMate tool to be more productive in using the Flexcel API and some information about what is not in APIMate.
  • Using Flexcel based reports and how they can be used for much more than reports. Demo of an entire application created using Flexcel reports.
  • Getting the most out of Flexcel rendering: exporting to HTML, PDF, printing, etc and how to make an Excel file so it prints fine.
  • Getting the most performance out of Flexcel in particular using the virtual mode, a unique feature in FlexCel
  • Using Flexcel with FireMonkey and using it in iOS applications with demo of Flexcel in the newest Delphi version and its new compiler for iOS.

Learning Aurelius by example with DataSnap REST Servers by Wagner Landgraf, Aurelius architect

A step by step presentation how the Delphi ORM Aurelius can be used to create an issue tracker from scratch.
  • Aurelius basic mapping/connection between Delphi classes and database tables
  • A look at the memory management concept of the object manager
  • The TAureliusDataset concept to bind entities automatically to controls
  • Overview of specific TAureliusDataset features: enumname, subproperties, lookup fields, entity fields, master-detail
  • Using & implementing queries from Aurelius
  • Switching between database servers, or how easy Aurelius makes it for you to switch database servers
  • Creating REST server and using Aurelius JSON serializer/deserializer to send/receive entities
  • Accessing Aurelius data from JavaScript

TMS VCL Components tips & tricks by Bruno Fierens, VCL component architect

Bruno Fierens will walk through several TMS VCL components showing advanced techniques.
  • Understanding and using filtering in TAdvStringGrid: various automatic filtering techniques from the user interface, logical operations between filter conditions
  • Using custom controls as inplace editors in the grid
  • Creating a custom styler & custom autocompletion for TAdvMemo
  • Using form-wide and application-wide styles for TMS VCL components
  • Various scenarios and use cases for TWebUpdate, the automatic application updater
  • Metro style UI’s with TMS Components

TMS FireMonkey components for business applications by Pieter Scheldeman, FireMonkey component architect

Pieter Scheldeman will unveil the architecture and features of the FireMonkey tableview and grid component.
  • Architecture of the tableview component, features, how its items can be customized and important performance aspects
  • Architecture of the grid component, design decisions, features
  • Using LiveBinding with the grid component
  • Customization of the grid via editing its style
  • Creating and using custom cell classes for the grid
  • TMS FireMonkey components for iOS, sneak peek at newest developments specifically for iOS with the new Delphi version

Connecting Delphi application to the cloud with the TMS Cloud Pack by Bruno Fierens, VCL component architect

Discover how your applications can benefit from consuming cloud services, getting data from the cloud, putting information in the cloud.
  • Consuming cloud services from Delphi applications
  • The OAuth challenge
  • TMS Cloud Pack component architecture and component overview
  • Connecting the VCL Planner components to the Google Calendar and Windows Live services
  • Creating a custom cloud service access component using the TMS Cloud Pack framework


Other than the sessions, the entire TMS team will be available all day at your disposition to listen to your project specific questions and suggestions. For location, prices and reservation for the last 2 seats, see www.be-delphi.com

Feature request voting system on TMS website ... 9 months later

$
0
0

Since July 2012, the website offers feature request voting capabilities for registered TMS customers. New request can be added, requests can be filtered and you can vote on requests from other customers.Now 9 months later, we’ve made an evaluation of our feature request voting system. The results:

115 requests
We’ve received 115 requests from our customers.
Components can be used in many different ways, so you are in the best position to determine which capabilities a control must have to be 100% functional in your application. Please keep on adding new requests, and help us to further extend our range of components offerings and enhance the feature set and quality of our components. All your suggestions are welcome to further improve our existing components. And of course suggestions for new components are also welcome.

Your opinion is highly appreciated and will be taken in account!

26 implemented requests
And now the good news, 26 requests have been implemented.
Several new capabilities and features were added to existing components but most importantly we've added 3 completely new components!

YOUR requests resulted in following new components:

  • A new syntax highlighting memo control for FireMonkey: TTMSFMXMemo
  • A new VCL mapping component to integrate, display & control OpenStreetMaps in VCL Windows applications : TMS WebOSMaps
  • A new OpenGL 3D multi-serie chart component: TMS TAdvChartView3D

Overview new capabilities and features in existing components:
654 votes on requests
Not only new requests were added to our list, but you also voted on existing requests. This helps us to set the right priorities and goals.

If you want to have a look at the list with requests or you want to add a new request, the overview page can be found here: http://www.tmssoftware.com/site/fr.asp
We at TMS are always trying to improve our products and bring them to a new level. We plan to add several new features in upcoming releases, our team is already busy with implementing your requests!


Chrome-style application setting persistence/synchronisation with DropBox

$
0
0

Google Chrome has the very interesting feature to be able to store its settings via your Google account. This means that when you install Chrome on a different machine and associate it with your Google account, it will automatically "inherit" all settings of your other configs. Not only Google Chrome does this but increasingly Windows desktop applications and tools use online storage to offer the convenience of having identical configurations on multiple machines. Another excellent example is the Google Chrome extension Speed Dial 2 that can synchronize its settings among machines this way.

Now, nothing prevents us from doing the same for a Delphi application and with the TMS Cloud Pack, it becomes very simple to use a cloud storage service such as DropBox to allow the user to persist his settings in a DropBox folder and have these settings synchronised between different machines this way.

We have created a very rudimentary example to demonstrate the concept. The settings from the sample application are simply the contents of a listbox where items can be added or removed via the application. We save the settings as a simple text file and load this at application startup time from a DropBox account and save it back to DropBox when the application closes.



All we need to do is drop an instance of TAdvDropBox from the TMS Cloud Pack on the form, set the DropBox application key and secret (that can be obtained for free after registering with DropBox) load the access tokens and when the access tokens do not yet exist, get an access token for DropBox via an authentication/authorization step and call one line of code to download the settings file.
When the application closes, we simply upload the settings again with one call.
When we save the access token (here for reasons of simplicity of the demo in an INI file), this one time authentication/authorization with the DropBox account by the user is sufficient (which is similar for Google Chrome settings synchronisation too by the way)
The code with information in comments for application startup becomes:
procedure TForm1.FormCreate(Sender: TObject);
var
  acc: boolean;
begin
  Dirty := false;
  // set the DropBox application key & secret here that is provided by DropBox
  // for free when registering via: https://www.dropbox.com/developers/apps
  AdvDropBox1.App.Key := DropBoxAppkey;
  AdvDropBox1.App.Secret := DropBoxAppSecret;
  // Use simple INI file storage for the access token that DropBox will give
  AdvDropBox1.PersistTokens.Location := plIniFile;
  AdvDropBox1.PersistTokens.Key := '.\sync.ini';
  AdvDropBox1.PersistTokens.Section := 'DropBox';

  if AdvDropBox1.App.Key <> '' then
  begin
    // Try to load an access token if it was already retrieved earlier
    AdvDropBox1.LoadTokens;
    // Test if the token is working
    acc := AdvDropBox1.TestTokens;
    if not acc then
      // If the token was not working try to refresh it
      acc := AdvDropBox1.RefreshAccess;

    if not acc then
    // No token was found or existing token is not valid, so authenticate/authorize via DropBox
      AdvDropBox1.DoAuth
    else
    // Download the settings from DropBox and apply
      LoadSettings;
  end;
end;
When the application is first used, there is no access token to download a file from the users DropBox account and in this condition, first the DropBox authentication login screen is shown:


followed by the authorization screen:


When the access token is obtained the first time, the TAdvDropBox component triggers OnReceivedAccessToken from where the token is first saved and then the settings downloaded and applied:
procedure TForm1.AdvDropBox1ReceivedAccessToken(Sender: TObject);
begin
  AdvDropBox1.SaveTokens;
  LoadSettings;
end;

When the application closes, we can simply save the settings in the Form's OnClose event via:
procedure TForm4.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  listbox1.Items.SaveToFile(GetSettingsFileName);

  if Dirty and AdvDropBox1.TestTokens then
    AdvDropBox1.Upload(nil,GetSettingsFileName);
end;

The full source of the sample can be download here. With the TMS Cloud Pack offering similar access also to Microsoft Skydrive and Google Drive, it becomes very easy to change to the cloud storage of your preference by swapping to the component TAdvGDrive or TAdvSkyDrive.

TMS Aurelius in your iPhone

$
0
0

We have just released TMS Aurelius 2.1 with XE4 support. This "small" release took a little longer, but with a good reason for that: thanks to the new iOS compiler provided in Delphi XE4, now TMS Aurelius supports iOS devices, in addition to the already supported Win32, Win64 and OS X platforms!

As you might already know, the new iOS compiler has some different concepts than the traditional Win32 compiler we are used to. Automatic reference counting for objects and zero-based strings are the main ones, and also the fact that pointers usage is discouraged now.

But for those considering using this new iOS compiler, there is good news. Personally, I was surprised, in a positive way, how backward compatible it is. Of course it depends on your code. If it has heavy pointer usage, lots of low-level hacks, etc., you might have a lot of work to do. But other than this, there is a good chance that you code will work smoothly on iOS. I can speak for Aurelius. It can be considered a very new TMS product (a little more than one year passed since 1.0 release in January, 2012) so it uses several new language features like generics, new RTTI, among other recent additions that helps the code to be very clean, well structured and with almost no pointer usage. Making most of it to compile to iOS required minimum changes, and it worked fairly well (of course, all our tests passed, in both iOS simulator and iOS device).

I said it was easy to compile "most of it" because the only exception was TAureliusDataset. Not that it was a nightmare, but without it, the other parts of TMS Aurelius would be compiling and running on iOS in a matter of minutes. But TAureliusDatset of course descends from TDataset which is a code that heavily uses pointers, internal buffers, etc.. So it required a some effort to convert.

All in all, you can have your TMS Aurelius code working on iOS, with all existing features, including TAureliusDataset and native SQLite support. And the best part is that you can use it the same way you do in Delphi: since TMS Aurelius already manages the memory in VCL/FMX applications (you usually don't have to worry about destroying objects retrieved from the database), you will have the same behavior in iOS.


FlexCel 6 and Working with files in iOS

$
0
0

Introducing FlexCel 6


As you might have noticed, we've just released FlexCel 6 for Delphi with iOS support (FlexCel 6 for .NET is coming soon now and it will also have iOS support). We've taken our time to release it because we didn't want this to be a "kind of works in iOS" release, but to be a fully working, usable solution to deal with xls, xlsx, pdf and html files in iOS.

And I think we've gotten it right. FlexCel 6 can smoothly generate xls, xlsx, native pdf and html files, open xls and xlsx files, print and preview xls and xlsx, and all in iOS. To get the maximum performance and compatibility, the xls/x viewer uses direct CoreGraphics calls in iOS or OSX instead of FireMonkey canvas calls to render the files, in a way that they can show in your phone as they show in a desktop machine. All the page is rendered to an off-screen buffer, which is then shown in the completely FireMonkey-native and styleable TFlexCelPreviewer control. You get the best of both worlds: You have a native FireMonkey control that can interoperate nicely with the rest of the FireMonkey controls in your form, and can be styled as any other FireMonkey control, but under the hood, the control is doing native calls to iOS for rendering to get the best results.

And this isn't a cut-out version, almost everything you can do in your desktop machine you can do it on your phone. The only thing that you can't do in iOS is to read or write encrypted xlsx files, because adding crypto support would require that you declare crypto routines when submitting to the app store, and you would need to comply with all the export regulations. So by the moment we are leaving encryption out, but virtually everything else is in. (Encryption might come later as an optional, use-it-at-your-own-risk, addin). Of course there are limitations with respect to the Desktop version; in a phone you have less memory, a less capable CPU, and everything will go slower. But it works and works quite well. (We've spent also a lot of time optimizing memory usage in FlexCel 6, so it works better in phones, but also works better in your desktop machine).

Working with files in iOS


As explained above, if we don't count encryption and the slower cpu/less memory, FlexCel for iOS is virtually identical to FlexCel for Windows. It is the same code, the same calls, everything, and this makes porting simple. But there is a big difference: Working with files.
In iOS you can't read or write files outside the folder where your application is. Which makes the system much more secure (now this little game that you downloaded can't read all your mail and documents and send them to some server overseas), but on the other hand, makes it much more difficult to interoperate and share files between apps.

For FlexCel 6 we were not happy to just give you a library to read and write xls/x files, and so we've spent a lot of time investigating how to make the workflow simpler, how to workaround the bugs currently in XE4, and how to make your app interop with the others. So you can read an xls file from DropBox, modify it, and send it back to DropBox. Or email it.

And I think all the information we collected might be very useful not only for FlexCel users, but for everybody who needs to deal with files in iOS and Delphi.
So I will write a link to the two documents explaining how to deal with files in iOS here. Those two documents also come as expected when you install FlexCel: trial or registered.

1)Using FlexCel with iOS.
This is a conceptual document, and explains how to import and export the files from Delphi. Once again, while it is targeted to FlexCel users, most of the content applies even if you are not a FlexCel user.

2)FlexView tutorial
This is a step-by-step tutorial showing how to start with an empty application and make it interoperate with the other apps in your phone. The full source code for the app created in the tutorial is available in the FlexCel trial or registered distributions.

Bonus track: Video of FlexCel 6 working in an iPhone

While FlexCel is focused mostly in the non visual stuff, here is an small example of how the FlexView application in the tutorial above works in an actual device:
FlexCel 6 working on an iPhone:
http://youtu.be/UbNbPRkle_A

FlexCel 6 working on an iPad(*):
http://youtu.be/Km7zDDBxIMM

(*)Note: In the iPad example we are showing a chart. Currently, charts are rendered in xls, but not in xlsx.

TMS iCL: an introduction

$
0
0

In this article, we'll try to explain what TMS iCL is, what it isn't and why it can matter for your Delphi applications for iOS.


What is TMS iCL?
TMS iCL is a component library. It stands for iOS Component Library. It is a Delphi component library and as such, it is accessible as Delphi objects with properties, methods, events. The components are in fact wrappers around the iOS operating system level defined controls, for now mostly visual controls. In iOS Objective C terminology, UIView types. In this respect it is very similar to the Delphi VCL standard components like TEdit, TButton, TListbox ... etc. These VCL controls are tiny wrappers around the Windows operating system controls like EDIT, BUTTON, LISTBOX ... These standard Delphi controls do not do much more than present the Windows user interface controls as Delphi classes with properties, method and events. In the case of TMS iCL, these controls are usable from a FireMonkey form, just like VCL controls are usable from a standard VCL form.

How is an iCL control different from a FireMonkey control?
As iCL controls wrap iOS operating system controls, the entire look & feel as well as feature set of the iCL control is defined by what the iOS control offers. As such, TMS iCL controls are not style-able FireMonkey controls. In fact, an iCL control has very little in common with a FireMonkey control. Where a FireMonkey control look is defined by a (customizable) hierarchy of style elements (such as rectangles, labels, etc...) that are rendered in a Quartz or OpenGL canvas by the FireMonkey framework itself, an iCL control is rendered by the iOS operating system. Where a FireMonkey control's keyboard and mouse (touch) handling is in fact handled by redirected FireMonkey form level keyboard and mouse (touch) handling, the operating system itself handles all this for the iCL control, or better the underlying UIView or similar control. The main result is that an iCL control is not style-able or extensible in the same way as a FireMonkey control and you won't find the typical keyboard or mouse (touch) events as in a FireMonkey control.

iCL and FireMonkey coexist
What is nice is that iCL controls and FireMonkey controls coexist fine on the same FireMonkey form. In fact, we have even created a FireMonkey form wrapper that makes it possible to embed or use these FireMonkey forms with iCL controls (like a tableview detail or popover for example)

Why use iCL controls?
Two very important considerations might make you consider using iCL controls: performance and consistency. While a FireMonkey control always remains an emulation (by means of a style element hierarchy) of an operating system control, the iCL control is the real thing. The iCL control is not just pixel-perfect, whatever the real meaning of pixel-perfect might be, it is just 100% perfect as it is the real control. The same applies to the behavior. Interaction with touch is not just simulated by FireMonkey animations inside in the control, but is the exact Apple defined behavior. Think about the exact scroll bounce effect, the exact glow on touch, the exact slide animation. With this also comes performance. iCL exposes the performance of the operating system control that Apple has maximised and fine-tuned. The best example of this is the tableview that remains extremely fast and responsive, irrespective of the number of items or the complexity of what is displayed in tableview items. A final consideration is that an iCL control is by definition operating system version agnostic. It has by definition always the exact appearance of the operating system control and makes the application that uses them consistent with iOS. An example of this is the switch control where Apple modified the appearance slightly between iOS 5 and iOS 6. The iCL control will always have the right look. As iOS 7 is expected to have a different look again with Jonathan Ive now responsible for the Apple UI design, the iCL will effortless make your app look consistent with iOS 7.

Why use FireMonkey controls?
The standard operating system controls in iOS are quite limited. There is no grid, there is no syntax highlighting memo, there is no chart, ... etc... Much like we have thousands of VCL controls that offer functionality that is not in the operating system, FireMonkey controls exactly offer these extras. The good news is that you can mix both control types to take advantage of each where it fits.

The 'native' confusion
We get quite a number of emails from users asking what the difference is between what is called 'native' FireMonkey mobile applications and iCL native controls. The definition of the exact difference between 'native' and 'native' would take a few pages to explain when applied to the FireMonkey framework in XE4. We'd like to summarize this in a nutshell. iCL exposes the native iOS control where a Delphi FireMonkey application runs as a native, i.e. ARM CPU machine language app on the iOS device. So, a Delphi FireMonkey application using iCL controls is about as native as an Objective C X-Code iOS application.

Limitations
Of course, the first important limitation is that an iCL control is only usable for iOS device applications. So, iCL is like VCL not cross-platform. As we make the iCL controls accessible from the Delphi IDE form designer, running directly on Windows, this means we are forced to show a sort of emulation of the control in the IDE form designer. In the first version, we have chosen to implement only a very rough and basic emulation of the control. As the code to do this being part of the control normally sits in the app and doesn't do anything, we wanted to minimize this bloat. For future versions, we consider to implement the design-time emulation this way that this code won't get linked with the app. Finally, there is currently an RTL limitation that limits the number of different iOS control classes usable in a form.
We have notified Embarcadero about this bug and hope Embarcadero will solve this as soon as possible. The limitation right now is such that for a typical application using some mix of iCL controls won't be affected, but when building something like an iPad form where you intend to use every single iOS control that we offer on the same form, it is likely that you'll bump into this limitation and the application will simply crash when the form opens on the iOS device.

Freedom of choice
After all, it is all about offering Delphi developers freedom of choice to use the technology that fits best their needs and using iCL is just one more such choice. If you struggle with a performance issue, if iOS consistency and 100% UI compliance is a major concern for you, you can now use iCL controls and where some functionality is missing in the standard controls, just add existing, your own or TMS FireMonkey controls. These are exciting times for Delphi developers, so, happy coding!

TMS iCL on iOS7

$
0
0

Curious as we are, we wanted to test out our TMS iCL native iOS controls for FireMonkey today on the beta of iOS7 that was released yesterday at WWDC 2013.
While there are some glitches with debugging, mainly due to the Delphi XE4 debugger itself, the good news is that our TMS iCL controls not only work smooth on iOS 7 but also adopt the new look & feel and new behaviors. The FireMonkey application using TMS iCL installed on iOS 6 adopts the iOS 6 look & feel while the same application deployed on iOS adopts the new iOS 7 look & feel.
Stay tuned.

Block Animations with TMS iCL

$
0
0

This week, we had a support question on Block Animations with TMS iCL, or in other words, how to animate for example a TTMSFMXNativeUIView?

To be honest, after all the hard work around the TMS iCL components, demos and documentation there wasn't room left for some more diving inside the iOS frameworks and features, so we couldn't really answer YES to the question until it was sorted out if it was even possible. When we got the question about Block Animations we were really curious if it would actually do anything since we are not working directly in XCode with the native language. So we dropped everything and started testing.

It was clear that even this feature of the iOS core animation framework would work as expected without any issues. We dropped a TTMSFMXNativeUIView instance on the form, dropped a button with the following code:

TUIView.OCClass.beginAnimations(nil, nil);  
TUIView.OCClass.setAnimationDuration(0.4);  
TMSFMXNativeUIView1.SetBounds(0, 0, 320, 480);  
TUIView.OCClass.commitAnimations;  
and some initialization to position the view out of sight:
TMSFMXNativeUIView1.SetBounds(0, 480, 320, 480); 
Starting the application and clicking the button slides the view in from the bottom.

This is only a basic animation and in the documentation of Block Animations there were some more complex samples such as animating the alpha of a component and the animation curve. The next sample animates a flip on the view and shows a second view:
var
  vwRemove, vwAdd: TTMSFMXNativeUIView;
begin
  vwRemove := nil;
  vwAdd := nil;
  if Assigned(TMSFMXNativeUIView1.Parent) then
  begin
    vwRemove := TMSFMXNativeUIView1;
    vwAdd := TMSFMXNativeUIView2;
  end
  else if Assigned(TMSFMXNativeUIView2.Parent) then
  begin
    vwRemove := TMSFMXNativeUIView2;
    vwAdd := TMSFMXNativeUIView1;
  end;

  if Assigned(vwRemove) and Assigned(vwAdd) then
  begin
    TUIView.OCClass.beginAnimations(nil, nil);
    TUIView.OCClass.setAnimationTransition(UIViewAnimationTransitionFlipFromLeft, TMSFMXNativeUIView3.View, False);
    TUIView.OCClass.setAnimationCurve(UIViewAnimationCurveEaseIn);
    TUIView.OCClass.setAnimationDuration(0.8);
    vwRemove.Parent := nil;
    vwAdd.Parent := TMSFMXNativeUIView3;
    TUIView.OCClass.commitAnimations
  end;
end;
The demo for this code can be found at the following link:
http://www.tmssoftware.net/public/FlipDemo.zip


An Excel report generated by TMS FlexCel for TMS FlexCel at Xamarin

$
0
0

With Xamarin targettng iOS, Android, Windows Phone mobile devices, they offer an online tool to perform an analysis for .NET assembly compatibiliy & readiness with the Xamarin tools. Having an interest to make our TMS Flexcel for .NET product ready for Xamarin, we did this analysis and one of the options is to save the analysis report as Excel file.
Guess what, behind the online analysis tool from Xamarin is our TMS Flexcel for .NET product that generates this report on the fly. So, when we got the report for the Xamarin analuysis of the TMS Flexcel assembly, the report was actually generated by TMS Flexcel itself. A nice coincidences we'd like to share.



Viewing all 1008 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>