18
Programming in Delphi with Thinfinity VirtualUI and jsRO In this tutorial we’ll show you how to publish a Windows application created with Delphi, starting with a simple, single-line-of-code user interface remoting and progressing with advanced programming techniques to reach a full web integration. For this purpose we’ll use the simple and well known “Fish Facts” demo and we’ve divided this tutorial in 5 steps: Step1: Shows how to take Fish Facts demo to the web by just including a unit in the project’s uses clause Step2: Shows how to start using our jsRO framework (Javascript Remote Objects) to remote a “db” object containing the “CATEGORY” of the selected fish and reflect the content of this property in a separate HTML div element on the web browser. Also, we’ll start taking advantage of VirtualUI’s Development Lab, to interactively work the the published objects. Step3: Shows how to publish all the dataset fields of the selected row at once as properties of the remote “db” object. It also, shows how to use the OnGet “getter” method to create a dynamic data binding to the dataset. On the browser side, we use the “SPECIES_NAME” property to show an iframe with images using the Bing Search engine. Step4: Shows how to add methods to the remote “db” object, to be called on the browser side, triggering events on Delphi side. This way, we added navigation buttons on the HTML page that control the app dataset. Step5: Shows how embed a HTML element inside the Windows application using custom events. In this case, we embed the Bing Search iframe into the area occupied by the fish picture and description. Preparation To proceed with the tutorial, you need to 1. Download the project files from http://www.cybelesoft.com/downloads/delphi-fishfact-tutorial.zip , unzip the file and load the Fishfact.groupproj project group in your Delphi XE+. 2. In Delphi, open the project group Fishfact.groupproj and compile all projects. 3. Run Fishfact0 project. This is the base project with no VirtualUI code. You cannot start this tutorial if this project doesn’t run properly! 4. Now open VirtualUI Server Manager, click on Applications tab and then in Add button. a. Click on “Open” button next to “Program path and file name” editbox, look for the compiled Fishfact1.exe and select it. b. Click on “Open” button next to “Home page” editbox, look for index.html file under tutorial\step1\web\ and select it. c. Click on “Ok” button. You might be required to complete the credentials tab before accepting all changes.

Programming in Delphi with Thinfinity VirtualUI and jsRO

  • Upload
    others

  • View
    5

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Programming in Delphi with Thinfinity VirtualUI and jsRO

Programming in Delphi with Thinfinity VirtualUI and jsRO In this tutorial we’ll show you how to publish a Windows application created with Delphi, starting with

a simple, single-line-of-code user interface remoting and progressing with advanced programming

techniques to reach a full web integration.

For this purpose we’ll use the simple and well known “Fish Facts” demo and we’ve divided this

tutorial in 5 steps:

● Step1: Shows how to take Fish Facts demo to the web by just including a unit in the project’s

uses clause

● Step2: Shows how to start using our jsRO framework (Javascript Remote Objects) to remote a

“db” object containing the “CATEGORY” of the selected fish and reflect the content of this

property in a separate HTML div element on the web browser. Also, we’ll start taking

advantage of VirtualUI’s Development Lab, to interactively work the the published objects.

● Step3: Shows how to publish all the dataset fields of the selected row at once as properties of

the remote “db” object. It also, shows how to use the OnGet “getter” method to create a

dynamic data binding to the dataset. On the browser side, we use the “SPECIES_NAME”

property to show an iframe with images using the Bing Search engine.

● Step4: Shows how to add methods to the remote “db” object, to be called on the browser

side, triggering events on Delphi side. This way, we added navigation buttons on the HTML

page that control the app dataset.

● Step5: Shows how embed a HTML element inside the Windows application using custom

events. In this case, we embed the Bing Search iframe into the area occupied by the fish

picture and description.

Preparation

To proceed with the tutorial, you need to

1. Download the project files from

http://www.cybelesoft.com/downloads/delphi-fishfact-tutorial.zip, unzip the file and load the

Fishfact.groupproj project group in your Delphi XE+.

2. In Delphi, open the project group Fishfact.groupproj and compile all projects.

3. Run Fishfact0 project. This is the base project with no VirtualUI code. You cannot start this

tutorial if this project doesn’t run properly!

4. Now open VirtualUI Server Manager, click on Applications tab and then in Add button.

a. Click on “Open” button next to “Program path and file name” editbox, look for the

compiled Fishfact1.exe and select it.

b. Click on “Open” button next to “Home page” editbox, look for index.html file under

tutorial\step1\web\ and select it.

c. Click on “Ok” button. You might be required to complete the credentials tab before

accepting all changes.

Page 2: Programming in Delphi with Thinfinity VirtualUI and jsRO

d. Repeat the procedure from subitem a) to c) with the projects 2 to 5.

e. Click “apply”.

5. You can now close the Server Manager.

Now, let’s start with the tutorial!

Step1: Simple remoting

Go to tutorial\step1\ and load Fishfact1.dpr, compile it and run it. Unlike before, the form will not

show up on the desktop! This is because the application is waiting for the web browser to start.

Open the web browser (preferably in another machine) and point to the ip address and port

associated to the Thinfinity VirtualUI Development Server. For example, http://192.168.0.115:6080/.

Now you should see the application running on the web browser looking and behaving just like a the

normal desktop application!

Finally, verify that the application still works as usual under Windows. Using the Windows explorer, go

Page 3: Programming in Delphi with Thinfinity VirtualUI and jsRO

to the folder where the compiled Fishfact1.exe was left and double click it. It should run without any

changes!

Now let’s take a look at the code:

1) Edit Fishfact1.dpr source and look at the uses clause. You will see that we added the

VirtualUI_AutoRun unit:

program Fishfact;

uses

Forms,

VirtualUI_AutoRun,

FFACTWIN in 'Ffactwin.pas' {Form1};

That’s it!

Step 2: Adding interaction between the application and the web page

We’ve already taken the first step by making the application available to the web browser. Now we

will increase the interaction between the application and the web page by making small changes in

the application that will allow us to expose application data to the browser side and viceversa.

This is achieved by using the Javascript Remote Objects framework (jsRO) included in Thinfinity

VirtualUI. It allows you to create remotable server objects, with its properties, methods and events

that are mirrored to the web as native javascript objects:

● When a property value changes on one end, automatically changes on the other end, firing an

event handler to let you take action.

● When a method is called on the browser side, a method handler callback is called on the

application.

● When the application fires an event, an event is fired on javascript.

Before looking into the code, let’s try the application and see how it behaves.

● Go to http://your-ip:6080

● Run Fishfact2.dpr project.

The application should show up inside the main area of the Development Lab as shown in below’s

picture:

Page 4: Programming in Delphi with Thinfinity VirtualUI and jsRO

Notice that on the right side panel you can see the a combo box with a “db” text inside, indicating that

there is a “db” jsRO object instantiated. Below of it, you see a property “CATEGORY”, showing the

actual value of the selected category field. Select another row on the application’s dbgrid and you will

see that this value will reflect the new field’s value.

Now, go to the panel above the application (Virtual Path panel) and enter /fishfact2/. Now the main

area should refresh, showing the customized web page.

Page 5: Programming in Delphi with Thinfinity VirtualUI and jsRO

As you can see, the category field is now feeding a vertical “label” on the left side of the application.

Now let’s analyse the code.

Step 2.1 Delphi code

We created a remote object called “db”, and we added to it a property called CATEGORY which will

expose the selected “CATEGORY” field of the fish database.

1. Open the Fishfact2.dpr project, located in tutorial\step2.

2. Open the ffactwin.pas file and look at the uses clause. We added the VirtualUI_SDK and

VirtualUI_TLb units : 

uses

...

...

VirtualUI_SDK,VirtualUI_TLb;

 

3. In Delphi, jsRO objects are created by using the TJSObject class that implements the IJSObject

Page 6: Programming in Delphi with Thinfinity VirtualUI and jsRO

interface. Below is shown the declaration of this object and a property accessor: 

{ Private declarations }

// Declares the JSObject

FRemoteDB : IJSObject;

function GetRemoteDB: IJSObject;

public

{ Public declarations }

property RemoteDB:IJSObject read GetRemoteDB;  

4. The GetRemoteDB creates the object if it doesn’t exist, adding the property and committing

changes to the web browser:

function TForm1.GetRemoteDB:IJSObject;

var

n : Integer;

Field : TField;

begin

if not Assigned(FRemoteDB) then begin

// Creates the "db" JSObject

FRemoteDB := TJSObject.Create('db');

// Creates the "CATEGORY" property.

FRemoteDB.Properties.Add('CATEGORY');

// Applies the object model and sends it to the web browser

FRemoteDB.ApplyModel;

end;

Result := FRemoteDB;

end;

5. In order to react to datasource changes, we add an OnDataChange event handler to the

Datasource1 and fill the CATEGORY property with the CATEGORY field value:

procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);

begin

// Assigns the value of the dataset field

RemoteDB.Properties['CATEGORY'].AsString :=

DataSource1.DataSet.FieldByName('CATEGORY').AsString;

end;

Step 2.2. Web side code Now let’s see the javascript code working with the remote “db” object. As a first example, we added a

div element in which we inserted the db.CATEGORY value:

1. Open the index.html file in the step2\web\ folder.

2. We added a fishfact.css stylesheet, for fancier results ;-):

Page 7: Programming in Delphi with Thinfinity VirtualUI and jsRO

<link rel="stylesheet" type="text/css" href="css/fishfact.css"/>  

3. Look at the div with id “fishName”. That div will show the “CATEGORY” field data:

<body style="background-color: #f2f2f2;">

<div id="fishName">fishfact</div>

<div id="virtualui" style="position:absolute;display:none;"/>

</body>

4. On jQuery’s ready event you see the creation of JsRO, which will allow us to access the remote

“db” object declared in Delphi: 

$(document).ready(function () {

var virtualUI = new Thinfinity.VirtualUI()

var jsro = new Thinfinity.JsRO();

virtualUI.connect();

});

5. Then, we added the event handlers to listen to the “db” model creation and the CATEGORY

property update.

$(document).ready(function () {

var virtualUI = new Thinfinity.VirtualUI();

var jsro = new Thinfinity.JSRO();

var db = null;

jsro.on('model:db, 'created', function () {

db = jsro.model.db;

});

jsro.on('model:db.CATEGORY, 'changed', function () {

document.getElementById("fishName").innerHTML = db.CATEGORY;

});

virtualUI.connect();

});

Step 3. Advanced property handling

Again, before looking into the code, let’s try this project and see how it behaves.

● Go to http://your-ip:6080/

● Type /fishfact3/ in the Virtual Path panel and press enter key or refresh button.

Page 8: Programming in Delphi with Thinfinity VirtualUI and jsRO

● Run Fishfact3.dpr project.

The application should show up inside the main area of the Development Lab as shown in below’s

picture:

As you can see on the right panel, most of the database fields are represented by properties. When

you select another row on the dbgrid, these properties are updated accordingly.

In this example we used Bing Image Search to show images corresponding to the field

“SPECIES_NAME”. We also added a checkbox to show or hide this content.

Play around a little bit and then let’s analyze the code.

Step 3.1. Delphi side

In this example we went a step further by publishing most of the dataset fields at once and adding

some dynamic binding code.

1. Select FactFish3.dpr project

Page 9: Programming in Delphi with Thinfinity VirtualUI and jsRO

2. Open the ffactwin.pas file and check out the GetRemoteDB method. We changed the single

property from step2 to a more complex algorithm to retrieve all fields names and types and

create the corresponding property on the “db” JSObject.  

function TForm1.GetRemoteDB:IJSObject;

var

n : Integer;

Field : TField;

begin

if not Assigned(FRemoteDB) then begin

// Creates the "db" JSObject

FRemoteDB := TJSObject.Create('db');

// Iterates over the dataset fields

for n := 0 to DataSource1.DataSet.Fields.Count-1 do begin

Field := DataSource1.DataSet.Fields[n];

// Bypasses some data types

if Field.DataType in [ftString, ftFixedChar,

ftMemo,ftWideString,ftFloat,ftCurrency] then

// Creates the property with the name of the field.

FRemoteDB.Properties.Add(Field.FullName) // Returns a IJSProperty

.OnGet(TJSBinding.Create( // Adds a "getter"

// This anonymous procedure do the actual get

procedure(const AObj:IJSObject;const AProp:IJSProperty)

begin

Field := DataSource1.DataSet.FieldByName(AProp.Name);

case Field.DataType of

ftString, ftFixedChar, ftMemo,ftWideString:

AProp.AsString := Field.AsString;

ftFloat,ftCurrency:

AProp.AsFloat := Field.AsFloat;

end;

end));

end;

// Applies the object model and sends it to the web browser

FRemoteDB.ApplyModel;

end;

Result := FRemoteDB;

end;

 3. We also changed the OnDataChange event. Now we use the ApplyChanges, that will trigger all

“getter” at once looking to update for changes. 

Page 10: Programming in Delphi with Thinfinity VirtualUI and jsRO

procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);

begin

// Calls all RemoteDB's properties "getters"

RemoteDB.ApplyChanges;

end;

Step 3.2. Web side code

1. Open the index.html file in the step3\web\ folder.

2. We changed the divs a little bit with the following content:

<div id="webcontent">

<div id="fishName">fishfact</div>

<div id="virtualui-container">

<div id="virtualui" style="position:absolute;display:none;"></div>

<div id="imagesbox">

<iframe id="photosearch" scrolling="no"></iframe>

</div>

</div>

</div>  

3. We also added an event handler for the SPECIES_NAME property, so we can do a new search

with the new value.

      $(document).ready(function () { ...

jsro.on('model:db.SPECIES_NAME', 'changed', function (obj) {

var ps = document.getElementById("photosearch")

ps.src = "http://www.bing.com/images/search?q=" + db.SPECIES_NAME;

ps.style.display = "block";

});

});

Step 4: Commanding fishfact from the web page

Now we’ll start using methods to take actions from the web page. In this case, we added HTML

buttons on the web page that will control the navigation of the dataset. We also added a button on

the application side to control whether to display or not the Bing Search frame.

● Go to http://your-ip:6080/

● Type /fishfact4/ in Virtual Path panel

● Run Fishfact4.dpr project.

Page 11: Programming in Delphi with Thinfinity VirtualUI and jsRO

The application should looks like this:

Step 4.1 Delphi side code

1. Select FactFish4.dpr project.

2. Open the ffactwin.pas file. In GetRemoteDB method we added a method definition for the

remote “db” object as shown below: 

// Adds a method to command the dataset navigation from Javascript

FRemoteDB.Methods.Add('navigate') // Returns a IJSMethod

.AddArgument('target', JSDT_STRING) // Adds a string parameter.

.OnCall(TJSCallback.Create( // Adds the callback

// this anonymous procedure to do the actual execution

procedure(const Parent: IJSObject; const Method: IJSMethod)

var

RV: boolean;

Target: string;

begin

Page 12: Programming in Delphi with Thinfinity VirtualUI and jsRO

RV := True;

Target := Method.Arguments['target'].AsString;

if (Target = 'first') then

Datasource1.DataSet.First

else if (Target = 'last') then

Datasource1.Dataset.Last

else if (Target = 'prev') then

Datasource1.DataSet.Prior

else if (Target = 'next') then

Datasource1.DataSet.Next

else

RV := false;

Method.ReturnValue.AsBool := RV;

end))

.ReturnValue.DataType := JSDT_BOOL; // Sets the return type

3. Additionally, we created another remote object called “layout” to do some fancy stuff, while

we learn about how to take advantage of a property’s setter. We put a property to control

whether we want to display or not the Bing Search info and a property “windowVisible”, to

command the visibility of the windows form from an external HTML button. The “setter”

code, will do the actual job of hiding/showing the window.

procedure TForm1.FormCreate(Sender: TObject);

begin

// Creates "layout" JSObject

FRemoteLayout := TJSObject.Create('layout');

// Adds a "bingSearch" property

FRemoteLayout.Properties.Add('bingSearch')

.OnGet(TJSBinding.Create( // Adds a "getter" to the returned IJSProperty

// This anonymous procedure do the actual get

procedure(const Parent: IJSObject; const Prop: IJSProperty)

begin

Prop.AsBool := chkBingSearch.checked;

end));

// Adds a "windowVisible" property

FRemoteLayout.Properties.Add('windowVisible')

.OnGet(TJSBinding.Create( // Adds a "getter" to the returned IJSProperty

// This anonymous procedure do the actual get

procedure(const Parent: IJSObject; const Prop: IJSProperty)

begin

Prop.AsBool := Visible;

end))

.OnSet(TJSBinding.Create( // Adds a "setter" to the returned IJSProperty

// This anonymous procedure do the actual set

procedure(const Parent: IJSObject; const Prop: IJSProperty)

Page 13: Programming in Delphi with Thinfinity VirtualUI and jsRO

begin

Visible := strToBoolDef(Prop.AsString, Visible);

if (not Visible) then

chkBingSearch.checked := true;

end));

// Applies the object model and sends it to the web browser

FRemoteLayout.ApplyModel;

end;

4. Finally we created an event handler attached to OnFormHide and OnFormShow events, to

update the “windowVisible” property’s value with the current state of the window.

procedure TForm1.LayoutChanged(Sender: TObject);

begin

// Calls all RemoteLayout's properties "getters"

if Assigned(FRemoteLayout) then

FRemoteLayout.ApplyChanges;

end;

Step 4.2 Web side

1. Open the index.html file in the step4\web\ folder.

2. We added div elements to act as buttons. Look at the code:  

    <div id="webcontent"> <div id="fishName">fishfact</div> <div id="virtualui-container"> <div id="virtualui" style="position:relative;display:none;"></div> <div id="imagesbox"> <iframe id="photosearch" scrolling="no"></iframe> </div> </div> <div class="ffnavbuttons" id="buttons-top"> <div class="button" id="windowVisibility">Hide Window</div><br /> </div> <div class="ffnavbuttons" id="buttons-bottom"> <div class="button" id="first">&lt;&lt;</div> <div class="button" id="prev">&lt;</div> <div class="button" id="next">&gt;</div> <div class="button" id="last">&gt;&gt;</div> </div> </div>   

3. We added javascript the definition for the remote “layout” object and “photosearch” div element, and we obtain the layout object on the ‘created’ event. 

$(document).ready(function () { var db, layout = null;

Page 14: Programming in Delphi with Thinfinity VirtualUI and jsRO

var ps = document.getElementById("photosearch"); ... jsro.on('model:layout', 'created', function (obj) { layout = jsro.model.layout; }); ... });

4. Now, when the “bingSearch” and “windowVisible” properties change, we take the appropriate

action.

$(document).ready(function () { ... jsro.on('model:layout.bingSearch', 'changed', function (obj) { document.getElementById("imagesbox").style.display = (obj.value) ? "inline-block" : "none"; }); jsro.on('model:layout.windowVisible', 'changed', function (obj) { document.getElementById("windowVisibility").innerHTML = (obj.value) ? "Hide Window" : "Show Window"; }); ... });

5. Finally, using jquery, we handle the clicks on the div elements acting as buttons.  $(document).ready(function () { ... ... $("#windowVisibility").on("click", function (e) { layout.windowVisible = !layout.windowVisible; }); $("#buttons-bottom .button").on("click", function () { db.navigate(this.id); }); });

Step 5: Firing custom events and embedding HTML content in the Delphi form

As a final step, we added a couple of radio buttons that will trigger a custom event. Also, we added

the coordinates of the panel containing the fish details and an identifier of the div element that holds

the applications image. With this information and some javascript code, when we fire the custom

event in Delphi, the iframe gets “embedded” into the windows form div. Let’s see the result.

Page 15: Programming in Delphi with Thinfinity VirtualUI and jsRO

● Go to http://your-ip:6080/

● Type /fishfact5/ in virtual path panel

● Run the Fishfact5.dpr project.

The application should looks like this:

Let’s dig into the code:

Step 5.1 Delphi side code

1. Select FactFish5.dpr project.

2. Open the ffactwin.pas file. In FormCreate we the windowId and bounds properties to the

“layout” object. We also added the custom event “embed”.

// Properties needed to embed the Bing Search iframe

// Adds a "windowId" property with the calculated div id containing this window

FRemoteLayout.Properties.Add('windowId').AsString :=

Format('virtualui_canvas_%d',[Handle]);

Page 16: Programming in Delphi with Thinfinity VirtualUI and jsRO

// Adds a "bounds" property with the coordinates where to embed the iframe

FRemoteLayout.Properties.Add('bounds')

.OnGet(TJSBinding.Create( // Adds a "getter"

// This anonymous procedure do the actual get

procedure (const AObj:IJSObject;const AProp:IJSProperty)

var

p : TPoint;

begin

p := pnlDetail.ClientToScreen(Point(0,0));

p.Offset(-Left,-Top);

// Returns a JSON object

AProp.AsJson := Format('{"left":%d,"top":%d,"width":%d,"height":%d}',

[p.X,p.Y,pnlDetail.Width,pnlDetail.Height]);

end));

// Adds the "embed" event with 1 boolean argument

FRemoteLayout.Events.Add('embed').AddArgument('value',JSDT_BOOL);

3. Here we added the code to fire the custom “embed” event

procedure TForm1.rbEmbedClick(Sender: TObject);

begin

// Fires the 'embed' event

FRemoteLayout.Events['embed']

.ArgumentAsBool('value', rbEmbedMode.Checked)

.Fire;

end;

Step 5.2 Web side code

1. Open the index.html file in the step5\web\ folder.

2. We added the javascript definition for imgb and savedStyle. $(document).ready(function () {

var db, layout = null; var imgb = document.getElementById("imagesbox"); var ps = document.getElementById("photosearch"); var savedStyle = { "bounds": {}, "searchMode": "desktop" };

... ...

});  

3. We coded a handler for the bingSearch property’s change event.

Page 17: Programming in Delphi with Thinfinity VirtualUI and jsRO

jsro.on('model:layout.bingSearch', 'changed', function (obj) { var display = (obj.value) ? "inline-block" : "none"; ps.style.display = display; ps.style.display = display; });   

4. Here is the code that handles the embedding: $(document).ready(function () { ... ...

jsro.on('model:layout.bounds', 'changed', function (obj) { if (savedStyle.searchMode == "embedded") changeBounds(); }); jsro.on('layout', 'embed', function (embedded) { if (layout.bingSearch) { if (embedded) { document.getElementById(layout.windowId).appendChild(ps); saveStyle(ps.style); changeBounds(); imgb.top = "0px"; } else { imgb.appendChild(ps); restoreStyle(ps.style); imgb.top = "-135px"; ps.style = "display:inline-block"; } } savedStyle.searchMode = (embedded) ? "embedded" : "desktop"; });

... ...

});   

5. Here are the rest of javascript functions used $(document).ready(function () {

… …

function changeBounds() { ps.style.left = layout.bounds.left + "px"; ps.style.top = layout.bounds.top + "px"; ps.style.width = layout.bounds.width + "px"; ps.style.height = layout.bounds.height + "px"; ps.style.zIndex = 1; } function saveStyle(style) {

Page 18: Programming in Delphi with Thinfinity VirtualUI and jsRO

savedStyle.bounds = { 'left': style.left, 'top': style.top, 'width': style.width, 'height': style.height, 'zIndex': style.zIndex }

} function restoreStyle(style) { style.left = savedStyle.bounds.left; style.top = savedStyle.bounds.top; style.width = savedStyle.bounds.width; style.height = savedStyle.bounds.height; style.zIndex = savedStyle.bounds.zIndex; } saveStyle(ps.style); });