Displaying an Image on a Form
You can display the image directly by placing it on a separate Image tab on the sales order form. Figure 5
shows an order for a bike with a frame size of 21 inches and a wheel
size of 28 inches. The user can click the Image tab to view the uploaded
bike image and confirm that it matches the ordered item before
confirming the sales order. The Sales Order form (AOT\Forms\SalesTable)
is located in the navigation pane, Accounts Receivable\Sales Order.
The
following two example implementations describe how to use the document
management tables as data sources in the form and how to create a
separate method on the SalesLine table. These examples demonstrate customization of the SalesTable sales order form and the SalesLine table.
Displaying an Image by Using Joined Data Sources
One way to display the image is to apply the DocuRef and DocuValue tables as data sources for the SalesTable form. The following example creates a DocuRef data source based on the relationship among the SalesLine, DocuRef, and DocuValue tables shown in Figure 5-9. The DocuRef data source relates to the DocuRef table and is joined to the SalesLine data source. Additionally, a DocuValue data source is created to connect to the DocuRef data source. Table 8 shows additional properties of the data sources.
Table 8. DocuRef and DocuValue Property Settings
Property | DocuRef | DocuValue |
---|
Table | DocuRef | DocuValue |
AllowEdit | No | No |
AllowCreate | No | No |
AllowDelete | No | No |
JoinSource | SalesLine | DocuRef |
LinkType | Active | Active |
The properties JoinSource and LinkType allow the DocuRef and DocuValue records to be fetched when the user moves from one line to another. The remaining properties disable editing of the records.
You can attach multiple files, documents, and notes to a SalesLine
record by using the document management feature, but the goal of this
example is to display an image from a linked document named Image. You
can limit the retrieved records from the DocuRef table by adding a range to the query used by the DocuRef data source. You do this by customizing the Init method on the DocuRef data source, as shown here.
public void init()
{
super();
docuRef_ds.query().dataSourceTable(
tableNum(DocuRef)).addRange(
fieldNum(DocuRef,TypeId)).value(queryValue('Image'));
}
|
This X++ code limits the query so that it retrieves only records from the DocuRef table in which the TypeId field is equal to the value ‘Image’.
Note
The use of a constant such as the word Image is not a best practice. The value must be retrieved from a configuration table so that the user can decide the naming. ‘Image’ is hard coded in the preceding example only to improve the readability and limit the scope of the example. |
The image is displayed by using a window control, which is placed in a tab control, as shown in Figure 6.
Although the image is stored in the File field on the DocuValue table, to display the image you can’t simply link the field as a DataField
value on the window control property sheet. The image must be parsed to
the control by using a method on the control in X++ that uses the FormWindowControl object. The AutoDeclaration property on the FormWindowControl
object is therefore set to Yes so that the forms designer automatically
declares an object handle with the same name. This handle can be used
in X++ and manipulated at run time because the form application runtime
automatically ensures that it is a handle to the FormWindowControl object. The Width and Height properties are set to Column width and Column height so that the image takes up all the space on the tab.
The last step is to parse the retrieved image from the DocuValue table to the BikeImage FormWindowControl object. You can do this when a DocuValue
record buffer is present. This record must contain an image that is
stored in the database, and the X++ code should be placed in the active
method on the DocuValue data source and look like the following.
public int active()
{
Image image;
int ret;
ret = super();
if (docuValue.File)
{
image = new Image();
image.setData(docuValue.File);
bikeImage.image(image);
}
else
{
bikeImage.imageResource(0);
}
return ret;
}
|
This code determines whether a value exists in the File field and, if so, instantiates an image object and parses the File field value to the image object. This object is then parsed by using the Image method to the FormWindowControl object that displays the image. If the File field doesn’t contain a value, the imageResource method on the FormWindowControl object is called with a value of 0 to clear the control of any previous content. The active method is executed only if a DocuValue
record has been retrieved. However, if a user moves from an order line
with an image to an order line without an image, the image isn’t cleared
because the active method isn’t executed. If you add the following line
to the active method on the SalesLine data source, the image is cleared when a new order line becomes active and before the DocuRef and DocuValue records are retrieved.
docuBikeImage.imageResource(0);
|
The customizations
described in this section make it possible to display the image on the
Image tab. This solution has one downside, however. Whenever a user
moves from one order line to another or a line is created or saved,
calls are made from the client to the server, and lookups are made in
the database for the DocuRef and DocuValue
data sources. You can see this by turning on the client/server or SQL
trace option in the Options dialog box, which you access from the Tools
menu. The next section addresses this issue and offers a
solution—decreasing the number of client/server calls and lookups in the
database.
Displaying an Image When Activating the Image Tab
The following example
implements a solution similar to the previous example, but it results in
calls to the server and the database only when the image is actually
displayed.
The TabPage control must be added to the SalesTable form and contain a FormWindowControl with property settings similar to those in the preceding example. The DocuRef and DocuValue
tables are not, however, added as data sources for the form. Instead,
this example retrieves the image—the only element shown on the Image
tab—from the database only when the user chooses to display the content
of the Image tab. You configure this by adding the following X++ code to
the pageActivated method on the TabPage control.
public void pageActivated()
{
Image image;
DocuValueFile docuValueFile;
;
docuValueFile = salesLine.bikeImage();
if (docuValueFile)
{
image = new Image();
image.setData(docuValueFile);
bikeImage.image(image);
}
else
{
bikeImage.imageResource(0);
}
super();
}
|
This code is very similar to the code added to the DocuValue active method, but in this case the value is retrieved from a bikeImage method on the SalesLine table. The bikeImage method is a new method created on the SalesLine table with the following content.
server public DocuValueFile bikeImage()
{
DocuRef docuref;
DocuValue docuValue;
;
select firstonly tableid from docuRef
where docuRef.RefCompanyId == this.DataAreaId &&
docuRef.RefTableId == this.TableId &&
docuRef.RefRecId == this.RecId &&
docuRef.TypeId == 'Image'
join file from docuValue
where docuValue.RecId == docuRef.ValueRecId;
return docuValue.File;
}
|
The select statement in the bikeImage
method is a combination of the two lookups in the database produced by
the runtime shown in the first sample implementation, which used data
sources. However, the statements in this method are joined. The bikeImage method could simply be implemented in the SalesTable form, but implementing it on the SalesLine table allows it to be reused in other forms or reports and executed on the server tier, if required.
The
advantage of this implementation method is that both database lookups
and calls from the client to the server are reduced by half. And because
calls are made only when the Image tab is activated, they aren’t made
when a user simply moves through the order lines without viewing the
content of the Image tab. The disadvantage, however, is that the user
can’t personalize the form or move the display of the image to another
tab because retrieval of the image is dependent on activation of the
Image tab.