Welcome!

PowerBuilder Authors: Chris Pollach, Yeshim Deniz, Jayaram Krishnaswamy, Kevin Benedict, Avi Rosenthal

Related Topics: PowerBuilder

PowerBuilder: Article

XML-Based/PowerBuilder User Interface

XML-Based/PowerBuilder User Interface

While writing one of my PowerBuilder 9 book chapters, I suddenly had an idea. Wouldn't it be great to use PowerBuilder as a WAP front end? This would be a cool application for Pocket PowerBuilder too (most of the new devices have WLAN integrated).

My first thought was that this might not be so easy. WML is based on XML - PowerBuilder 9 offers good XML support - so not a problem here. No, the problem might be the creation of the controls. Hmm, I had to dig around a little bit and see if this could be a problem. Wait, I played around with the WinCE API with PocketBuilder a few weeks ago and remembered that there might be a chance to create objects on the fly. I tried further and found a solution that I'd like to present.

The Source: An XML File
Let's think about the following scenario. There is an existing shop system out there somewhere on the Web that's reachable using mobile phones and WAP. You can order products there and, of course, enter some information about yourself so the products will be shipped to you. So far, so good. The WAP page sent to the client would look like the one in Listing 1.

On this page we see that there's one card with the following objects on it:

  • Three input fields (that map to singlelineedits in PB) for the shoppers: "Name," the "Phone" number, and the "e-mail" address
  • A text area (that maps to a multiline edit in PB) so we know the "address" that the products go to
  • Two select fields (that map to listbox controls in PB) where we will choose our "Products" and a free supplement from the vendor
  • Three radio buttons so the shopper can choose a "Shipping" method

    To see the WML page in action, create a new Web application in EAServer (or Tomcat) and give it the name "wap", then create a file called "order.wml" with the code displayed in Listing 1.

    To see this page in action we need a WAP emulator. A lot of them are available on the Web; I've chosen a small one that you can find on www.pyweb.com/tools/#deckit.

    Install the emulator and enter the address http://localhost:8080/wml/order.wml; the output should be similar to Figure 1.

    PB as WAP Emulator
    We know how the page looks on a real WAP device. Now, we'd like to create a window on the fly in PowerBuilder with the necessary controls. Two problems need to be resolved:
    1.  Parse the XML/WAP file and figure out which controls are on it.
    2.  Create the controls on the fly.

    Parse the XML File
    Special thanks to Sybase for implementing XML (especially the PBDOM functions) into PowerBuilder 9. This makes our task very easy.

    The PBDOM PowerBuilder API implemented in PowerBuilder 9 can be used for reading, writing, and manipulating standard format XML from within PowerScript code, so this is exactly what we are looking for. With the provided PBDOM objects, we're able to parse existing XML documents and extract the information needed to build our user interface. By using these objects, existing XML documents can be read and modified by manipulating or transforming the PBDOM tree of objects, instead of parsing XML strings directly by using string functions or using an external component (for example, the MS DOM ActiveX control) as we had to do before.

    To figure out what should be displayed on our window, first get the XML document into a tree-view model consisting of parent and child nodes that we can walk through and create the necessary objects. That's pretty easy: we declare an instance variable of type PBDOM_BUILDER. By using the buildfromfile function, the PBDOM_BUILDER class serves as a DOM factory that creates a PBDOM_DOCUMENT from our XML file (don't forget to add the pbdom90.pbd to your library search path or the XML objects won't be available for use) (see Listing 2).

    To be sure we have a valid XML file, use the XMLParseFile function as it gives a return value and an error string if something is wrong (note that you will have to remove the !DOCTYPE tag from your XML file or the parser will hang).

    Now we have a document element that represents the top-level node of an XML document. Each child node of the document element has one or many child nodes that represent the branches of the tree (see Figure 2). Nodes in the tree are easily accessible through different PBDOM class methods, which we will use now to walk through the object tree and create our objects. This is done in a new window, w_xml, that gets the PBDOM_DOCUMENT as a parameter.

    We'll have to create a window function, wf_createview, that takes one parameter, a_obj_node of type PBDOM_OBJECT. This function will be used recursively to walk through the PBDOM object hierarchy, so in fact this is the place where the real parsing of the XML file happens. Before we code the function we have to get the parameter passed by the calling window. This is done by reading the PowerObjectParm in the open event:

    PBDOM_DOCUMENT lPBDOM_Doc
    lPBDOM_Doc = Message.PowerObjectParm
    in_obj = CREATE n_cst_objects
    wf_createView( lPBDOM_Doc)

    We'll also create a userobject that's responsible for creating the right control on the window, but be a little patient, we first have to find a control within our DOM tree. Again this is not too hard; we just have to figure out if we have a leaf, or if there are nodes (see Figure 3).

    // Get the childs of the node
    a_obj_node.GetContent(l_objA_node[])
    // If it is leaf label node then there will be no child
    If IsNull(l_objA_node) Then ll_count_node = 0

    When we have nodes, we call the function recursively to walk through the nodes. To get the nodes type, we use the function GetObjectClassString on the PBDOM_OBJECT passed into the function.

    l_s_original_objType = a_obj_node.GetObjectClassString()

    If the type is a PBDOM_ELEMENT, we might have to create a control, depending on the name of the object that we get with the GetName function. Now, depending on the name, we'll create different objects. Listing 3 shows you how to create a textarea object. This object has a parameter, called rows, that tells us the height of the control so the user is able to enter all the vital information (his or her address in our case).

    Create the Controls on the Fly
    We know how to figure out which objects should be created, but now comes another interesting part: How do we create the objects on the fly on our window?

    There are two solutions to this problem:
    1.  Extend a standard visual PowerBuilder control, or
    2.  Use the WinAPI to create the controls

    In this article I'll use the second approach since the first one is (technically) not as interesting. We'll use the CreateWindowEx API function, which creates an overlapped, pop-up, or child window with an extended style. The definition in PowerBuilder looks like this:

    Function Long CreateWindowEx (Long dwExStyle, String lpClassName ,
    String lpWindowName, Long dwStyle, Long xx, Long yy, Long nWidth,
    Long nHeight, Long hWndParent, Long hMenu, Long hInstance, ulong
    lpParam) Library "user32" Alias for "CreateWindowExA"

    This is the one and only function we need to use for creating any kind of windows control. Depending on the lpClassName parameter of this function, it creates a static, edit, button, and listbox, but how will we create a check box, radio button, and other objects? That's not a big problem. There's just another parameter to add to the game; we will change the property of the BUTTON and make it a radio button or check box. These properties are called control styles. I won't describe them here, since describing each one of them will take too much space. Just check out any Windows API book for more information. Let's look into the basic controls and the properties that we're going to use:

  • Static text: "STATIC" class with styles:

    WS_TABSTOP Or WS_CHILD Or WS_VISIBLE

  • Text box: "EDIT" class with styles

    WS_TABSTOP Or WS_CHILD Or WS_VISIBLE Or WS_BORDER Or ES_LEFT

  • Text area: "EDIT" class with styles

    WS_TABSTOP Or WS_CHILD Or WS_VISIBLE Or WS_BORDER Or ES_LEFT Or
    ES_AUTOHSCROLL Or ES_MULTILINE Or WS_VSCROLL Or WS_HSCROLL

  • Radio box: "BUTTON" class with styles

    WS_TABSTOP Or WS_CHILD Or WS_VISIBLE Or BS_AUTORADIOBUTTON

  • Check box: "BUTTON" class with styles

    WS_TABSTOP Or WS_CHILD Or WS_VISIBLE Or BS_AUTOCHECKBOX

  • List Box: "LISTBOX" class with styles

    WS_TABSTOP Or WS_CHILD Or WS_VISIBLE Or WS_BORDER Or LBS_MULTIPLESEL
    Or LBS_DISABLENOSCROLL Or WS_VSCROLL Or WS_HSCROLL

    To create a control, create a function, of_createcontrol (see Figure 4), in the custom class user object where we defined the external functions and the API constants you see in Tables 1 and 2. The function will take the following parameters: the handle of the current window where the control should be created, which object we would like to create, the object's text, the basic control properties, and the object's length and height. The x and y position should be calculated within the function to keep a good look and feel within the window so that the window will look like the one in Figure 5.

    The call to create our textarea would look like Listing 4.

    To send windows messages to the control, keep the handle created by the CreateWindowEx (this is done in the il_controlsHwnD array). This is useful if you'd like to retrieve the entered text. To retrieve the text of the first control, send a WM_GETTEXT message to the object (handle); for example, write the following code:

    CONSTANT ULONG WM_GETTEXT = 13
    String ls_text
    ls_text = SPACE(128)
    in_obj.SendMessageList( in_obj.il_controlsHwnD[1], &
    WM_GETTEXT, Len(ls_text),ls_text )

    The text is passed back by windows into ls_text. This is what happens when you write a sle_1.text within your own applications. Remember that nearly everything in windows works with handles and messages. Don't forget to destroy the created objects when you close the window (walk through the controls array and use the DestroyWindow API call) (see Listing 5).

    References
    Check out the XML information provided in the PowerBuilder documentation - "Application Techniques," Chapter 13. If you're not familiar with the newest version of PowerBuilder and its features, you might consider the "Moving to PowerBuilder 9" class provided by Sybase as well as the two new PowerBuilder 9 books:

  • Armstrong, B., et al. (2003). PowerBuilder 9: Advanced Client/Server Development. SAMS.
  • Green, W., et al. (2003). PowerBuilder 9: Internet and Distributed Application Development. SAMS.

    Conclusion
    PowerBuilder 9 makes it easy to use XML within your applications. We've seen that we can load an XML-based WML file into our application, walk through the structure with some simple-to-use functions, and create a user interface on the fly.

    You'll find the full source code on http://codexchange.sybase.com in the PowerBuilder/XML section. Feel free to download it and maybe enhance it. I wish you a lot of fun with XML and PowerBuilder!

  • More Stories By Berndt Hamboeck

    Berndt Hamboeck is a senior consultant for BHITCON (www.bhitcon.net). He's a CSI, SCAPC8, EASAC, SCJP2, and started his Sybase development using PB5. You can reach him under [email protected]

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.