Utilizing the Power of the Datawindow Object

advertisement
Utilizing the Power of
the Datawindow Object
By Buck Woolley
Making use of advanced
graphical datawindows
he PowerBuilder datawindow
object is one of the main reasons
for the success that PowerBuilder
has achieved. However, since the beginning, the datawindow has been used primarily to display and manipulate data as
a variety of input and maintenance forms.
Today, users are demanding a more
dynamic user interface, in which intuitive
display and manipulation of graphical
representation of data replaces the simple
method of displaying and editing text
data. These interfaces present data in a
graphical format and allow the user to
modify that data with the mouse or other
pointing device. The PowerBuilder
datawindow object is ideal for developing
a wide variety of advanced graphically
rich dynamic user interfaces, displayed
on a variety of platforms such as .net,
PDA devices, and web browsers.
T
Characteristics of Advanced
Graphical Datawindows
There are several characteristics of the
datawindow that make it an ideal tool
for developing graphical-based interfaces.
First, the datawindow, of course, is a
presentation object. Although primarily
used to present data in a text format,
many of the capabilities can be used to
present graphical interfaces as well.
Buck Woolley is the owner of dwGraphical datawindows are external and
extreme.com and is a PowerBuilder
dynamic, and their complexity requires
consultant at Patrix AB. He can be
reached at bwoolley@dw-extreme.com. that they be developed dynamically at
2
ISUG TECHNICAL JOURNAL
runtime. These datawindows are only used
for presentation purposes and are normally
sourced from other datastores that retrieve
the various visual properties from tables.
During development, they normally exist
as external datawindows, only containing
the columns to hold visual properties and
without any visual objects. The visual
objects are generated at runtime to meet
the needs of the user request.
Graphical datawindows also take
advantage of several powerful native
functions, specifically, those that appear in
expressions such as string and group functions. This is due to the tight coupling of
data to the visual properties of objects on
a datawindow. Through the use of the
abundant available functions to modify
user input, users can manipulate graphical
properties simply by changing a column
value. These include mouse events such as
pbm_lbuttonup, pbm_lbuttondown and
pbm_mousemove in PowerBuilder.
Datawindows of all types generally
use the same methods and techniques in
PowerBuilder, PocketBuilder, or Visual
Basic through the use of dw.net. The functionalities are the same across platforms.
Also, with very few exceptions, these
methods and techniques can be deployed
to the web using Appeon for PowerBuilder.
Secondly, the datawindow is inherently
row-based. For certain graphical situations,
this is quite beneficial. For example, you
can create an object on a datawindow and
P O W E R
make it appear multiple times, simply by switching its visibility
property for each row. This makes rendering and manipulating
graphical objects quite efficient in most situations.
You can also create and mix graphical objects such as rectangles, ellipses, lines, text, and images with data objects such
as columns, expressions, and drop-down datawindows to create components that are not only strictly graphical but have a
data component as well.
Advanced datawindows are ideal for a variety of purposes.
They are especially useful in situations where there is a need
to visualize data with a start and end date/time component,
or to visualize physical configurations. This could apply to
any solution that involves scheduling or recording an asset to
a period of time or to a location. Examples include physical
items such as people, rooms, autos, equipment, or virtual
items such as programs, documents, project requirements, and
work events. Other applicable solutions include visualizing
parent-child relationships, such as in an organizational chart,
or representing physical models such as a warehouse or store
shelves.
The Metadata Datawindow
Each of the solutions we just described uses a method of
presentation called the metadata datawindow. The metadata
datawindow relies on string columns to store visual properties
of datawindow objects. This technique combines the rowbased architecture of the datawindow with the access to
column data from visual properties, via property expressions
within datawindow objects. For example, visual properties
such as x, y, width, height, color, visibility can all be stored
in datawindow columns.
The metadata aspect comes from using STRING columns
to store the visual properties of many objects, rather than one,
and using STRING functions in the expressions of the object
visual properties to access the values for each object. An
example of this technique is the planner datawindow component, which is used to display and manipulate entities represented as rectangles on a time-based grid (see Figure 1).
Each row on the grid is a row in the datawindow, allowing
the planner to make use of the efficiencies of the row-based
nature of the datawindow. In Figure 1, we see seven task
rectangles. However, actually only two rectangles have been
created. These exist on every row, but are made invisible by
setting the visible property of each rectangle on each row.
The visible property exists as a metadata column within the
datawindow. The rectangles have a variety of other display
properties such as colors, patterns, length, and width, stored
in datawindow columns as metadata.
O F
T H E
D A T A W I N D O W
O B J E C T
Figure 1
For a simple illustration of why metadata is the best method
to control the various display properties and how it
is used, let’s consider the following example. The visibility
of the rectangle (r_1) can be set by entering a value in the
visible property expression of the object (see Figure 2).
Figure 2
This method is limiting, however, as it only allows control
of the visibility on one instance of r_1 on one row. A better
method is to set the visible expression to the value in the visible column and setting the value in the visible column to 1:
Figure 3
This lets the user control the visibility of r_1 on multiple
rows within the datawindow. However, it limits us to r_1, and
should you create a rectangle (r_2), you’d need to create a new
column (visible_2) in the datawindow to control its visibility:
FIRST QUARTER 2006
3
P O W E R
O F
T H E
D A T A W I N D O W
O B J E C T
Figure 4
Using these approaches to control the properties of graphic
objects are not sufficient, as in most situations you will not
know the number of rectangles required until runtime. The
best solution is to be able to store all the visible properties
of all possible rectangles, on all possible rows, in a single
column. This is done by using a string of sufficient length
to store the property of all rectangles and to use the MID()
string function within the visible expression to access the
portion of the string that applies to it.
The visibility properties for rectangles r_1 and r_2 are
stored in a single column visible_string. In this example, the
column has a value of “10.” Rectangle r_1 takes the value
of the first character in visible_string by using a couple of
built-in functions with its visible expression.
layers of objects have their visibility controlled by metadata.
The top object is the marker, which is seen as an inverted
“v.” Beneath this are the blue tiles that hides the mines.
The numbers indicate the number of mines adjacent to it,
then the mines themselves are visible as red-filled circles.
Each row of objects is a row in the datawindow. Each time
a tile is clicked that has no mine or number beneath it, an
algorithm searches for the boundary of contiguous mineless
space. During this process, the visible property embedded in the
metadata of all the rectangles selected in this algorithm is set
to 0, which reveals the blank space and numbers underneath.
Figure 6
The source code for the minesweeper game is available from
the author.
The number of objects that can be switched on and
off is only limited to the defined length of the string column
containing the data that controls the visible property. Other
display properties, such as x, y, width, height, colors, patterns,
line types and widths, bitmap filenames, and even text of
objects within datawindows can be controlled the same way.
Figure 7
Figure 5
The expression contains LONG(MID(visible_string,1,1)), which takes
the character in position 1 for length 1 in the column visible_
string and converts it into a long value, which is then applied
to the property. Of course, rectangle r_2 would have a value of
LONG(MID(visible_string,2,1)) to resolve the value of the second
character in the visible_string column.
This technique of visual property control by metadata is
most easily seen in the datawindow-based version of a minesweeper game that uses metadata to control the visibility of
various row-based objects on a datawindow (Figure 6). Four
4
ISUG TECHNICAL JOURNAL
This example, from the planner component, shows the
columns used to contain the data for three display properties
for the three rectangles: pattern, color, and background color.
The color and background color are stored in string segments
that have a length of 8. In this case, the values are stored
in fixed length segments within the string. Each column is
defined as a string of sufficient length to contain all the data
for the maximum number of rectangles that could exist on
one row. This usually isn’t a problem, as a string column
capable of holding a string of 8,000 can contain the colors
for 1,000 rectangles.
P O W E R
If you were to create these rectangles at runtime, you
would use the create statement and define the values of these
three display properties as follows:
ls_syntax = ‘create rectangle(...
brush.hatch="0~tlong(mid(patternbtype,'+key1+',1))“
brush.color="0~tlong(mid(color,'+key8+',8))“
background.color="0~tlong(mid(background_bcolor,'+key8+',8))"...)’
dw_1.modify(ls_syntax)
O F
T H E
D A T A W I N D O W
O B J E C T
value in key20 defines the starting position of each of the
segments that contain a file name. Other properties required
to display the images include visible, x, y, height, and width.
Each of these properties is also stored as a metadata column.
Other Metadata Datawindows Examples
Another example of the metadata datawindow includes the
stock chart that displays stock price data in various charting
styles. Each chart, such as the one in Figure 10, is contained
in a single row within a datawindow.
Where key1 is the starting location of values that have a
length of 1, such as brush.hatch, key8 is the starting location
of values that have a length of 8, such as colors. This
technique can also be used to display images:
Figure 10
Figure 8
In this example, there are three datawindow rows displayed.
Each row contains a number of images of different items.
Again, the advantage of the row-based nature of the datawindow is evident. As you can see, there are 32 images displayed.
However, only eleven bitmap objects were actually created
and are repeated on every row. The file names of the images
are stored in a metadata column that is parsed out in similar
fashion, as in the previous example.
Figure 11 displays three rows from the stock chart datawindow. Once you have generated objects to display the
chart for one chart, you may display an infinite number of
charts simply by adding additional rows of price data. This
technique certainly isn’t limited to stock data and can be
applied to a number of situations.
Figure 9
The image file names are stored in a string metadata column
named file and are accessed using built-in functions within
the create statement of the bitmap:
Figure 11
'create bitmap(...filename="A~tTRIM(trim(mid(file,'+key20+',20)))"...)’
Filenames are stored in string segments of 20 characters. The
Figure 12 demonstrates a railyard datawindow, showing
the location of railcars. It allows the user to select a view of
FIRST QUARTER 2006
5
P O W E R
O F
T H E
D A T A W I N D O W
O B J E C T
information about a railcar as well as to reorganize the railcars
within the railyard simply by selecting and moving them with
the mouse.
Figure 12
The timeline datawindow (Figure 13) is similar to the
planner; however, it is configured to display all the data
in a footprint that doesn’t require horizontal scrolling.
This is used in situations where printing is a primary focus.
Figure 13
The appointment datawindow (Figure 14) displays appointments with their times on the vertical axis rather than on the
horizontal. This datawindow uses the metadata technique to
render activities on the appointment page.
Figure 14
6
ISUG TECHNICAL JOURNAL
Using DragDrop to Move Objects
In order to make your datawindow truly interactive, we need
to be able to manipulate objects on a datawindow, between
multiple datawindows, or between other PowerBuilder objects
using the mouse. For instance, in many applications you may
need to drag an image from a palette of images to a canvas.
In this case, you will copy from one datawindow, containing
a set of standard images, to another datawindow in which
you are building a network, shelf, chart or workflow diagram.
In order to create a seamless movement between the two
datawindows, you must use a third independent object that
performs the actual move between the objects. In this case, a
descendant of the PowerBuilder statictext object will be used.
You can create instance variables within the statictext object
to store offset values determined by the location of the pointer when you selected the object to move. You also must set
the statictext object to “not visible” because you are using it
as a drag object only.
The action is initiated by the oe_lbd event on an object
in the palette datawindow. In this event, you will extract the
row of the selected image and the file name of the image.
You also want to position the picture object precisely over
the datawindow image object, and have it assume its width
and height so that it appears as if you are dragging the actual
image object. This is made more difficult if you have “autosize” set on your detail band. In the palette datawindow, you
must create a computed field that contains the cummulative
“y” value of every row. This is done with the following
computed expression called row_height:
P O W E R
cumulativeSum( rowheight() for all)
You must also take into account that the user may have
scrolled down and the first row on the page is not Row 1.
This is resolved by using FirstRowOnPage to determine
which row is the first visible row:
string ls_object
long ll_row, ll_page_row, ll_first_row
ls_object = this.getobjectatpointer()
IF LEFT(ls_object,4) = 'file' THEN
ll_row = LONG(MID(ls_object,POS(ls_object,'~t') + 1,
LEN(ls_object) - POS(ls_object,'~t')))
IF ll_row > 0 THEN
ll_page_row = LONG(this.Object.DataWindow.FirstRowOnPage)
IF ll_row > 1 THEN
//This area places the drag object over the selected
//image and sets its width and height
ll_first_row = LONG(this.object.datawindow.firstrowonpage)
IF ll_page_row = 1 THEN
iuo_drag_object.y = this.object.cumheight[ll_row - 1] + 28
ELSE
iuo_drag_object.y = (this.object.cumheight[ll_row - 1] this.object.cumheight[ll_first_row - 1]) + 28
END IF
iuo_drag_object.x = this.object.c_x[ll_row] + 16
iuo_drag_object.height = this.object.height[ll_row]
iuo_drag_object.width = this.object.width[ll_row]
//Set some instance variables that will be used later
iuo_drag_object.il_key = this.object.key[ll_row]
iuo_drag_object.is_desc = this.object.desc[ll_row]
iuo_drag_object.il_x = iuo_drag_object.pointerx()
iuo_drag_object.il_y = iuo_drag_object.pointery()
iuo_drag_object.is_file = this.object.file[ll_row]
iuo_drag_object.drag(begin!)
return -1
END IF
END IF
END IF
At this point, you should see a box perfectly outlining the
boundary of the datawindow image object. You should also
be able to move the box around the window. If you drop
the object on the canvas datawindow, you will process the
dragged object in the dragdrop event of the canvas datawindow. The il_x and il_y hold offset values so that the object
will be created with the same reference to the mouse pointer
as it had when it was selected. Other properties in the dragged
object are used to set the size and filename of the new image.
O F
T H E
D A T A W I N D O W
O B J E C T
iuo_dw_planner.setredraw(FALSE)
do_x = iuo_dw_planner.pointerx()
luo_drag_object = source
IF source.classname() = 'drag_object' THEN
i_task = luo_drag_object.il_key
i_task_desc = luo_drag_object.is_desc
is_file = luo_drag_object.is_file
il_width = luo_drag_object.width
il_height = luo_drag_object.height
ll_y = 324 - il_height
ll_x = do_x - luo_drag_object.il_x
this.event oe_make_bar(TRUE,ll_y,ll_x,row)uo_pic lp
long ll_x, ll_y
string ls_id
lp = source
ll_x = idw.pointerx() + lp.il_x
ll_y = idw.pointery() + lp.il_y
dw_canvas.modify('create bitmap(band=foreground filename=
"'+ lp.picturename +'" border="0" x="' +STRING(ll_x)+'"
y="'+STRING(ll_y)+'" height="'+STRING(lp.height)+'"
width="'+STRING(lp.width)+'" name='+ls_id+' )')
This will create an image on the canvas datawindow at the
location of the mouse cursor with the same bitmap and
properties as the selected image on the palatte datawindow.
To illustrate this, select an image from a palette datawindow
of the shelf demo, in this case, a fine bottle of scotch. You
notice the rectangle around the selected object which is the
picture object with its visibility set to 0. Since the drag
(begin!) function is invoked, the outline of the picture object
does appear around the bottle.
Figure 15
Drag the object from the palette datawindow to the target
datawindow, in this case, the shelf.
Figure 16
FIRST QUARTER 2006
7
P O W E R
O F
T H E
D A T A W I N D O W
O B J E C T
Drop the object onto the target datawindow and build the
datawindow bitmap object, based on the properties stored
in the dragged object and the results of the pointerX() and
pointerY() functions.
is_function = 'newnode'
//Create new node
END IF
This sequence can be seen in the following figures.
Figure 17
Object Manipulation From a Treeview to a
Datawindow
Many times the source or palette may be a treeview rather
than a datawindow. This makes things a bit easier, since it
does not require a third object to perform the dragdrop, as the
treeview itself acts as the drag object. To initiate the process,
place the following code in the Selectionchanged event of the
treeview object. Check to see if the treeviewitem selected is
one that can be dragged. This can either be a value stored in
the treeviewitem data property or the picturename of the
treeviewitem, as in this example.
IF string(this.picturename[ltvi_item.pictureindex]) = 'arrow2.bmp' THEN
this.dragauto = TRUE
ELSE
this.dragauto = FALSE
END IF
Figure 18
The desired treeviewitem is selected and drag has to be set to
begin!. The treeview item can be dragged since it has the picture indicated, in this case, arrow2.bmp.
Figure 19
The item is dragged over the canvas:
This automatically enables dragging of the treeview. Drag
the treeview icon onto the datawindow and release it at the
desired location. The process is similar to the datawindow to
datawindow processing. In the dragdrop event of the target
datawindow, check for the source of the dragdrop, and if it is
a treeview:
Dragdrop event
IF source.typeof() = treeview! THEN
lt_tree = source
ll_handle = lt_tree.finditem(currenttreeitem!,0)
lt_tree.getitem(ll_handle,lt_item)
ll_parent = lt_tree.FindItem(ParentTreeItem! , ll_handle)
lt_tree.getitem(ll_parent,lt_parent) //get the bitmap for the node
ls_bitmap = string(lt_tree.picturename[lt_parent.pictureindex])
ls_item = lt_item.label
is_object_type = 'node'
8
ISUG TECHNICAL JOURNAL
Figure 20
The item is dropped and the image is rendered on the canvas,
at the location of the pointer, in the dragdrop event of the
canvas datawindow.
Multiple Platforms
One of the great advantages of the datawindow object is that
it can be migrated with minimal changes to a variety of platforms. With the development of PocketBuilder, you can take
P O W E R
the functionality of the datawindow directly to your Windowbased PDA device. This is especially useful for advanced
graphical datawindows, since PDA applications tend to be
primarily or exclusively pointer-based. Using techniques that
allow users to enter data by “drawing” can result in more
sophisticated applications. Also, since data is displayed
graphically, you can use position, color, sizes, and patterns
to make data more meaningful to the user.
Figure 21 shows two PocketBuilder views of advanced
datawindows used for data input using the PDA pointer. The
calendar datawindow allows users to select the current date,
a treeview datawindow displaying a list of tasks, and the planner data window in which the task is rendered. The pointer is
also used to move, change, and delete tasks from the planner.
Datawindow manipulation in PocketBuilder uses the same
events and techniques as PowerBuilder, such as metadata
and object manipulation, to achieve this functionality.
O F
T H E
D A T A W I N D O W
O B J E C T
Be aware that there is a learning curve associated with using
vb.net due to differences in coding methods between Visual
Basic and Powerscript. One major difference is that you must
code the event handlers. Let’s consider the commonly used
clicked() event in Powerscript. To do the same in Visual
Basic, you must add the event handler for the click event, as
well as other definitions. There are resources that go in-depth
on this topic, but as just one example, consider the clicked()
event in Powerscipt where the row number and dwobject are
passed automatically as parameters to the clicked event. The
following code accomplishes the same in Visual Basic:
‘Define the datawindow variable and the object at pointer variable
Dim oDW As Sybase.DataWindow.DataWindowControl
Dim oClicked As Sybase.DataWindow.ObjectAtPointer
‘Define the click event handler for datawindow dw_1
Private Sub dw_1_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs)
Handles dw_1.Click
‘Cast the sending object into the datawindow variable
oDW = CType(sender, Sybase.DataWindow.DataWindowControl)
Figure 21
Visual Studio
With the release of datawindow.net for the Microsoft Visual
Studio platform, the same advanced datawindows can be
created using vb.net or C#. At this time, they have only
been created for winforms. Again, the methods used are the
same as for the PowerBuilder datawindows. The meta data is
built into large strings, then stored in datawindow columns.
Objects are built dynamically in the datawindow, which
access values within the datawindow columns.
‘Get the object at pointer, its name and the row number that was clicked
oClicked = oDW.ObjectUnderMouse
oClicked.Gob.Name
oClicked.RowNumber
There are also differences between the two platforms in the
syntax of built-in functions. Some of the more common
functions are summarized in the following table.
PowerBuilder
Visual Basic.net
Use (‘) or (“) to enclose string variables Always use (“)
Time("6:00:00")
TimeValue("6:00:00")
Date("01/01/1900")
DateValue("01/01/1900")
Round(9.334,0)
Math.Round(9.334,0)
String(‘123’)
Str(“123”)
RelativeDate(start_dt, 5)
DateAdd(DateInterval.Day, 5,
start_dt)
DayNumber(startdate)
Weekday(startdate)
RegistryGet("HKEY_CURRENT_USER\
Control Panel\International",
"sShortDate",RegString!, local_setting )
Figure 22
Microsoft.Win32.Registry.Current
User.OpenSubKey("control
panel\\international", False)
local_setting =
regKey.GetValue("sShortDate")
There are resources that go in depth into datawindow.net
coding techniques in Visual Studio.
FIRST QUARTER 2006
9
P O W E R
O F
T H E
D A T A W I N D O W
O B J E C T
Browser
Advanced datawindows have been migrated to the browser
using Appeon for PowerBuilder version 3.0. This integration
into the Powerbuilder IDE makes deploying to the web an
easy process. Appeon supports much of the dynamic datawindow creation required to display advanced datawindows,
as well as dragdrop and other mouse-based functionality.
Although Appeon for PowerBuilder does not support the
setdetailheight() datawindow function, this can be overcome
by using the autosizeheight property on the detail band,
which allows the continued use of the detail area for each
row. Even the use of dynamically created datawindow
columns is supported for storing details regarding each task.
As you can see, a very complete and easy deployment of
advanced datawindow technology is achieved using Appeon
3.0 for PowerBuilder and PowerBuilder 9.
Summary
As the datawindow has matured and spread into new technologies and markets, the full capability of this outstanding
tool must be harnessed to create applications that are robust
and visually appealing to the user. Although it is the premier
product for the functionality it has traditionally been used for,
the capabilities of the datawindow can be extended to create
rich, interactive user experiences in a variety of platforms.
My goal is for developers to think outside the box when
using the datawindow control in their applications. By using
innovative techniques such as metadata to control visual
properties and by implementing mouse based object manipulation, you can generate a graphical user interface that creates
a more dynamic and interesting user experience.
For a more complete explanation of advanced graphical
datawindows please refer to Powerbuilder 9 Advanced Client/
Server Development published by Sams (2003). Chapter 7
covers in detail the methods and techniques used to develop
the datawindow examples shown in this article. ■
Figure 23
Got a great tip for DBAs or
developers? Some great
advice, a new method, or
excellent syntax?
Consider writing an article for
the ISUG Technical Journal!
Becoming an author for the Journal helps build
your resume, establish your reputation, and share
your great concepts with fellow ISUG members.
For more information, check out the ISUG
Technical Journal Online to see sample articles
and guidelines for authors. Then contact
Managing Editor Mary Freeman at freemancomm@
yahoo.com to discuss your article concept.
www.isug.com
10
ISUG TECHNICAL JOURNAL
Download