In this tutorial you will develop a Mobile RPG application from start to finish. Along the way you will learn some Mobile RPG concepts and be encouraged to further explore other Mobile RPG Documentation and development materials to learn more about the different components and their options.
To complete this tutorial you must have a Windows 7, 8, or 10 development machine with:
You also need access to an IBM i server with:
The tutorial assumes you have a working knowledge of RPG programming on the IBM i, including creation of data files. The data files the tutorial uses are part of the samples that ship with Mobile RPG and their creation will not be further discussed.
Imagine you own a tow truck that can transport a vehicle, and you are going to travel across the country in it; maybe you are going to pick up some vehicle or are taking a vehicle to another town. Wouldn't it be great if you could make some money both ways? But how do you easily find a towing job? The solution is Carolina, a Mobile RPG application for your phone that will help you locate vehicles in need of transportation. You will be able to search for potential jobs, contact the person soliciting the job, and plan the path to the job's destination.
So, given your years of experience in IT you sit down to think about it and come up with a mock-up of the Carolina app, shown in Figure 1:
The application's first screen is at the top left; it will be a menu where the tow truck driver can search for a job either by type of car or by job ID. It also has a menu option to view a management dashboard.
Selecting the Body and Size option will take the user to a page that shows a clickable chart with information on the available jobs by type of car; the user's tow truck may be able to carry only specific sizes of vehicles.
Clicking on a particular body and size column will show a list with all the available jobs in that category, and clicking on any of the shown vehicles will open the Job page, showing all the job-related information. The Job page also has a map with the suggested delivery route, and pictures of the vehicle as it was when picked up and at delivery.
Back to the main menu, selecting Find by Job ID will bring the Search Job page, and inputing the proper Job ID will show the Job page.
Finally, selecting the Management dashboard will bring a page where summarized information about revenues is shown monthly in column chart form, and by state in pie chart form.
In terms of data files we start with two: one with the vehicle information, which we'll call VEHICLE, and one with the job information, which we'll call JOB. Both files can be found in the ASNAMRSAMP library. You can recreate the files from DDS if you want. The VEHICLE file contains the descriptive information for a vehicle. It has a unique vehicle identifier, VHID, to link the vehicle to a job, and standard vehicle information like VIN, year, make, model, etc., and is defined as:
A R RVEHICLE A VHID 10 TEXT('ID') A VHVIN 17 TEXT('Serial Number') A VHMAKE 20 TEXT('Make') A VHMODEL 40 TEXT('Model') A VHYEAR 4P 0 TEXT('Year') A VHPLATE 10 TEXT('Plate') A VHCOLOR 20 TEXT('Color') A VHRUNS 1 TEXT('Is drivable') A VHBODY 10 TEXT('Body Style') A VHSIZE 10 TEXT('Size')
The JOB file contains information about a towing job. It has a unique job identifier, the unique vehicle identifier to link to the vehicle's record in the VEHICLE file, and from and to addresses to specify where to pick the vehicle up and where to deliver it. Here's the file's definition:
A R RJOB A JBID 10 TEXT('Job ID') A JBVHID 10 TEXT('Vehicle ID') A JBFRMFIRST 30 TEXT('From First Name') A JBFRMINIT 1 TEXT('From Initial') A JBFRMLAST 30 TEXT('From Last Name') A JBFRMSTR1 50 TEXT('From Street 1') A JBFRMSTR2 50 TEXT('From Street 2') A JBFRMCITY 20 TEXT('From City') A JBFRMSTATE 2 TEXT('From State') A JBFRMZIP5 5 TEXT('From Zip Code') A JBFRMCNTRY 30 TEXT('From Country') A JBFRMPH1 10P 0 TEXT('Telephone 1') A JBFRMPH2 10P 0 TEXT('Telephone 2') A JBFRMPCELL 10P 0 TEXT('Cell phone') A JBTOSTR1 50 TEXT('To Street 1') A JBTOSTR2 50 TEXT('To Street 2') A JBTOCITY 20 TEXT('To City') A JBTOSTATE 2 TEXT('To State') A JBTOZIP5 5 TEXT('To Zip Code') A JBTOCNTRY 30 TEXT('To Country')
Now we have enough information to start developing the Carolina application. You can play with a full version of the Carolina application at https://demo.asna.com/Carolina. Works best using a phone!
In preparation for starting on the Carolina application we'll first review some basic concepts of ASNA Mobile RPG, which we will refer to simply as Mobile RPG or MR in this Tutorial, and create a default Mobile RPG website.
Mobile RPG takes advantage of the Rational Open Access, RPG Edition (OAR) product on the IBM i. OAR allows the input/output operations on a file to be intercepted by a Handler, which then decides how to process these operations. Mobile RPG adds a Handler that specifically works on workstation files and redirects the workstation's I/O operations to a website ASPX is the .Net version of Active Server Pages (ASP). hosted on a Windows ASPX web server. In this way Mobile RPG presents a web face on traditional RPG ILE programs.
A Mobile RPG program starts when a browser accesses the Mobile RPG website. The web server calls the RPG program on the IBM i server, and the RPG program answers the browser's request by simply doing an EXFMT on a workstation file. The Mobile RPG handler intercepts this EXFMT and communicates with the Mobile RPG components on the web server to deliver the proper web page and data to the browser.
The development cycle of a Mobile RPG application is very similar to the development of a green-screen RPG application. The next table compares traditional RPG vs Mobile RPG development:
Traditional RPG | ASNA Mobile RPG | |
---|---|---|
RPG code | RPG ILE on the IBM i server | RPG ILE on the IBM i server |
Display Files | Edited as DDS source and compiled on the IBM i | Edited as ASPX web pages on Windows using MS Visual Studio, then exported to the IBM i |
In both cases the RPG ILE code is developed and compiled on the IBM i. The difference is where the source for the DDS resides. In green-screen programming it resides in the IBM i server. In Mobile RPG, it's coded as an ASPX web page, called a Mobile Display File, or MobileDF.
Developing a Mobile RPG application is an iterative process, done both in the Windows development box, which acts as a Web server, and the IBM i server, as shown in Figure 2:
Now we are ready, and this is how you will start any new Mobile RPG application you write:
In order to properly create the website, the MR website Wizard requires a connection to the IBM i server that will be used to develop the application, so you will now see the dialog in Figure 4 to select the IBM i server and the login credentials to use on it. It is very important that the given credentials have enough authority to create libraries and files, and to compile RPG ILE and DDS.
You can also use a Database Name, which is a cached description of the connection parameters to an IBM i server. To create a Database Name you need to use DataGate Explorer. There's a brief step-through in Appendix A, see your ASNA documentation for further details. There is a slight but significant difference in the behavior of the created website when using credentials vs a Database Name. If you use login credentials, the website user will be asked for user and password to start the application. If you use a Database Name, the application starts using the credentials cached in the Database Name.
Type in your login credentials, and select the library that will be used for the IBM i side of the application. We suggest XXCAROLINA, where XX represents your initials so other co-workers following this tutorial don't mistakenly erase or change your work. Click OK and after a few seconds the MR Wizard will have created the base website from which you can start building your application. Figure 5 shows what you should expect to see:
If the pane on the right hand side is not visible, go to VIEW > Solution Explorer. The Solution Explorer shows the structure and files of the created website.
Two of the source files are of special interest. Web.config is an XML source file that contains settings used by the login process. Let's looks at that part of the file:
<appSettings>
<add key="MobileRPGUsername" value="xxxx"/>
<add key="MobileRPGServerName" value="elm"/>
<add key="MobileRPGServerPort" value="5042"/>
<add key="MobileRPGPromptForServer" value="no"/>
<add key="MobileRPGLibraryName" value="XXCAROLINA"/>
<add key="MobileRPGProgramName" value="HelloRPG"/>
</appSettings>
The user, server, port, and library values are the ones that were provided in the credentials dialog of the wizard. These values can be easily changed to whatever’s needed for deployment. When the app runs, the SignOn page is displayed first asking for the user and the password to connect to the server and start the app. Whether the SignOn page also prompts for the server and port is determined by the value of the “MobileRPGPromptForServer” entry. Any value other than no
will cause the prompting of the server and port in addition to user and password. If the login credentials are valid, the app starts by calling the program indicated in the “MobileRPGProgramName” entry, set to HelloRPG
which is the name of the program created by the wizard. If any of these key-value pairs is missing, there are defaults hardcoded in the MobileRPGJob.cs source file, put there by the wizard.
If you used a Database Name in the credentials dialog of the wizard you'll see these keys instead:
<appSettings>
<add key="MobileRPGDatabaseName" value="My400_5042"/>
<add key="MobileRPGPromptForServer" value="no"/>
<add key="MobileRPGLibraryName" value="XXCAROLINA"/>
<add key="MobileRPGProgramName" value="HelloRPG"/>
</appSettings>
Instead of server, port, user and password, the Database name selected in the credentials screen of the wizard is put here. The big difference in app behavior is that when using a Database name, You can use a Database name for development and a user/password for deployment or viceversa, just edit Web.config and change the proper settings. the SignOn page is bypassed and the app starts immediately using the credentials cached in the Database name. This implies that the “MobileRPGPromptForServer” value is ignored. If you need to prompt for a server you must not use a Database name.
Now let's take a look at the Mobile Display File: HelloDspf.aspx. This is the display file used by the startup program "HelloRPG" that we have specified in Web.config, and which was created by the wizard in your IBM i. If it's not open, double-click on it. If it's open but you don't see the text select Split or Source at the bottom of the window. (If you don't see the Design, Split, and Source buttons, right click on HelloDspf.aspx in the solution explorer, click Open With, select Web Forms Editor in the following dialog. Click Set as Default then OK.) ASPX source files are special text files containing markup, a combination of ASPX tags and HTML tags, that when processed by the web server produce an HTML page to be sent to a browser.
Take your time reading the comment, in green, at the top of the ASPX source. DDS controls define both data and presentation. You should read the Mobile Display File Overview document, which explains in detail the structure of the Mobile Display File and the different DDS controls. To specify records and fields we use a set of ASPX controls designed to be used by Mobile RPG, called the DDS controls. You can see these controls in the Toolbox (go to VIEW > Toolbox if you can't see the Toolbox window, use the thumbtack to pin it so it won't autohide). See Figure 6.
Going back to HelloDspf.aspx, look for the second asp:Content element. Inside it is the definition of the DDS file. It contains one DDS record, and the record itself has a DDS bar with two DDS bar segments, and a HTML element that shows the 'Hello World!' text. This is the basic structure of a Mobile display file: a DDS file, with one or more DDS records containing various DDS controls and HTML.
In the IBM i the wizard created the library XXCAROLINA (if it didn't already exist) and three files: QRPGLESRC with the HELLORPG member, the HELLODSPF display file, and the compiled HELLORPG program, shown in Figure 7:
Now run the website by pressing F5 on your keyboard. After VS builds your website your default browser will open with the first page of your app, which is the signon screen, in Figure 8:
enter your IBM i login credentials to start the app which shows the Hello World screen, shown in Figure 9: If you have a mobile device connected via Wi-Fi to the local network you may setup your Carolina Mobile App under Internet Information Services (IIS), and test it. See Appendix B for a brief overview of the required steps.
Pressing END will finish the app which will show the End of Job screen, in Figure 10:
Finally, there is some rebranding we need to do to better reflect the name of the application we are developing (and to prevent the inadvertent loss of our work in case we start over!).
<add key="MobileRPGProgramName" value="CARORPG">
With this Lesson we start modifying the Mobile Display File by creating the Home Menu screen, seen in Figure 13.
In this lesson we will:
Before we start modifying CaroMain.aspx let's review some rules governing the syntax of the markup:
<
and >
, and they come in open/close pairs, with the closing Tag being prefixed with the /
character. For example, <div> ... </div>
. Tags that do not have any nested content can be implicitly closed by adding the /
before the >
. For example, <br />
.<
, are reserved and need to be replaced with a specific escape sequence (see the online list here).<br />
when needing paragraph separation.keyword = "value"
, for example, <div style="mydivstyle">
.prefix:name
. ASPX tags require the property runat="server"
. Mobile RPG tags are ASPX tags using the prefix mdf:
.Open CaroMain.aspx. Click Split at the bottom of the window so you see both the markup and the design. Find the Here is a quick reference to HTML tags. Some that you should become familiar with are h1 through h6, p, br, hr, div, and span Hello, world! text, and remove it. Before:
</mdf:DdsBar>
Hello, world!
<br/>
</mdf:DdsRecord>
after:
</mdf:DdsBar>
<br/>
</mdf:DdsRecord>
In its place add a header, level 2, saying Home Menu, and a header, level 4, saying Locate Transportation Jobs.
</mdf:DdsBar>
<h2>Home Menu</h2>
<h4>Locate Transportation Jobs</h4>
</mdf:DdsRecord>
Be sure to click the yellow bar to keep your views in sync, as shown in Figure 14:
Now we'll use a div tag to create a block where we'll put a button and its text. Write the open tag of the div, and VS will write the close for you. You can alternatively find a div in the toolbox, in the HTML section, and drag-and-drop it in the proper place in the text:
<h2>Home Menu</h2>
<h4>Locate Transportation Jobs</h4>
<div></div>
</mdf:DdsRecord>
Go to the toolbox and locate the DdsButton in the ASNA Controls. Drag it and drop it inside the div. You can do this in the Source pane or in the Design pane of the code window, whatever you prefer. Whether you work with the Design view or the Source view is up to you. There are certain operations that are difficult to do in Design, so it's good to get used to working in Source.After the button, type a br line break (or press Enter if you are in the Design pane), and the text By Body & Size:
<div>
<mdf:DdsButton ID="DdsButton2" runat="server" />
<br />
By Body & Size
</div>
Now we need to customize the button for our purposes. Position your cursor on any part inside the DdsButton tag, or click to select the button in Design, and press F4. This should bring up the Properties window. In the Appearance group look for the ButtonStyle property and set its value to Image. Now look for the Image property and set it to ~/Themes/Current/Images/Chart.png, and the ImageAlign property to Middle.
You will see that the text in the editor is changing as you select values for the button properties:
<div>
<mdf:DdsButton ID="DdsButton2" runat="server" ButtonStyle="Image" Image="~/Themes/Current/Images/Chart.png" ImageAlign="Middle" />
<br />
By Body & Size
</div>
The Image property is a path to an image file. The ~ in the path means the root of the website.
To borrow the images from the Carolina sample, right-click on the Images folder, and select
Add > Existing Item. Browse for the folder where the Carolina sample is installed (filter for
image files at the bottom right of the browse dialog), and select Chart.png, Dashboard.png,
Delete.png, HomeScreenIcon.png, Refresh.png, and Suv.png.
See Figure 16:Fig. 16 Adding Files
So the button will start from the root of the website, go into Themes, then Current, then Images, and look in that folder for the
Chart.png file to be its image. For now, this image file doesn't exist, but we can borrow it and a few others from the Carolina
sample, or you can make your own.
Now, we need to link the button with a function key. For this, the button has a property called AidKey. Go to the Behavior group in the property window and set the AidKey property to F14. You can use any F key and any indicator you want. Finally, we need to enable the F14 key in the record and assign it an indicator. Find the DdsRecord declaration and look for the FuncKeys property in the AidKeys group of the property window. The difference between FuncKeys and AttnKeys is that FuncKeys return values to the program, while AttnKeys don't. It's a common mistake to put in AttnKeys what should go in FuncKeys. If your RPG program doesn't seem to receive data check here first! Type in F14 14;; this selects indicator 14 to be associated with key F14. See Figure 17:
You'll notice that F3 is set to turn on indicator 03, and this is to support the Exit button in the top navigation bar.
Now go and add a second div for the By Job ID button and text. Use the ~/Themes/Current/Images/Suv.png image, and F13 with indicator 13 as the Aid key and indicator. You may want to add a couple of line breaks in between the divs, use your good taste. And of course you are allowed to use Copy/Paste in the ASPX editor. Do not forget to set the FuncKeys in the DdsRecord, otherwise your button will not be visible when you run the app. You should end up with something like this:
<h2>Home Menu</h2>
<h4>Locate Transportation Jobs</h4>
<div>
<mdf:DdsButton ID="DdsButton2" runat="server" ButtonStyle="Image" Image="~/Themes/Current/Images/Chart.png" ImageAlign="Middle" AidKey="F14" />
<br />
By Body & Size
</div>
<br />
<div>
<mdf:DdsButton ID="DdsButton3" runat="server" ButtonStyle="Image" Image="~/Themes/Current/Images/Suv.png" ImageAlign="Middle" AidKey="F13" />
<br />
By Job ID
</div>
To finish the Home Menu we need to add another h4 header containing the text Dashboard, and another div with a button followed by the text Management. The button will use F15 with indicator 15, and the image ~/Themes/Current/Images/Dashboard.png. Put all this right after the Job ID div section:
<div>
<mdf:DdsButton ID="DdsButton3" runat="server" ButtonStyle="Image" Image="~/Themes/Current/Images/Suv.png" ImageAlign="Middle" AidKey="F13" />
<br />
By Job ID
</div>
<br />
<h4>Dashboard</h4>
<div>
<mdf:DdsButton ID="DdsButton4" runat="server" ButtonStyle="Image" Image="~/Themes/Current/Images/Dashboard.png" ImageAlign="Middle" AidKey="F15" />
<br />
Management
</div>
Build and run the app by pressing F5. Your screen should look like Figure 18:
You may be wondering why we didn't export the Mobile Display File to the IBM i. The answer is we didn't have to because the record format didn't change. DdsButtons don't create fields in the record format or affect it in any way.
Last, change the RPG program to call (empty) subroutines when the different indicators for the buttons are detected. Write this code any way you want, but use BYBDYSZE, BYJOBID, and MGMNTDSH as the names of the subroutines. Here's a possible implementation. You are welcome to use free format if you prefer:
0007.00 C Select 0008.00 C When *In13 0009.00 C ExSr BYJOBID 0010.00 C When *In14 0011.00 C ExSr BYBDYSZE 0012.00 C When *In15 0013.00 C ExSr MGMNTDSH 0014.00 C When *In03 0015.00 C Eval *InLR = *On 0016.00 C Return 0017.00 C EndSl 0018.00 0019.00 C****************************************** 0020.00 C* Search for Jobs 0021.00 C****************************************** 0022.00 C BYJOBID BegSr 0023.00 C EndSr 0024.00 0025.00 C****************************************** 0026.00 C* Search by body and size of vehicle 0027.00 C****************************************** 0028.00 C BYBDYSZE BegSr 0029.00 C EndSr 0030.00 0031.00 C****************************************** 0032.00 C* Management Dashboard 0033.00 C****************************************** 0034.00 C MGMNTDSH BegSr 0035.00 C EndSr 0036.00
Compile your RPG program. Run the website again from VS to verify all is well.
Add a DdsCharField after the Dashboard div. Change its ID to Operation, and set its Usage to OutputOnly. Export the Display File to the IBM i (you need this because the DdsCharField represents a new field in the record format. Extra-extra credit: what is its length? hint: look for Length in the Property Window). In each of the RPG subroutines set the Operation field to "By Job", "Body&Size", and "Management" respectively. Compile the RPG program, run the app, and click on the different buttons.
Here's the DdsCharField:
<mdf:DdsCharField ID="Operation" runat="server" CssClass="DdsCharField" Usage="OutputOnly" />
And here's just one of the subroutines. The other two are similar.
0022.00 C BYJOBID BegSr 0023.00 C Eval Operation = 'By Job ID' 0024.00 C EndSr
Extra-extra credit: the default length of a DdsCharField is 10.
In this lesson we are going to add two new screens. The first one is the Job Search screen, and the second one is the Job screen, seen in Figure 19:
![]() |
![]() |
In this lesson we will:
Open The CaroMain.aspx. From the Toolbox, drag and drop a DdsRecord and put it right after the existing DdsRecord (i.e., right after the </mdf:DdsRecord>
). You'll get this:
</mdf:DdsRecord>
<mdf:DdsRecord ID="DdsRecord1" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;">
</mdf:DdsRecord>
</asp:Content>
Let's take a look at the DdsRecord default properties. ID is the name of the ASPX control, it's created by the editor when you drag and drop the control on a page. This name must be unique. The runat property specifies that the control is a server control, which means it has code that runs in the web server to produce the HTML for the browser. You can see all the predefined Css styles in the drop-down for the CssClass value in the Property Window. These styles live in the .css source files inside the ~/Themes/Current/Styles folder of the website. Erase Formats is a list of the record formats to be erased from the file when writing to this record, and CssClass determines the visual styling of the control when shown on the browser. The Style value indicates that the position of the record on the page is relative, i.e. is not fixed to some arbitrary place when viewed in a browser. This allows the browser to decide the best accommodation for the record. You may have already noticed that what the browser shows at runtime is not exactly identical to what you see in Design.
Now go to the Properties window and set the Alias property to JobSrch
. You can leave the ID property as it is, but it's better if you give it a more meaningful name. It's considered good practice to name it just slightly different than the Alias, by prefixing the Alias value with _
if it's a record, or by record-name_
if it's a field. Strictly speaking, ID represents the name of the control in the ASPX page, while Alias represents the name of the entity (field, record, or file) in the RPG program. If Alias is not given, then ID will also represent the name of the entity in the RPG program. This distinction is needed because ASPX requires unique names for its controls, while RPG record formats can share field names.
What does the record contain? Let's take a look again at the page we are creating:
The record has a bar at the top with a Home button and some text, and the body of the record has a grouping with text, an input field, and a Search button. We'll start with the bar. Drag and drop a DdsBar control into the DdsRecord (again, you can do this in the Source or Design panes) and change its ID to JobSrchBar. You'll get something like this:
<mdf:DdsRecord ID="_JobSrch" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="JobSrch">
<mdf:DdsBar ID="JobSrchBar" runat="server" CssClass="DdsBar">
<mdf:DdsBarSegment runat="server" Alignment="Center">
</mdf:DdsBarSegment>
</mdf:DdsBar>
</mdf:DdsRecord>
To set the bar as the navigation bar for the record set the record's NavigationBarControlID property to JobSrchBar. You can see the bar already contains a DdsBarSegment. Change the bar segment's Alignment to Left. Add a button in the bar segment (drag and drop a DdsButton into it). The button style NavButton matches the style of the navigation bar. Set the button's CssClass to NavButton, AidKey to F12, and Text to Home:
<mdf:DdsBarSegment runat="server" Alignment="Left">
<mdf:DdsButton ID="DdsButton5" runat="server" CssClass="NavButton" AidKey="F12" Text="Home" />
</mdf:DdsBarSegment>
Now let's enable the button's AidKey as an Attention Key in the record and pair it with indicator 12. Indicator 12 will then tell the RPG program to return to the Home Menu screen. This is what the record looks like now:
<mdf:DdsRecord ID="_JobSrch" runat="server" CssClass="DdsRecord"
EraseFormats="*ALL" Style="position: relative;"
Alias="JobSrch" NavigationBarControlID="JobSrchBar" AttnKeys="F12 12;">
Add a second DdsBarSegment. Set its Alignment to Center. Inside this bar segment type Search By ID.
<mdf:DdsBarSegment ID="DdsBarSegment3" runat="server" Alignment="Center">
Search By ID
</mdf:DdsBarSegment>
Let's use a <span>
around the text to set some formatting on it:The class property on an HTML tag is equivalent to the CssClass property on a server control tag.
<span class="PanelTitle">Search By ID</span>
For the body of the record we need a grouping control that will contain the rest of the elements of the screen and give them a nice look. This control is the DdsPanel. Look for DdsPanel in the toolbar and add it to the record after the DdsBar. The grouping text of a DdsPanel can also be given at runtime, by setting the name of the field that contains the text in the GroupingTextField and GroupingTextFieldLength properties. If you do this, the field becomes part of the record format. Set its GroupingText property to Please enter the:
</mdf:DdsBar>
<mdf:DdsPanel ID="DdsPanel1" runat="server" CssClass="DdsPanel" GroupingText="Please enter the">
</mdf:DdsPanel>
</mdf:DdsRecord>
Now add a DdsConstant and a DdsCharField for the label and the input field on the record. Set the Text property of the constant to Job ID. Set the Alias of the char field to JBID (and its ID to JobSrch_JBID, its Length to 10, and its Usage to InputOnly. This is the name of the key field in the JOB file, so declaring it also here will simplify the RPG code for reading a JOB record. You can also set the ID of these controls to more meaningful names if you want:
<mdf:DdsPanel ID="DdsPanel1" runat="server" CssClass="DdsPanel" GroupingText="Please enter the">
<mdf:DdsConstant ID="DdsConstant1" runat="server" CssClass="DdsConstant" Text="Job ID" />
<mdf:DdsCharField ID="JBID" runat="server" CssClass="DdsCharField" Alias="JBID" Usage="InputOnly" />
</mdf:DdsPanel>
Finally, add the Search DdsButton after the char field. Set its ButtonStyle property to Button, Text property to Search, and AidKey property to Enter. This sets the button click to behave as if the user pressed Enter on the keyboard:
<mdf:DdsButton ID="DdsButton6" runat="server" Text="Search" AidKey="Enter" ButtonStyle="Button" />
The changes to the MobileDF are finished. Make sure that the CaroMain.aspx page builds by right-clicking on the file in Solution Explorer and selecting Build Page. See Figure 21:
If the Build is successful, run the Export process to create the display file on the IBM i.
Now we can modify the RPG code to process the By Job ID button of the Home Menu. Edit the BYJOBID subroutine to execute the new record format:
0022.00 C BYJOBID BegSr 0023.00 C ExFmt JobSrch 0024.00 C If *In12 0025.00 C LeaveSr 0026.00 C EndIf 0027.00 C EndSr
Save and compile. Go back to VS and run the app. The By Job ID button should open the new screen, as shown in Figure 22:
Although we are not yet handling the Search button, the Home button should work and bring you back to the Home Menu.
You may notice that the text in the navigation bar is aligned to the right. We can move it to the middle by adding an empty bar segment after the text, as bar segments split the navigation bar area among them. Go ahead and add it (and add a couple of line breaks before the panel too):
...
<span class="PanelTitle" >Search By ID</span>
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment4" runat="server"></mdf:DdsBarSegment>
</mdf:DdsBar>
<br /><br />
<mdf:DdsPanel ID="DdsPanel1" runat="server" CssClass="DdsPanel" GroupingText="Please enter the">
...
Run the program again, no need to export and recompile, and see that the label is now in the center of the bar, shown in Figure 23:
We are now ready to add the record format for the screen that shows the Job details, shown in Figure 24:
Add a new DdsRecord to the file. Set its Alias to JobRec, set its ID using the _
naming convention. Add two DdsBars, one will be the top navigation bar, and one will be the footer bar. Name the first one JobNavBar, and the second one JobFooterBar. Attach the bars to the record by setting the record's NavigationBarControlID and FooterBarControlID properties to the IDs of the bars:
<mdf:DdsRecord ID="_JobRec" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="JobRec" NavigationBarControlID="JobNavBar" FooterBarControlID="JobFooterBar">
<mdf:DdsBar ID="JobNavBar" runat="server" CssClass="DdsBar">
<mdf:DdsBarSegment ID="DdsBarSegment5" runat="server" Alignment="Center"></mdf:DdsBarSegment>
</mdf:DdsBar>
<mdf:DdsBar ID="JobFooterBar" runat="server" CssClass="DdsBar">
<mdf:DdsBarSegment ID="DdsBarSegment6" runat="server" Alignment="Center"></mdf:DdsBarSegment>
</mdf:DdsBar>
</mdf:DdsRecord>
The navigation bar has 3 segments, the first one with the Back button, the middle one shows the job number, and the last one has two buttons that allow the user to move to the previous or next records in the JOB file. Add the bar segments and their contents to the navigation bar. Start by adding the 2 additional DdsBarSegment controls, set their alignments to Left, Center, and Right respectively. Add the Back button to the first segment, with AidKey set to F12, and CssClass set to NavButton. Add the text Job: XXXX to the second (middle) one. In the last bar segment add two buttons with the characters < and > as text and AidKeys set to F7 and F8 respectively; set the buttons CssClass to NavButtonSymbol:
<mdf:DdsBarSegment ID="DdsBarSegment5" runat="server" Alignment="Left">
<mdf:DdsButton ID="DdsButton7" runat="server" ButtonStyle="Button" Text="Back" CssClass="NavButton" AidKey="F12" />
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment7" runat="server" Alignment="Center">
<span class="PanelTitle">Job: XXXX</span>
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment8" runat="server" Alignment="Right">
<mdf:DdsButton ID="DdsButton8" runat="server" ButtonStyle="Button" AidKey="F7" Text="<" CssClass="NavButtonSymbol" />
<mdf:DdsButton ID="DdsButton9" runat="server" ButtonStyle="Button" AidKey="F8" Text=">" CssClass="NavButtonSymbol" />
</mdf:DdsBarSegment>
Don't forget to set the record's AttnKeys with the F keys for the buttons, pairing them with the same-numbered indicators.
AttnKeys="F7 07;F8 08;F12 12;"
The footer bar has two segments, aligned Left and Right respectively, and each segment contains a single button. The first segment contains the Refresh button, and the second segment the Delete button. Both buttons show images instead of text and are of NavButton style. The Refresh button shows the ~/Themes/Current/Images/Refresh.png image and uses F5, while the Delete button shows the ~/Themes/Current/Images/Delete.png image and uses F4. Add these segments and buttons now, and add the F keys to the record's AttnKeys:
<mdf:DdsBarSegment ID="DdsBarSegment6" runat="server" Alignment="Left">
<mdf:DdsButton ID="DdsButton10" runat="server" ButtonStyle="Image" AidKey="F5" Image="~/Themes/Current/Images/Refresh.png" />
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment9" runat="server" Alignment="Right">
<mdf:DdsButton ID="DdsButton11" runat="server" ButtonStyle="Image" AidKey="F4" Image="~/Themes/Current/Images/Delete.png" />
</mdf:DdsBarSegment>
As for the information displayed on the screen, for now, as we said at the start of this lesson, we will only show place holders for each of the five groups of information. Add five DdsPanels with some descriptive text in each, with a line break before each one:
<br />
<mdf:DdsPanel ID="DdsPanel2" runat="server" CssClass="DdsPanel">
Link to external site</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel3" runat="server" CssClass="DdsPanel">
FROM address</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel4" runat="server" CssClass="DdsPanel">
Map</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel5" runat="server" CssClass="DdsPanel">
Pictures at source</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel6" runat="server" CssClass="DdsPanel">
Pictures at destination</mdf:DdsPanel>
Build and Export CaroMain.aspx. We can change now the RPG code to process the Search button of the Search By ID screen. Create the subroutine SHOWJOB to show the new record format we added. Let's also handle the Back button of the Job screen and set placeholders for the Refresh and Delete buttons. We can also setup the program structure to handle the different indicators set by the buttons:
0022.00 C BYJOBID BegSr 0023.00 C SrchJob Tag 0024.00 C ExFmt JobSrch 0025.00 C If *In12 0026.00 C LeaveSr 0027.00 C EndIf 0028.00 C ExSr SHOWJOB 0029.00 C If *In12 0030.00 C GoTo SrchJob 0031.00 C EndIf 0032.00 C EndSr 0033.00 0034.00 C****************************************** 0035.00 C* Show Job record 0036.00 C****************************************** 0037.00 C SHOWJOB BegSr 0038.00 C ExFmt JobRec 0039.00 C Select 0040.00 C When *In12 0041.00 C LeaveSr 0042.00 C When *In05 0043.00 C* refresh 0044.00 C When *In04 0045.00 C* delete record 0046.00 C EndSl 0047.00 C EndSr
Finally, let's read the job and vehicle information we need for the Job screen and use the job number in the navigation bar. To find the Job and vehicle information we'll use a logical file that combines records from both the JOB and the VEHICLE files using the vehicle ID which exists in and links both files. The logical will have the job ID field, JBID, as the key. Here's the DDS description of the JOBVEH01 logical file:
A R RJOBVEH JFILE(JOB VEHICLE) A J JOIN(JOB VEHICLE) A JFLD(JBVHID VHID) A VHID 10 TEXT('ID') A VHVIN 17 TEXT('Serial Number') A VHMAKE 20 TEXT('Make') A VHMODEL 40 TEXT('Model') A VHYEAR 4P 0 TEXT('Year') A VHPLATE 10 TEXT('Plate') A VHCOLOR 20 TEXT('Color') A VHRUNS 1 TEXT('Is drivable') A VHBODY 10 TEXT('Body Style') A VHSIZE 10 TEXT('Size') A JBID 10 TEXT('Job ID') A JBVHID 10 TEXT('Vehicle ID') A JBFRMFIRST 30 TEXT('From First Name') A JBFRMINIT 1 TEXT('From Initial') A JBFRMLAST 30 TEXT('From Last Name') A JBFRMSTR1 50 TEXT('From Street 1') A JBFRMSTR2 50 TEXT('From Street 2') A JBFRMCITY 20 TEXT('From City') A JBFRMSTATE 2 TEXT('From State') A JBFRMZIP5 5 TEXT('From Zip Code') A JBFRMCNTRY 30 TEXT('From Country') A JBFRMPH1 10P 0 TEXT('Telephone 1') A JBFRMPH2 10P 0 TEXT('Telephone 2') A JBFRMPCELL 10P 0 TEXT('Cell phone') A JBTOSTR1 50 TEXT('To Street 1') A JBTOSTR2 50 TEXT('To Street 2') A JBTOCITY 20 TEXT('To City') A JBTOSTATE 2 TEXT('To State') A JBTOZIP5 5 TEXT('To Zip Code') A JBTOCNTRY 30 TEXT('To Country') A K JBID
This file already exists in the ASNAMRSAMP library, or if you want you can recreate it in your own XXCAROLINA library out of the DDS code.
Let's modify the navigation bar of the Job screen, in the JobRec record format, to show the Job ID. We know that the Job ID is in the JBID field of the JOBVEH01 file so we'll make this field part of the MobileDF record format. Remove the XXXX from the text in the navigation bar, and add after the </span>
a DdsCharField to declare the OutputOnly field JBID with the same type and length as in the JOBVEH01 file. Remember that field names go in the Alias property, and set the ID to the same name, prefixed with JobRec_
. Use PanelTitle for its CssClass to match the preceding text:
<span class="PanelTitle">Job: </span>
<mdf:DdsCharField ID="JobRec_JBID" runat="server" CssClass="PanelTitle" Alias="JBID" Length="10" Usage="OutputOnly" />
Build and Export the page.
Add the code to the RPG program to read the record with the JBID key provided by the user in the Job Search screen. Add code to handle *In07 and *In08 (previous record and next record):
0002.00 FJOBVEH01 IF E K DISK ... 0050.00 C BYJOBID BegSr 0051.00 C SrchJob Tag 0052.00 C ExFmt JOBSRCH 0053.00 C If *In12 0054.00 C LeaveSr 0055.00 C EndIf 0056.00 C JBID Chain JOBVEH01 3030 0057.00 C If *In30 0058.00 C GoTo SrchJob 0059.00 C EndIf 0060.00 C ShwNxtPrv Tag 0061.00 C ExSr SHOWJOB 0062.00 C Select 0063.00 C When *In12 0064.00 C GoTo SrchJob 0065.00 C When *In07 0066.00 C ReadP JOBVEH01 3030 0067.00 C When *In08 0068.00 C Read JOBVEH01 3030 0069.00 C EndSl 0070.00 C If *In30 0071.00 C JBID Chain JOBVEH01 3030 0072.00 C GoTo ShwNxtPrv 0073.00 C EndIf 0074.00 C If *In07 or *In08 0075.00 C GoTo ShwNxtPrv 0076.00 C EndIf 0077.00 C EndSr
Compile your RPG program, and run the app from VS. To test, the valid Job ID keys go from J01 to J20. You should see something like Figure 25:
![]() |
![]() |
This lesson continues the work we started in lesson 3. In this lesson we will:
We start with the first panel, which will display a hyperlink to an external website showing generic information for the particular brand, model, and year of vehicle. For this we'll use the DdsLink control. Many of the Mobile RPG controls support two ways to handle text values via properties. You can either provide a constant at design time, when you are creating the MobileDF, or at runtime, via fields set in the RPG program. The properties that support this behavior follow this naming convention: XyzValue, which is used to set a design-time constant, and XyzFieldName paired with XyzFieldLength, which declare a field in the record format that will contain the value at runtime. In the DdsLink both the Text to display and the Url to link to are of this kind. This control makes it easy to set a hyperlink on the page, so that the hyperlink will be invoked when the user clicks or taps on the text. We start by deleting the placeholder text and adding a DdsLink control in its place. We'll use fields to control the text to be shown and the url to jump to. Set the name of the field for the text to VEHDESC
with a length of 50, and set the url field to SITESPECS
with a length of 128:
<mdf:DdsLink ID="DdsLink1" runat="server" TextFieldLength="50" UrlFieldName="SITESPECS" UrlFieldLength="128" TextFieldName="VEHDESC" />
The information to create the Text and the Url comes from the VHMAKE
, VHMODEL
, and VHYEAR
fields in the logical file JOBVEH01. For the Text, we'll show Year Make Model, e.g. 2006 Jeep Wrangler Unlimited. For the Url, we'll be using www.edmunds.com (a very popular new and used auto information website). They have a comprehensive catalog for people looking into buying or selling vehicles.
When you use the www.edmunds.com site you can look for a particular vehicle specs and inspect the URL. They have their pages organized so that the spec HTML file is saved under a make/model/year folder structure. For example, to get to the 2003 Toyota Tundra, you can just type the following URL on your website:
http://www.edmunds.com/toyota/tundra/2003/features-specs.html
so we'll construct that string out of the vehicle data. We just need to ensure that it's in lower case. Edit the RPG code to set both the VEHDESC and SITESPECS fields:
0004.00 Dlower C Const('abcdefghijklmnopqrstuvwxyz') 0005.00 DUPPER C Const('ABCDEFGHIJKLMNOPQRSTUVWXYZ') ... 0045.00 C SHOWJOB BegSr 0046.00 C Eval VEHDESC = %char(VHYEAR) + ' ' + 0047.00 C %trimr(VHMAKE) + ' ' + 0048.00 C %trimr(VHMODEL) 0049.00 C Eval SITESPECS = 'http://www.edmunds.com/' + 0050.00 C %trimr(VHMAKE) + '/' + 0051.00 C %trimr(VHMODEL) + '/' + 0052.00 C %char(VHYEAR) + '/features-specs.html' 0053.00 C UPPER:lower XLATE SITESPECS SITESPECS 0054.00 C ExFmt JobRec
Build and export the page, and compile your program. Run the app, search for job J09 (you can search for any job from J01 to J20). You should see something like Figure 26:
The second panel contains the From address, To address, Name, and Telephone numbers. Go to the panel and remove the placeholder text. Add two DdsCharFields for the From address. Set their Alias properties to FromStr
and FromCSZ
, Length 60 for both, and OutputOnly for Usage. Add a line break between the fields, and a line break and a horizontal rule (<hr />
) after both fields:
<mdf:DdsCharField ID="JobRec_FromStr" runat="server" CssClass="DdsCharField" Alias="FromStr" Usage="OutputOnly" Length="60" />
<br />
<mdf:DdsCharField ID="JobRec_FromCSZ" runat="server" CssClass="DdsCharField" Alias="FromCSZ" Usage="OutputOnly" Length="60" />
<br />
<hr />
Repeat for the To address components, same property values, use ToStr
and ToCSZ
for the Alias names:
<mdf:DdsCharField ID="JobRec_ToStr" runat="server" CssClass="DdsCharField" Alias="ToStr" Usage="OutputOnly" Length="60" />
<br />
<mdf:DdsCharField ID="JobRec_ToCSZ" runat="server" CssClass="DdsCharField" Alias="ToCSZ" Usage="OutputOnly" Length="60" />
<br />
<hr />
Add one more char field for the full name of the contact, set its Alias to FullName
, and declare it OutputOnly and Length 60. Add a line break:
<mdf:DdsCharField ID="JobRec_FullName" runat="server" CssClass="DdsCharField" Alias="FullName" Usage="OutputOnly" Length="60" />
<br />
Looking at the data file we see that there may be up to three phone numbers for the contact. Let's add a DdsConstant and a DdsDecField for the first phone number. Set the Text property of the constant to Phone 1:
and give the DdsDecField the same Alias name as the file field, JBFRMPH1
, with same Length and Decimals, 10 and 0. Let's also use an editword of 0( )& -
, appropriate for displaying phone numbers. Now, what happens if there is no telephone number? In traditional IBM i DDS we can condition the visibility of constants and fields using indicator expressions. It's the same with MobileDFs. For both controls, look for the property named VisibleCondition and set it to 51.
This means that the visibility of these controls will be dependent on the value of indicator 51. Add a line break. Repeat for JBFRMPH2
and JBFRMPCELL
, conditioning their visibility with indicators 52 and 53, respectively:
<mdf:DdsConstant ID="DdsConstant2" runat="server" CssClass="DdsConstant" Text="Phone 1:" VisibleCondition="51" />
<mdf:DdsDecField ID="JobRec_JBFRMPH1" runat="server" CssClass="DdsDecField" Alias="JBFRMPH1" EditWord="0( )& - " VisibleCondition="51" Length="10" Decimals="0" />
<br />
<mdf:DdsConstant ID="DdsConstant3" runat="server" CssClass="DdsConstant" Text="Phone 2:" VisibleCondition="52" />
<mdf:DdsDecField ID="JobRec_JBFRMPH2" runat="server" CssClass="DdsDecField" Alias="JBFRMPH2" EditWord="0( )& - " VisibleCondition="52" Length="10" Decimals="0" />
<br />
<mdf:DdsConstant ID="DdsConstant4" runat="server" CssClass="DdsConstant" Text="Mobile:" VisibleCondition="53" />
<mdf:DdsDecField ID="JobRec_JBFRMCELL" runat="server" CssClass="DdsDecField" Alias="JBFRMPCELL" EditWord="0( )& - " VisibleCondition="53" Length="10" Decimals="0" />
<br />
Build and Export the page. Edit the RPG source and add the operations to compute FromStr, FromCSZ, ToStr, ToCSZ, FullName, and the value of the visibility indicators *In51, *In52, and *In53:
... 0053.00 C UPPER:lower XLATE SITESPECS SITESPECS 0054.00 C Eval FromStr = %trimr(JBFRMSTR1) + ' ' + 0055.00 C %trimr(JBFRMSTR2) 0056.00 C Eval FromCSZ = %trimr(JBFRMCITY) + ', ' + 0057.00 C %trimr(JBFRMSTATE) + ' ' + 0058.00 C %trimr(JBFRMZIP5) 0059.00 C Eval ToStr = %trimr(JBTOSTR1) + ' ' + 0060.00 C %trimr(JBTOSTR2) 0061.00 C Eval ToCSZ = %trimr(JBTOCITY) + ', ' + 0062.00 C %trimr(JBTOSTATE) + ' ' + 0063.00 C %trimr(JBTOZIP5) 0064.00 C Eval FullName = %trimr(JBFRMFIRST) 0065.00 C FullName Cat JBFRMINIT:1 FullName 0066.00 C FullName Cat JBFRMLAST:1 FullName 0067.00 C Eval *In51 = JBFRMPH1 > 0 0068.00 C Eval *In52 = JBFRMPH2 > 0 0069.00 C Eval *In53 = JBFRMPCELL > 0 0070.00 C ExFmt JOBREC ...
Compile the program, run the app from VS. You should see something like Figure 27:
We don't want the driver to get lost, so we will now add a map to show the delivery route in the Job Record. Mobile RPG provides a very useful construct for adding a Google Map to an app, the DdsGMap control. The DdsGMap presents the user with a Google Map containing optional markers, routes, and terrain views. To the RPG program, the DdsGMap presents itself as a Subfile. Each record of the subfile represents an address to be included in the map. You can view the exact subfile representation of the DdsGMap (and other) controls if you save the DDS when exporting. The DdsGMap control has several properties that name the Subfile Control, the Subfile, the Address Field in the subfile record format, and the Clear Indicator to clear the subfile.
Start by adding a DdsGMap control into the third panel, replacing the placeholder text (Map) we added earlier. In the Properties window, set the SubfileControlName to JobAddrSfc
, the SubfileName to JobAddrSfl
, the AddressField to FullAddr
, AddressFieldLength to 128, and ClearIndicator to indicator 97. With this, we just declared a one-field subfile and subfile control in the JobRec record format. For the cosmetic part of the map, set MapType to Roadmap
, Marks to Route
, and Height to 200px. We learned earlier that we can add grouping text to a panel, so add Suggested Route as grouping text to the parent panel of the DdsGMap:
<mdf:DdsPanel ID="DdsPanel4" runat="server" CssClass="DdsPanel" GroupingText="Suggested Route">
<mdf:DdsGMap ID="DdsGMap1" runat="server" AddressField="FullAddr" AddressFieldLength="128" ClearIndicator="97" SubfileControlName="JobAddrSfc" SubfileName="JobAddrSfl" Marks="Route" Height="200px" />
</mdf:DdsPanel>
Build and Export the page. In the RPG source, we need to specify the use of the subfile and declare the RRN field. After that, write a subroutine, LoadMap, that clears the subfile control and writes the To and From addresses into the subfile:
0001.00 FCAROMAIN CF E WORKSTN Handler('MOBILERPG') 0002.00 F Sfile(JobAddrSfl:JobAddrRrn) ... 0006.00 DJobAddrRrn S 4 0 ... 0073.00 C Eval *In53 = JBFRMCELL > 0 0074.00 C ExSr LoadMap 0075.00 C ExFmt JOBREC ... 0093.00 C LoadMap BegSr 0094.00 C SetOn 97 0095.00 C Write JobAddrSfc 0096.00 C Eval FullAddr = %trim(JBFRMSTR1) + ' ' + 0097.00 C %trimr(JBFRMSTR2) + ', ' + 0098.00 C %trimr(JBFRMCITY) + ' ' + 0099.00 C %trimr(JBFRMSTATE) + ' ' + 0100.00 C %trimr(JBFRMZIP5) 0101.00 C Eval JobAddrRrn = 1 0102.00 C Write JobAddrSfl 0103.00 C Eval FullAddr = %trimr(JBTOSTR1) + ' ' + 0104.00 C %trimr(JBTOSTR2) + ', ' + 0105.00 C %trimr(JBTOCITY) + ' ' + 0106.00 C %trimr(JBTOSTATE) + ' ' + 0107.00 C %trimr(JBTOZIP5) 0108.00 C Eval JobAddrRrn = 2 0109.00 C Write JobAddrSfl 0110.00 C Eval *In97 = *off 0111.00 C Write JobAddrSfc 0112.00 C EndSr
Compile your RPG program, and run the app from VS. You should see something like Figure 28:
To complete the Job screen we need to show the user the pictures of the car at pickup time and have a way to add more pictures at pickup and delivery. To complete this section you need to
install the sample pictures that ship with Mobile RPG in IFS of your IBM i. See appendix C for instructions. We will be using the DdsImage control. It is a very simple but powerful control that can display one or many images that are stored in IFS on your IBM i. Some wildcard special characters:
'?' matches a single character
'*' matches 0 or more characters All it needs to be given
is the path and the file name. Multiple images are supported via wildcards in the file name.
We'll start with the Origin images, and repeat the process for the Destination images. Add a DdsImage control in the fourth panel, replacing the placeholder text. Set the grouping text of the panel to At Origin. Set the DdsImage DirectoryField to ImgPath
, DirectoryFieldLength to 250, NameField to ImgOrig
, NameFieldLength to 32, and give it a Width of 200 and Height of 150. Repeat for the fifth panel, setting the grouping text to At Destination, and all the properties of the image to the same values except NameField, which should be ImgDest
:
<mdf:DdsPanel ID="DdsPanel5" runat="server" CssClass="DdsPanel" GroupingText="At Origin">
<mdf:DdsImage ID="DdsImage1" runat="server" DirectoryField="ImgPath" DirectoryFieldLength="250" NameField="ImgOrig" NameFieldLength="32" Width="200" Height="150" />
</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel6" runat="server" CssClass="DdsPanel" GroupingText="At Destination">
<mdf:DdsImage ID="DdsImage2" runat="server" DirectoryField="ImgPath" DirectoryFieldLength="250" NameField="ImgDest" NameFieldLength="32" Width="200" Height="150" />
</mdf:DdsPanel>
Build and Export the page. Edit the RPG source and add code to set the fields ImgPath, ImgOrig, and ImgDest. The images are stored in the path /AsnaMrSamp/MRCarolina/VehiclePhotos/<vhid>
, where<vhid>
is the vehicle ID, a 4 digit number that goes from 1001 to 1020. The file names are stored as JPEG files with the pattern o-<vhid>-????.jpg
for pictures at origin, and d-<vhid>-????.jpg
for pictures at destination. The RPG code is then:
... 0010.00 DVPath C Const('/AsnaMrSamp/MRCarolina/- 0011.00 D VehiclePhotos/') ... 0077.00 C Eval ImgPath = %trimr(VPath) + %trimr(VHID) 0078.00 C Eval ImgOrig = 'o-' + %trimr(VHID) + '-????.jpg' 0079.00 C Eval ImgDest = 'd-' + %trimr(VHID) + '-????.jpg' ...
Compile the program and run the app. You should see something like Figure 29:
To finish these panels let's add the upload capability. Upload will only work in browsers and mobile devices that support it. We'll start with the Origin properties: set UploadCondition to *true
, UploadCaption to Add Picture, UploadKey to F5, UploadedNameField to NewImgOrig
, with UploadedNameFieldLength set to 32. Set the same values for Destination, except set UploadedNameField to NewImgDest
.
When the control is rendered, the user will see a set of widgets on the screen that will allow him to select an image from the gallery of this device or to take a picture if the device has an integrated camera. When the user is satisfied with the chosen image, he will be able to touch or press a button to start the upload process.
The name given to the file will be generated by the DdsImage control based on the value of the field specified in NameField and reported back in the field specified in the UploadedNameField property. If the value given in NameField contains wildcards, the control will generate a new name following the pattern of the wildcard name, with random numbers used in place of the wild cards. The location in IFS of the new file will be in the field specified in the DirectoryField property.
<mdf:DdsPanel ID="DdsPanel5" runat="server" CssClass="DdsPanel" GroupingText="At Origin">
<mdf:DdsImage ID="DdsImage1" runat="server" DirectoryField="ImgPath" DirectoryFieldLength="250" NameField="ImgOrig" NameFieldLength="32" Width="200" Height="150" UploadCaption="Add Picture" UploadCondition="*true" UploadedNameField="NewImgOrig" UploadedNameFieldLength="32" UploadKey="Enter" />
</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel6" runat="server" CssClass="DdsPanel" GroupingText="At Destination">
<mdf:DdsImage ID="DdsImage2" runat="server" DirectoryField="ImgPath" DirectoryFieldLength="250" NameField="ImgDest" NameFieldLength="32" Width="200" Height="150" UploadCondition="*true" UploadCaption="Add Picture" UploadedNameField="NewImgDest" UploadedNameFieldLength="32" UploadKey="Enter" />
</mdf:DdsPanel>
Build and Export the page. Change the RPG code to detect whether a new file was uploaded to refresh the screen:
... 0081.00 C ExFmt JOBREC 0082.00 C Select ... 0089.00 C When *In05 or NewImgOrig <> *blanks 0090.00 C or NewImgDest <> *blanks 0091.00 C GoTo ShwJob ...
Compile the RPG code and run the app. You should see something like Figure 30:
When we display a phone number using a DdsDecField, as we did above, a mobile phone that accesses the page won't be able recognize it as such. If the user wants to call that number he would need to select, copy, and paste it into his phone dialer. What we really want is to let the phone know that the number is indeed a phone number so it will let the user dial by simply tapping or clicking. The DdsLink control provides this capability via the AppType property, shown in Figure 31:
As you can see from the image, there are several possible values for AppType. Here we will only explore the Telephone value. See your documentation for details on the others.
Let's change the MobileDF to support phone number dialing in the page. We'll start with Phone 1, and repeat for Phone 2 and Mobile. Add a DdsLink control replacing the JobRec_JOBFRMPH1 DdsDecField control. Set its AppType to Telephone. We need two character fields, one to hold the Text we want displayed, and one for the Url, in this case the telephone number, that we want to link to. Let's use JBFRMPH1C as the TextFieldName, and set its length to 15, and JBFRMPH1L as the UrlFieldName with a length of 10. Set its VisibleCondition to indicator 51:
<mdf:DdsLink ID="DdsLink2" runat="server" AppType="Telephone" TextFieldLength="15" TextFieldName="JBFRMPH1C" UrlFieldLength="10" UrlFieldName="JBFRMPH1L" VisibleCondition="51" />
Repeat for Phone 2 and Mobile, using indicators 52 and 53:
<mdf:DdsLink ID="DdsLink3" runat="server" AppType="Telephone" TextFieldLength="15" TextFieldName="JBFRMPH2C" UrlFieldLength="10" UrlFieldName="JBFRMPH2L" VisibleCondition="52" />
...
<mdf:DdsLink ID="DdsLink4" runat="server" AppType="Telephone" TextFieldLength="15" TextFieldName="JBFRMCELLC" UrlFieldLength="10" UrlFieldName="JBFRMCELLL" VisibleCondition="53" />
Build and Export the page. Change the RPG code to set the phone and link fields. Note that the phone field of the MobileDF record format used to be decimal but now it's character, so we need to use %EDITW to format the value:
... 0073.00 C FullName Cat JBFRMLAST:1 FullName 0074.00 C Eval JBFRMPH1C = %editw(JBFRMPH1: 0075.00 C '0( )& - ') 0076.00 C Eval JBFRMPH1L = %char(JBFRMPH1) 0077.00 C Eval JBFRMPH2C = %editw(JBFRMPH2: 0078.00 C '0( )& - ') 0079.00 C Eval JBFRMPH2L = %char(JBFRMPH2) 0080.00 C Eval JBFRMCELLC = %editw(JBFRMPCELL: 0081.00 C '0( )& - ') 0082.00 C Eval JBFRMCELLL = %char(JBFRMPCELL) 0083.00 C Eval *In51 = JBFRMPH1 > 0 ...
Compile the program and run the app. You should see the phone numbers now appear as clickable links and the phone will initiate a call if a phone number is tapped or clicked. See Figure 32:
![]() |
![]() |
It's time to create the By Body and Size screen, shown in Figure 33:
In this lesson we will:
The main component of this screen is the chart which shows the information in the VEHICLE file in column chart form. Vehicles are sorted by size, and within size, by body type. The user can click or tap on a column of the chart to bring up the list of vehicles corresponding to that size and body type. The Mobile RPG DdsChart control provides the support for showing summarized information in chart form. Just like the DdsGMap, the DdsChart creates a subfile so the programmer can easily load the chart with the information to be displayed.
Before we start lets explore the DdsChart control and the naming scheme it uses. Figure 34 shows a column chart with four Categories.
Each category has two data points, with the first one belonging to the first series, Series A, and the second one to the second series, Series B. The number of Categories and Series may vary according to the application data. In Carolina we have three vehicle Categories: Compact, Medium, and Large, and three Series: Sedans, Trucks, and Vans.
Each Category in the chart represents a record in a subfile, and the fields in the subfile include the Category name, and the values of each Series for this category. Adding a new category to the chart is as simple as setting field values and writing a subfile record in the RPG program.
All of the chart titles and legends are configurable, either via a design-time constant, or with a field to be set at runtime, using the -Field/-FieldLength convention to specify the field's name and length. It's worth noting that some of these fields end up as part of the subfile control, while others end up as part of the subfile as indicated in Figure 35:
Finally, the Fonts used in the different labels and values are also configurable at design time. See Figure 36:
We are now ready to begin. Start by adding a new record format, set its Alias to BodySize
(and its ID to _BodySize
). Inside the record add a <h2>
header with the text By Body and Size, and follow it with a DdsPanel to contain the chart. Use the DdsChartPanel CssClass, and set the BackColor to White, BorderColor to Black, BorderStyle to Solid, and BorderWidth to 3px.
<mdf:DdsRecord ID="_BodySize" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="BodySize">
<h2>By Body and Size</h2>
<mdf:DdsPanel ID="DdsPanel7" runat="server" CssClass="DdsChartPanel" BorderStyle="Solid" BorderWidth="3px" BorderColor="Black" BackColor="White">
</mdf:DdsPanel>
</mdf:DdsRecord>
Add a DdsChart inside the panel. Let's start with the subfile control part. Set SubfileControlName to ByBdySzSfc
, set ChartTitle to Inventory
, CategoryTitle to Sizes
. Set the ClearIndicator to 99, ChartType to Column, LegendPosition to Right, DataLabelPosition to Above. Change Height to 160px, and Width to 290px. Set Backcolor to WhiteSmoke (Hex={F5,F5,F5}).
For the subfile part, set SubfileName to ByBdySzSfl
. Set CategoryField to ChSize
with a CategoryFieldLength of 10. Click the ...
button next to the SeriesList property. This will bring up a window where we can define the series that will be part of the chart. See Figure 37:
Change the default series Color to Yellow
, Legend to Sedans
, DataField to Sedans
, DataFieldLength to 5, and DataFieldDecimals to 0. Click Add
to add a second series. Set its Color to Red
, Legend to Trucks
, DataField to Trucks
, DataFieldLength to 5, and DataFieldDecimals to 0. Click Add
again to add the last series, and set its Color to Blue
, Legend to Vans
, DataField to Vans
, DataFieldLength to 5, and DataFieldDecimals to 0.
This is what your DdsChart should look like:
<mdf:DdsChart ID="DdsChart1" runat="server" Height="160" Width="290" CategoryTitle="Sizes" ChartTitle="Inventory" ClearIndicator="99" SubfileControlName="ByBdySzSfc" DataLabelPosition="Above" BackColor="WhiteSmoke" CategoryField="ChSize" CategoryFieldLength="10" SubfileName="ByBdySzSfl">
<SeriesList>
<mdf:Series Color="Yellow" DataField="Sedans" DataFieldDecimals="0" DataFieldLength="5" Legend="Sedans" />
<mdf:Series Color="Red" DataField="Trucks" DataFieldDecimals="0" DataFieldLength="5" Legend="Trucks" />
<mdf:Series Color="Blue" DataField="Vans" DataFieldDecimals="0" DataFieldLength="5" Legend="Vans" />
</SeriesList>
</mdf:DdsChart>
To complete the screen we need to add the navigation bar. Add a DdsBar right before the <h2>
header, with ID set to BodySize_NavBar
. Add two more DdsBarSegments to complete three. In the first segment add a DdsButton with CssClass set to NavButton, AidKey set to F12, and Text set to Home
. In the second segment add a <span>
with class set to PanelTitle
. Inside the span write the screen title Select Subset. In the last segment add a DdsButton with ButtonStyle set to Image. Set the Image property to ~/Themes/Current/Images/Refresh.png
, and the AidKey to F5. Now go back to the DdsRecord and set the NavigationBarControlID to BodySize_NavBar
, and AttnKeys to F5 05;F12 12;:
<mdf:DdsRecord ID="_BodySize" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="BodySize" NavigationBarControlID="BodySize_NavBar" AttnKeys="F5 05;F12 12;">
<mdf:DdsBar ID="BodySize_NavBar" runat="server" CssClass="DdsBar">
<mdf:DdsBarSegment ID="DdsBarSegment10" runat="server" Alignment="Left">
<mdf:DdsButton ID="DdsButton12" runat="server" CssClass="NavButton" AidKey="F12" Text="Home" />
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment11" runat="server">
<span class="PanelTitle">Select Subset</span>
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment12" runat="server" Alignment="Right">
<mdf:DdsButton ID="DdsButton13" runat="server" ButtonStyle="Image" AidKey="F5" Image="~/Themes/Current/Images/Refresh.png" />
</mdf:DdsBarSegment>
</mdf:DdsBar>
The last thing we need to do is detect when the user taps the chart and, based on what was tapped, show the required subset of the vehicles. For this, the DdsChart provides one property called SelectSeriesField that defines a decimal field that will be part of the subfile control. At runtime, the field indicates which of the chart components that are part of the subfile control was tapped. And it has another property, called SelectDataField, that defines a decimal field that will be part of the subfile. At runtime, the field indicates which of the chart components that are part of the subfile was tapped. Note that this field is part of the subfile, so finding the category that was tapped is as simple as READC. These two properties have companion properties to define the Aid key that indicates the user action, SelectSeriesKey and SelectDataKey. Figure 38 shows what the value of the fields should be for the different taps the user may perform:
Let's add these fields to the chart, set SelectDataField to BdySzData
, and SelectSeriesField to BdySzSries
. Use F7 for the SelectDataKey, and F8 for the SelectSeriesKey. Enable both keys in the record FuncKeys, pairing them with indicators 7 and 8 respectively:
<mdf:DdsRecord ID="_BodySize" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="BodySize" NavigationBarControlID="BodySize_NavBar" AttnKeys="F5 05;F12 12;" FuncKeys="F7 07;F8 08;">
...
<mdf:DdsChart ID="DdsChart1" runat="server" Height="160" Width="290" CategoryTitle="Sizes" ChartTitle="Inventory" ClearIndicator="99" SubfileControlName="ByBdySzSfc" DataLabelPosition="Above" BackColor="WhiteSmoke" CategoryField="ChSize" CategoryFieldLength="10" SelectDataField="BdySzData" SelectDataKey="F7" SelectSeriesField="BdySzSries" SelectSeriesKey="F8" SubfileName="ByBdySzSfl">
Build and Export the page. Now, the first task to do in the RPG code is load the chart with the summarized data. We will use another logical file, VEHJOB01, which is very similar to JOBVEH01, but with a three-part key, indexed by size (Category), then by body type (Series), and finally by vehicle ID:
A R RVEHJOB JFILE(VEHICLE JOB) A J JOIN(VEHICLE JOB) A JFLD(VHID JBVHID) A VHID 10 TEXT('ID') A VHVIN 17 TEXT('Serial Number') A VHMAKE 20 TEXT('Make') A VHMODEL 40 TEXT('Model') A VHYEAR 4P 0 TEXT('Year') A VHPLATE 10 TEXT('Plate') A VHCOLOR 20 TEXT('Color') A VHRUNS 1 TEXT('Is drivable') A VHBODY 10 TEXT('Body Style') A VHSIZE 10 TEXT('Size') A JBID 10 TEXT('Job ID') A JBVHID 10 TEXT('Vehicle ID') A JBFRMFIRST 30 TEXT('From First Name') A JBFRMINIT 1 TEXT('From Initial') A JBFRMLAST 30 TEXT('From Last Name') A JBFRMSTR1 50 TEXT('From Street 1') A JBFRMSTR2 50 TEXT('From Street 2') A JBFRMCITY 20 TEXT('From City') A JBFRMSTATE 2 TEXT('From State') A JBFRMZIP5 5 TEXT('From Zip Code') A JBFRMCNTRY 30 TEXT('From Country') A JBFRMPH1 10P 0 TEXT('Telephone 1') A JBFRMPH2 10P 0 TEXT('Telephone 2') A JBFRMPCELL 10P 0 TEXT('Cell phone') A JBTOSTR1 50 TEXT('To Street 1') A JBTOSTR2 50 TEXT('To Street 2') A JBTOCITY 20 TEXT('To City') A JBTOSTATE 2 TEXT('To State') A JBTOZIP5 5 TEXT('To Zip Code') A JBTOCNTRY 30 TEXT('To Country') A K VHSIZE A K VHBODY A K VHID
We'll need a loop to iterate over all the records, in key order, to count within each vehicle size how many of each body type there are. Once we are done with a vehicle size, we have to write the subfile record. Here's the code:
0001.00 FCAROMAIN CF E WORKSTN Handler('MOBILERPG') 0002.00 F Sfile(JobAddrSfl:JobAddrRrn) 0003.00 F Sfile(ByBdySzSfl:ByBdySzRrn) ... 0006.00 FVEHJOB01 IF E K DISK ... 0009.00 DByBdySzRrn S 4 0 ... 0136.00 C BYBDYSZE BegSr 0137.00 C ReLdBdySz Tag 0138.00 C ExSr LdBdySzCh 0139.00 C ExFmt BodySize 0140.00 C Select 0141.00 C When *In07 0142.00 C* Data tapped 0143.00 C ReadC ByBdySzSfl 0144.00 C Eval VHSIZE = CHSIZE 0145.00 C Select 0146.00 C When BdySzData = 2 0147.00 C Eval VHBODY = 'Sedan' 0148.00 C When BdySzData = 3 0149.00 C Eval VHBODY = 'Truck' 0150.00 C When BdySzData = 4 0151.00 C Eval VHBODY = 'Van' 0152.00 C EndSl 0153.00 C ExSr BdySzSubst 0154.00 C When *in08 0155.00 C* Chart tapped 0156.00 C When *In12 0157.00 C LeaveSr 0158.00 C EndSl 0159.00 C GoTo ReLdBdySz 0160.00 C EndSr 0161.00 0162.00 C* Load the By Body and Size Chart 0163.00 C LdBdySzCh BegSr 0164.00 C Eval *In99 = *On 0165.00 C Write ByBdySzSfc 0166.00 C Eval ByBdySzRrn = 0 0167.00 C *LOVAL SetLL VEHJOB01 0168.00 C Eval ChSize = *Blanks 0169.00 C Eval *In30 = *Off 0170.00 C DoW *In30 = *Off 0171.00 C Read VEHJOB01 3030 0172.00 C If VHSIZE <> CHSIZE or *In30 0173.00 C If CHSIZE <> *Blanks 0174.00 C Eval ByBdySzRrn += 1 0175.00 C Write ByBdySzSfl 0176.00 C EndIf 0177.00 C Eval Sedans = 0 0178.00 C Eval Trucks = 0 0179.00 C Eval Vans = 0 0180.00 C Eval CHSIZE = VHSIZE 0181.00 C EndIf 0182.00 C Select 0183.00 C When VHBODY = 'Sedan' 0184.00 C Eval Sedans += 1 0185.00 C When VHBODY = 'Truck' 0186.00 C Eval Trucks += 1 0187.00 C When VHBODY = 'Van' 0188.00 C Eval Vans += 1 0189.00 C EndSl 0190.00 C EndDo 0191.00 C Eval *In99 = *Off 0192.00 C Write ByBdySzSfc 0193.00 C EndSr 0194.00 0195.00 C* Load subset of vehicles 0196.00 C BdySzSubst BegSr 0197.00 C EndSr
Compile the RPG program and run the app. Your browser should look like Figure 39. If you are using a mobile device you can drag the tab and show the data in table form.
![]() |
![]() |
When the Chart we created in Lesson 5 is tapped we need to show the vehicle Subset screen, in Figure 40, listing the subset of vehicles that was selected.
In this lesson we will:
The main component of this screen is the list control which shows a subset of the vehicles in list form. The chevron marker >
on the right of each vehicle is a clickable item that will cause the app to show the Job information for that vehicle. The Mobile RPG DdsList control provides a very easy way to construct this kind of list. Just like the DdsGMap and the DdsChart, the DdsList creates a subfile so the programmer can easily create the list with the required information.
Before we start lets explore the DdsList control and the naming scheme it uses. See Figure 41.
Each item of information, which corresponds to a subfile record, is expressed with a Category, a Text, a Detail, an Image, and a Navigable field (Navigable tells whether the item responds to a user tap or click). Two more fields that are not visible are the Selected field and the Value field. Selected indicates which record appears selected when displayed on the screen, and Value is a field that can contain any arbitrary information associated with this record. Each of these parts of the list record are defined via properties of the DdsList control. If more than one record has the same Category, the DdsList control will show them grouped together.
Let's begin by adding a new DdsRecord, naming it JobList as its Alias, and _JobList as its ID. Add a DdsBar with three segments, just like the one we added in Lesson 5: set the DdsBar's ID to JobList_NavBar. Add two more DdsBarSegments to complete three. In the first segment add a DdsButton with CssClass set to NavButton, AidKey set to F12, and Text set to Back
. In the second segment add a <span>
with class set to PanelTitle
. Inside the span write the screen title Vehicle Subset. In the last segment add a DdsButton with ButtonStyle set to Image. Set the Image property to ~/Themes/Current/Images/Refresh.png
, and the AidKey to F5. Now go back to the DdsRecord and set the NavigationBarControlID to JobList_NavBar
, and AttnKeys to F5 05;F12 12;:
<mdf:DdsRecord ID="_JobList" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="JobList" AttnKeys="F5 05;F12 12;" NavigationBarControlID="JobList_NavBar">
<mdf:DdsBar ID="JobList_NavBar" runat="server" CssClass="DdsBar">
<mdf:DdsBarSegment ID="DdsBarSegment13" runat="server" Alignment="Left">
<mdf:DdsButton ID="DdsButton14" runat="server" CssClass="NavButton" AidKey="F12" Text="Back" />
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment14" runat="server">
<span class="PanelTitle">Vehicle Subset</span>
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment15" runat="server" Alignment="Right">
<mdf:DdsButton ID="DdsButton15" runat="server" ButtonStyle="Image" AidKey="F5" Image="~/Themes/Current/Images/Refresh.png" />
</mdf:DdsBarSegment>
</mdf:DdsBar>
</mdf:DdsRecord>
We are done with the navigation bar. Now add a DdsList after the DdsBar. Set its ListType to Navigation. Learn about the other list types in the Mobile RPG documentation. Set Backcolor to WhiteSmoke. Set SubfileControlName to JobListSfc
, SubfileName to JobListSfl
, and ClearIndicator to 90. Set CategoryField to VehCat
with a CategoryFieldLength of 30, TextField to VehDesc
with a TextFieldLength of 50, DetailField to VehCSZ
with a DetailFieldLength of 60 and a DetailAlignment set to Below. Set the SelectedField to Selected
. Set the ValueField to Cookie
, with a ValueFieldLength of 30. We will use the ValueField to keep track of the record key in the VEHJOB01 logical file, which is of length 30. We'll define a data structure in the program consisting of these key fields. Set NavigableField to *true, which means that all the records in the list are navigable. Finally, set the Image related properties: ImageLocation to HostIFS, ImageDirectoryField to ImgPath
, ImageDirectoryFieldLength to 250, ImageNameField to ImgName
, ImageNameFieldLength to 32.
<mdf:DdsList ID="DdsList1" runat="server" CssClass="DdsList" CssClassChevron="DdsListChevron"
CssClassDetail="DdsListDetail" CssClassImage="DdsListImage" CssClassList="DdsListList"
BackColor="WhiteSmoke" SubfileControlName="JobListSfc" SubfileName="JobListSfl" ClearIndicator="90"
SelectedField="Selected" CategoryField="VehCat" CategoryFieldLength="30"
DetailField="VehCSZ" DetailFieldLength="60"
NavigableField="*true" TextField="VehDesc" TextFieldLength="50" ValueField="Cookie" ValueFieldLength="30"
ImageDirectoryField="ImgPath" ImageDirectoryFieldLength="250" ImageNameField="ImgName" ImageNameFieldLength="32" />
Build and Export the page. In the RPG code we will load and write the DdsList subfile and show the Subset screen when a particular column in the By Body and Size chart is tapped or clicked. It will also show the Job screen when a particular vehicle is tapped or clicked in the list on Subset screen:
0001.00 FCAROMAIN CF E WORKSTN Handler('MOBILERPG') 0002.00 F Sfile(JobAddrSfl:JobAddrRrn) 0003.00 F Sfile(ByBdySzSfl:ByBdySzRrn) 0004.00 F Sfile(JobListSfl:JobListRrn) ... 0011.00 DJobListRrn S 4 0 0012.00 DTotListRec S 4 0 ... 0018.00 DCookie DS 0019.00 DVHID 0020.00 DVHSIZE 0021.00 DVHBODY 0022.00 0023.00 C KLSzBdy KList 0024.00 C KFld VHSIZE 0025.00 C KFld VHBODY 0026.00 C KLSzBdyID KList 0027.00 C KFld VHSIZE 0028.00 C KFld VHBODY 0029.00 C KFld VHID ... 0159.00 C BYBDYSZE BegSr 0160.00 C *Like Define VHBODY VEHBODY 0161.00 C *Like Define VHSIZE VEHSIZE 0162.00 C ReLdBdySz Tag 0163.00 C ExSr LdBdySzCh 0164.00 C ExFmt BodySize 0165.00 C Select 0166.00 C When *In07 0167.00 C* Data tapped 0168.00 C ReadC ByBdySzSfl 0169.00 C Eval VEHSIZE = CHSIZE 0170.00 C Select 0171.00 C When BdySzData = 1 0172.00 C Eval VEHBODY = *Blanks 0173.00 C When BdySzData = 2 0174.00 C Eval VEHBODY = 'Sedan' 0175.00 C When BdySzData = 3 0176.00 C Eval VEHBODY = 'Truck' 0177.00 C When BdySzData = 4 0178.00 C Eval VEHBODY = 'Van' 0179.00 C EndSl 0180.00 C ExSr BdySzSubst 0181.00 C When *In08 0182.00 C* Non-data tapped - do nothing 0183.00 C When *In12 0184.00 C LeaveSr 0185.00 C EndSl 0186.00 C GoTo ReLdBdySz 0187.00 C EndSr ... 0190.00 C LdBdySzCh BegSr 0191.00 C Eval *In99 = *On 0192.00 C Write ByBdySzSfc 0193.00 C Eval ByBdySzRrn = 0 0194.00 C *LOVAL SetLL VEHJOB01 0195.00 C Eval ChSize = *Blanks 0196.00 C Eval *In30 = *Off 0197.00 C DoW *In30 = *Off 0198.00 C Read VEHJOB01 3030 0199.00 C If VHSIZE <> CHSIZE or *In30 0200.00 C If CHSIZE <> *Blanks 0201.00 C Eval ByBdySzRrn += 1 0202.00 C Write ByBdySzSfl 0203.00 C EndIf 0204.00 C Eval Sedans = 0 0205.00 C Eval Trucks = 0 0206.00 C Eval Vans = 0 0207.00 C Eval CHSIZE = VHSIZE 0208.00 C EndIf 0209.00 C Select 0210.00 C When VHBODY = 'Sedan' 0211.00 C Eval Sedans += 1 0212.00 C When VHBODY = 'Truck' 0213.00 C Eval Trucks += 1 0214.00 C When VHBODY = 'Van' 0215.00 C Eval Vans += 1 0216.00 C EndSl 0217.00 C EndDo 0218.00 C Eval *In99 = *Off 0219.00 C Write ByBdySzSfc 0220.00 C EndSr ... 0231.00 C BdySzSubst BegSr 0232.00 C SubsetStrt Tag 0233.00 C ExSr LdSbstSfl 0234.00 C Eval TotListRec = JobListRrn 0235.00 C ExFmt JobList 0236.00 C Select 0237.00 C When *In12 0238.00 C LeaveSr 0239.00 C When *In05 0240.00 C GoTo SubsetStrt 0241.00 C EndSl 0242.00 C ReadC JobListSfl 0243.00 C Eval Selected = '0' 0244.00 C Update JobListSfl 0245.00 C NextOrPrev Tag 0246.00 C JobListRrn Chain JobListSfl 3030 0247.00 C N30KLSzBdyID Chain VEHJOB01 3030 0248.00 C If *In30 0249.00 C GoTo SubsetStrt 0250.00 C EndIf 0251.00 C ExSr SHOWJOB 0252.00 C Select 0253.00 C When *In12 0254.00 C LeaveSr 0255.00 C When *In07 and JobListRrn > 1 0256.00 C Eval JobListRrn -= 1 0257.00 C When *In08 and JobListRrn < TotListRec 0258.00 C Eval JobListRrn += 1 0259.00 C EndSl 0260.00 C GoTo NextOrPrev 0261.00 C EndSr ... 0264.00 C LdSbstSfl BegSr 0265.00 C Eval *In90 = *On 0266.00 C Write JobListSfc 0267.00 C Eval JobListRrn = 0 0268.00 C Eval VHSIZE = VEHSIZE 0269.00 C Eval VHBODY = VEHBODY 0270.00 C KLSzBdy SetLL VEHJOB01 0271.00 C Read VEHJOB01 3030 0272.00 C DoW *In30 = *off 0273.00 C Eval VehCat = %trimr(VHSIZE) + ' ' + 0274.00 C %trimr(VHBODY) 0275.00 C Eval VehDesc = %char(VHYEAR) + ' ' + 0276.00 C %trimr(VHMAKE) + ' ' + 0277.00 C %trimr(VHMODEL) 0278.00 C Eval VehCSZ = %trimr(JBFRMCITY) + ' ' + 0279.00 C %trimr(JBFRMSTATE) + ' ' + 0280.00 C %trimr(JBFRMZIP5) 0281.00 C Eval ImgPath = %trimr(vPath) + %trimr(VHID) 0282.00 C Eval ImgName = 'o-' + %trimr(VHID) + '-0001.jpg' 0283.00 C Eval JobListRrn += 1 0284.00 C Write JobListSfl 0285.00 C KLSzBdy ReadE VEHJOB01 3030 0286.00 C EndDo 0287.00 C Eval *In90 = *Off 0288.00 C Write JobListSfc 0289.00 C EndSr
Compile the program and run the app. You should see something like Figure 42:
![]() |
![]() |
![]() |
By body and Size, click or tap on "compact sedan" | Click or tap on "2008 Dodge" | Try the '<' and '>' buttons |
As more features are added to an App it may be desirable to break the code and Markup into several smaller modules. ASNA Mobile RPG follows the IBM i RPG development model in that programs can call other programs which use their own Display Files. In this lesson we will learn how to add a new Mobile Display File and how to call another program. For this we will create the Management Dashboard screen in a new Mobile Display File, and the code to handle it in a new RPG program. See Figure 43:
In this lesson we will:
We'll start by adding a new Mobile Display File to the application. In Solution Explorer, right-click the MobileDFs folder, select Add, and then Add New Item, as shown in Figure 44:
The Add New Item dialog will pop up, shown in Figure 45. On the left pane select Visual C#, on the central pane select ASNA Mobile RPG Display File. Change the aspx file name to Dashboard.aspx:
The new MobileDF already contains a DdsFile with one DdsRecord. The record has a navigation bar, already wired to the record, with three bar segments. Change the DdsRecord Alias to Charts
, and ID to _Charts
. In the navigation button that's in the first bar segment, change the AidKey to F12, and change the Text from End
to Home
. Add F12 linked to indicator 12 to the list of AttnKeys of the record. In the second (middle) bar segment set the title text to Dashboard
. In the third bar segment add a DdsButton with ButtonStyle set to Image, AidKey to F5, and the Image to the path of the Refresh.png image. Add F5 linked to indicator 5 to the list of AttnKeys of the record:
<mdf:DdsRecord id="_Charts" runat="server" style="position: relative;" Alias="Charts"
CssClass="DdsRecord" NavigationBarControlID="NavigationBar" FuncKeys="F3 03;" EraseFormats="*ALL" AttnKeys="F12 12;F5 05;" >
<mdf:DdsBar ID="NavigationBar" runat="server" CssClass="DdsBar">
<mdf:DdsBarSegment ID="DdsBarSegment1" runat="server" Alignment="Left">
<mdf:DdsButton ID="DdsButton1" ButtonStyle="Button" runat="server" CssClass="NavButton" AidKey="F12" Text="Home" />
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment2" runat="server" Alignment="Center">
<span class="PanelTitle" >Dashboard</span>
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment3" runat="server" Alignment="Right">
<mdf:DdsButton ID="DdsButton10" runat="server" ButtonStyle="Image" AidKey="F5" Image="~/Themes/Current/Images/Refresh.png" />
</mdf:DdsBarSegment>
</mdf:DdsBar>
After the navigation bar, we need two charts to display the Historical Revenue and the Top 10 states in sales. Start by adding two DdsChart controls after the bar, with one line break between them. Set the first one's properties as follows:
Historical Revenue
MonthSfc
MonthSfl
Add two series, in the first one set DataField to Revenue
, DataFieldLength to 5, and DataFieldDecimals to 0. Set the Color to Yellow, and the Legend to Revenue
. In the second one set DataField and Legend both to Expense
, Color to Red, and the same length and decimals as Revenue
(5, 0).
For the second DdsChart, here are the properties:
Top 10 States
StateSfc
StateSfl
Add just one series to this chart. Set its DataField to StateRev
, DataFieldLength to 6, and DataFieldDecimals to 0:
<mdf:DdsChart ID="DdsChart1" runat="server" Height="160px" Width="290px" SubfileControlName="MonthSfc" SubfileName="MonthSfl" BackColor="WhiteSmoke" ChartTitle="Historical Revenue" CategoryField="MonthName" CategoryFieldLength="3" EnableDataView="False">
<SeriesList>
<mdf:Series DataField="Revenue" Color="Yellow" DataFieldDecimals="0" DataFieldLength="5" Legend="Revenue" />
<mdf:Series DataField="Expense" Color="Red" DataFieldDecimals="0" DataFieldLength="5" Legend="Expense" />
</SeriesList>
</mdf:DdsChart>
<br />
<mdf:DdsChart ID="DdsChart2" runat="server" Height="160px" Width="290px" SubfileControlName="StateSfc" SubfileName="StateSfl" BackColor="WhiteSmoke" ChartTitle="Top 10 States" ChartType="Pie" CategoryField="StateAbbr" CategoryFieldLength="2" EnableDataView="False">
<SeriesList>
<mdf:Series DataField="StateRev" DataFieldDecimals="0" DataFieldLength="6" />
</SeriesList>
</mdf:DdsChart>
Build and Export the new Dashboard.aspx display file.
Because we don't have real data for these charts we will write the RPG program to load some mock data. We'll do three months of mock revenues and expenses for the bar chart, and 10 states with mock revenues for the pie chart. As we'll be creating a new source file called DASHBOARD, we need to add the CALL to it from CARORPG when the user selects the Management
button:
0294.00 C MGMNTDSH BegSr 0295.00 C Call 'DASHBOARD' 0296.00 C EndSr
And the DASHBOARD RPG mock code is:
0001.00 FDashBoard CF E WORKSTN Handler('MOBILERPG') 0002.00 F Sfile(MonthSfl:MonthRrn) 0003.00 F Sfile(StateSfl:StateRrn) 0004.00 0005.00 DMonthRrn S 4 0 0006.00 DStateRrn S 4 0 0007.00 0008.00 C********************************************************************* 0009.00 C* Use the RPG-Cycle to display the Dashboard, get out on IN12 0010.00 C********************************************************************* 0011.00 C ExSr LoadDash 0012.00 C ExFmt Charts 0013.00 C If *In12 0014.00 C Eval *InLR = *On 0015.00 C Return 0016.00 C EndIf 0017.00 0018.00 C****************************************** 0019.00 C* Load Dashboard 0020.00 C****************************************** 0021.00 C LoadDash BegSr 0022.00 C ExSr LoadMonth 0023.00 C ExSr LoadState 0024.00 C EndSr 0025.00 0026.00 C****************************************** 0027.00 C* Load Month Chart Subfile 0028.00 C****************************************** 0029.00 C LoadMonth BegSr 0030.00 C Eval *In99 = *On 0031.00 C Write MonthSfc 0032.00 C Eval MonthName = 'Jan' 0033.00 C Eval Revenue = 12345 0034.00 C Eval Expense = 10345 0035.00 C Eval MonthRrn = 1 0036.00 C Write MonthSfl 0037.00 C Eval MonthName = 'Feb' 0038.00 C Eval Revenue = 11345 0039.00 C Eval Expense = 09345 0040.00 C Eval MonthRrn = 2 0041.00 C Write MonthSfl 0042.00 C Eval MonthName = 'Mar' 0043.00 C Eval Revenue = 13345 0044.00 C Eval Expense = 10345 0045.00 C Eval MonthRrn = 3 0046.00 C Write MonthSfl 0047.00 C Eval *In99 = *Off 0048.00 C Write MonthSfc 0049.00 C EndSr 0050.00 0051.00 C****************************************** 0052.00 C* Load State Chart Subfile 0053.00 C****************************************** 0054.00 C LoadState BegSr 0055.00 C Eval *In99 = *On 0056.00 C Write StateSfc 0057.00 C Eval StateRrn = 0 0058.00 C Eval StateAbbr = 'CA' 0059.00 C Eval StateRev = 193640 0060.00 C Eval StateRrn += 1 0061.00 C Write StateSfl 0062.00 C Eval StateAbbr = 'TX' 0063.00 C Eval StateRev = 130743 0064.00 C Eval StateRrn += 1 0065.00 C Write StateSfl 0066.00 C Eval StateAbbr = 'NY' 0067.00 C Eval StateRev = 115650 0068.00 C Eval StateRrn += 1 0069.00 C Write StateSfl 0070.00 C Eval StateAbbr = 'FL' 0071.00 C Eval StateRev = 75400 0072.00 C Eval StateRrn += 1 0073.00 C Write StateSfl 0074.00 C Eval StateAbbr = 'IL' 0075.00 C Eval StateRev = 64420 0076.00 C Eval StateRrn += 1 0077.00 C Write StateSfl 0078.00 C Eval StateAbbr = 'PA' 0079.00 C Eval StateRev = 57560 0080.00 C Eval StateRrn += 1 0081.00 C Write StateSfl 0082.00 C Eval StateAbbr = 'NJ' 0083.00 C Eval StateRev = 49700 0084.00 C Eval StateRrn += 1 0085.00 C Write StateSfl 0086.00 C Eval StateAbbr = 'OH' 0087.00 C Eval StateRev = 48340 0088.00 C Eval StateRrn += 1 0089.00 C Write StateSfl 0090.00 C Eval StateAbbr = 'VA' 0091.00 C Eval StateRev = 42770 0092.00 C Eval StateRrn += 1 0093.00 C Write StateSfl 0094.00 C Eval StateAbbr = 'NC' 0095.00 C Eval StateRev = 40740 0096.00 C Eval StateRrn += 1 0097.00 C Write StateSfl 0098.00 C Eval *In99 = *Off 0099.00 C Write StateSfc 0100.00 C EndSr
Compile both RPG programs and run the app. Figure 46 shows what you should see when selecting Management Dashboard on the Home Menu screen:
Here ends this Tutorial. You made it this far, Congratulations! You have written a non-trivial Mobile RPG application. By completing this exercise you now have the basic tools and knowledge to design and create your own MR apps. From here, your journey should continue with a deeper exploration of the different controls and their options; they enrich the presentation of your apps. Also, you should read about designing mobile apps. There are many online references. One of the most important design ideas you should always keep in mind is Don't Make Me Think. A user who blankly stares at your screen while trying to figure out what he needs to do to get what he wants from the app is a frustrated user who will end up jumping ship at the first chance.
One subject this tutorial didn't touch is Cascading Style Sheets, or CSS. Try to learn some CSS; play by tweaking the style sheets in the Mobile RPG template, see what happens. Take for instance NavButton in Theme.css:
.NavButton, .NavButtonIcon, .NavButtonSymbol {
width: auto;
height: 2.8em;
margin-top: 0.2em;
padding: 0 5px;
overflow: hidden;
line-height: 28px;
font-family: inherit;
font-size: 0.8em;
font-weight: bold;
text-shadow: rgba(0, 0, 0, 0.6) 0px -1px 0;
text-overflow: ellipsis;
text-decoration: none;
text-align: center;
white-space: nowrap;
color: #fff;
background: rgb(37,141,200); /* Old browsers. VS Designer */
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #8ea4c1), color-stop(0.5, #5877a2), color-stop(0.5, #476999), color-stop(1, #4a6c9b));
background-image: -ms-linear-gradient(top, #B5C1C0 0%, #12A1F3 50%, #1230EF 100%);
-webkit-border-radius: 6px;
border-radius:6px;
border: 1px solid #45505d;
vertical-align:middle;
}
what does it all mean? can you change the blueish gradient color for a reddish one, so it matches the navigation bar?
We hope you put all these tools to good use. Happy coding!
Database Names are created using DataGate Explorer within VS. Open VS and go to the DATAGATE menu, select DataGate Explorer. A tool window will show up that looks something like Figure 47:
The thumbtack at the top bar helps in pinning the window so it doesn't autohide. Right-click the Database Names folder, and select "Add New Database Name". See Figure 48:
The dialog in Figure 49 will come up:
where you need to enter the following:
If the server components for Mobile RPG and DataGate 400 were installed to listen to port 5042, there is nothing more to do. Press "Test Connection" to verify the given parameters can connect successfully. If the ASNA server components are listening on a different port you need to set the correct port number in the Advanced properties dialog. Ensure that Test Connection returns successfully before pressing OK. Back in the DataGate Explorer Window you will see Figure 50:
Change "DB2" to a more appropriate name, like the name of the IBM i server on the network. Any name is fine, e.g. "My400_5042". This is the name to be used when using ASNA products and a Database Name is required.
This appendix assumes you have IIS installed on your developer machine, and that you have WiFi in your offices that allows your WiFi device into your local network.
This appendix assumes you have already installed the ASNAMRSAMP sample library in your IBM i. It's easier if you run two 5250 emulator sessions, one of them in qsh
.
qsh
, create the /AsnaMrSamp/MrCarolina directorymkdir /AsnaMrSamp/MrCarolina
RST DEV('/qsys.lib/asnamrE80.lib/carophotos.file') OBJ('/AsnaMrSamp/MrCarolina')
qsh
, verify that the restored files and file structure are correct.> pwd
/AsnaMrSamp/MRCarolina/VehiclePhotos
$
> ls
1001 1004 1007 1010 1013 1016 1019
1002 1005 1008 1011 1014 1017 1020
1003 1006 1009 1012 1015 1018
$
/AsnaMrSamp/MRCarolina/VehiclePhotos
each named using the vehicle ID in the Carolina VEHICLE file. Inside each of these folders, there are JPEG photos with the naming convention<prefix>-<VHID>-<unique-seq> .jpg