Contact Information

Chris Toohey

    Site Resources

    RSS Site Content
    Syndicate This Site!
    DG Banner Logo
    BleedYellow.com Dogears
    Advertisements

    IBM Lotus Notes and Domino 7 now available

    Context-Sensitive Tabbed Navigation in the Lotus Notes Client

    02/16/2006 09:21:16 AM | Chris Toohey | Saylorsburg, PA

    Revolution CRM - Context Sensitive Tab Navigation ExampleIt's something that gave our CRM product (Revolution CRM) an amazingly fresh and simple-to-navigate Notes-client user interface, and despite it's seemingly simple function... implementing a context-sensitive navigation system for a table-based form layout in the Lotus Notes client required quite a bit of hackery. In this article, I'll showcase exactly what we did to facilitate such functionality and ultimately provide our users with a simple, intuitive interface (at least in my and several of our users' opinions)

    We start off by creating a Programmed Table, turning off the table tabs to the UI (fig. 1), then naming the table row and assigning each row tag a unique name (fig. 2).

    (fig. 1) - Table Properties

    (fig. 2) - Disabling Tabs Table Properties

    Now that we have our Programmed Table, we need to create the dynamic display functionality. To do this, we simply create a field called $row:

    Field Name: $row
    Field Type: Text - Computed for Display (CFD)
    Field Formula:@If($row="" ; "1"; $row)

    And now that we have our Programmed Table and programatic control field, we'll work on the tabbed interface.

    You can make this navigation system look however you would like, but the ultimate goal is to have an active and inactive state for each of your navigation tabs, and set the active table row's correlating navigation tab to it's "active" state. To do this, we'll first build out the navigator. And for this navigator, we'll use a table, and more specifically, a table cell.

    We are going to put a hotspot in a table cell, and when said hotspot is clicked, I want to 1) set the current navigation tab to it's "active" state and 2) activate the correlating Programatic Table row. For 1, we'll need to place the following formula in the cell properties of the navigation tab (fig. 3):

    (fig. 3) - Computed Cell Properties

    @If($row="1";"activetab.gif";"tab.gif")

    The above code, checks to see if the first tab is active, and if so, sets the cell background-image to "activetab.gif". Otherwise, it remains "tab.gif", which is the application's inactive tab background-image. Pretty simple approach, so far. The problem that we ran into is that we could not successfully "reload" the page to display the now-active navigation tab nor the now-active Programmed Table row.

    Bring on the hackery!

    We start by using Lotuscript on the form at various form events, and then an on-click function loaded from a script libary. We'll start by displaying a list of form events and their script counterparts:

    QueryOpen

    Sub Queryopen(Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant)

        Set w = New NotesUIWorkspace
        Set s = New NotesSession
        Set db = s.CurrentDatabase
        Set uidoc = source

        Call InitializeRow ( s, uidoc )

    End Sub

    QueryClose

    Sub Queryclose(Source As Notesuidocument, Continue As Variant)

        Call FinalizeRow ( s, source )

    End Sub

    PostOpen

    Sub Postopen(Source As Notesuidocument)

        Set doc = source.Document

    End Sub

    Declarations

    Dim s As NotesSession
    Dim w As NotesUIWorkspace
    Dim db As NotesDatabase
    Dim doc As NotesDocument
    Dim uidoc As notesuidocument

    And now, for our on-click function. On each "tab", you'll want to create a hotspot with the following Lotuscript (replacing the "1" with the intended Programmed Table's Row name:

    Sub Click(Source As Button)

        Call OnClickTab ( w, s, uidoc, doc, "1" )

    End Sub

    And now, for our OnClickTab, InitializeRow, and FinalizeRow functions:

    Sub OnClickTab ( w As NotesUIWorkspace, s As NotesSession, uidoc As NotesUIDocument, doc As NotesDocument, row As String )
    ' FUNCTION: OnClickTab ( w, s, uidoc, doc, row )
    ' PURPOSE: Change the row the the programmable table
    ' ARGUMENTS:
    '      w ( NotesUIWorkspace ) - global ui workspace
    '      s ( NotesSession ) - global session
    '      uidoc ( NotesUIDocument ) - notes ui document
    '      doc ( NotesDocument ) - notes backend document
    '      row ( row ) - row to set

      If uidoc.editmode Then
        Call doc.ReplaceItemValue("$row", row)
        Call uidoc.refresh
      Else
        Call s.setEnvironmentVar("RevolutionRow", row)
        Call uidoc.close
        Call w.EditDocument(False , uidoc.Document)
      End If

    End Sub

    Sub InitializeRow ( s As Notessession, uidoc As notesuidocument )
    ' FUNCTION: InitializeRow ( uidoc )
    ' PURPOSE: Initialize the row to one pressed by user
    ' ARGUMENTS:
    '      s ( NotesSession ) - global session
    '      uidoc ( NotesUIDocument ) - current ui document

      Dim doc As NotesDocument
      Dim row As String

      Set doc = uidoc.Document

      If doc Is Nothing Then Exit Sub

        If uidoc.editmode Then
          Call doc.ReplaceItemValue("$row" , "1")
        Else
          row = s.getEnvironmentString("RevolutionRow")
            If row = "" Then row = "1"
              Call doc.ReplaceItemValue("$row", row)
          End If
    End Sub

    Sub FinalizeRow ( s As NotesSession, uidoc As notesuidocument )

    ' FUNCTION: FinalizeRow ( s, uidoc )
    ' PURPOSE: Reset the rows to first row
    ' ARGUMENTS:
    '      s ( NotesSession ) - global session
    '      uidoc ( NotesUIDocument ) - current ui document

      Call s.setEnvironmentVar("RevolutionRow", "1")
    End Sub

    Now this is a simple example of how you can manually refresh the current UI to not only display the currently active Programmed Table row, but also the currently active Navigation tab. You could just as well conditionally set font colors, weight, and even the text itself by simply computing the values based on the $row field - thus completely allowing your applications to provide a more controlled and more intuitive UI for your users.


    Technorati tags:


    Like what you see? Help feed-the-beast by donating to the site and it's humbly thankful author!

    Comments

    johnhead
    http://www.johndavidhead.com

    Nice stuff Chris ... would be a great idea to create a sample database for people to see it in action ... not from your CRM app but something just generalized.

    John


    Wild Bill
    http://www.billbuchan.com

    And this is exactly how FirM does its "wizard" boxes.. One form, one tabbed table, etc, etc.

    Nice one!

    ---* Bill


    johnm

    I could not get to make this work. I get "Object variable not set" error from OnClickTab function at this line. though 'row' value gets set as "1": Call doc.ReplaceItemValue("$row", row). Any suggestions?


    Jamie Jenkins

    I receive errors as well. I'm not the best developer in the world either, so I second the request to see if you would be so kind as to post a sample database.

    Thanks!


    Chris Toohey
    http://www.dominoguru.com

    Heard lound and clear gang - I'll try and have something posted later this week for everyone to rip apart!


    Chris Toohey
    http://www.dominoguru.com

    For those of you inclined, you can download the example database that uses this method here!

    Comments - and - Feedback, as always, are both greatly appreciated!


    Matt

    Great stuff Chris. I've just modified a couple of forms in a database I've been working on to use this method as we had run out of room using the horizontal tabs.

    One small problem was encountered with the general performance of the form in read mode due to the necessity to close and re-open the document (and the size of the form!). I got around this by removing the computation used to decide which tab image to display and basically duplicated each 'tab' to alternate the on/off image and then used hide-whens based on the value of $row to show or hide as necessary. This then simplifies the code so that you no longer need an environment variable and can set the $row field and call uidoc.RefreshHideFormulas to update in read mode. The result was a much faster changing of tabs.

    Cheers, Matt.


    Jason Kurant
    04/18/2007 03:28:37 PM

    I have a simpler situation that I simply cannot get to work. It worked one day but now it doesn't and I don't know what I did to break it.

    All I want to do is have a tabbed table on a form and have it remember which tab I was on when I open the form again. So, my table name is $TabbedTable, and that is what I put in the table properties as the Name/ID on the HTML tab. I made the field editable and I can see that it gets the value of the tab I select. But when I save and come back in, the $TabbedTable field has the old value!

    I'm really baffled because the other fields retain their values, if changed, but not this field. It is back to the previous value. I can't even understand how that is possible! Can anyone tell me what is wrong with this?


    Keith Strickland
    http://www.keithstric.com
    07/10/2007 10:32:07 AM

    Chris,

    Love this technique, however I'm having issues with field validation. Whenever the uidocument is refreshed it fires the validation formulas. Which is expected, but once you get through the validation error messages it changes to whatever tab you clicked on and then you can't get back to correct the validation issues. Any ideas on how to get around this?

    Thanks for publishing this.
    Keith


    Edward
    02/19/2008 05:01:58 AM

    Wow, i'm really glad that i found this, it's been very helpful, even if i ended up "cheating" with a simpler method. This may not work for everyone, but for simpler tabbed tables, create an array of hotspot images (or buttons) that looks something like this:

    on off off
    off on off
    off off on

    then use a hide when to hide all but one row (e.g. row 1's "hide when" would be '@if $row!="1"' since hide when will work dynamically when $row is changed. you will be hiding and showing a different row, but it happens quickly enough that it's hard to tell the difference. Of course then you want to create a new image (or button) for the "on" vs "off".

    Hopefully this helps some other poor fool that couldn't get the lotusscript to work right!



    Add a New Comment


    (Not Published/Public)
    Remember my Information!

    (Enter the text from the image to proceed)

    (Full markup allowed in the comment field below; play nice!)

    Live Comments Preview