I am presently working on a project that requires coordination between separate components contained within a frameset. In Notes 8 this would be considered a composite application but I need to implement this solution in Notes 6.0. Fortuantely there has been some excellent articles recently posted by Tim Tripcony, Jan Shulz , and Nathan T Freeman on the subject of remote event binding - an important part for creating composite applications.
I have now extended the .Domino Framework by adding a new DominoListener class. This class is designed to listen for events that occur in a nominated Notes UI class.
Class DominoListener As DominoBaseClass
Private iParent As Variant ' The object requesting a listener
Private iTarget As Variant ' The UI object being monitored for one or more events
Sub New(Parent As Variant,Target As Variant), DominoBaseClass(ENUM_CLASS__ABSTRACT + "DOMINOLISTENER")
End Sub
Property Get Parent As Variant
Set Parent = iParent
End Property
Property Set Parent As Variant
Set iParent = Parent
End Property
Property Get Target As Variant
Set Target = iTarget
End Property
Property Set Target As Variant
Set iTarget = Target
End Property
End Class
The DominoLIstener class is extended by DominoDocumentListener and DominoViewListener classes designed to specifically listen for events in NotesUIDocument and NotuesUIView classes. ( a DominoDatabaseListener may be added later).
Class DominoViewListener As DominoListener
Sub New(Parent As Variant,Target As Variant)
Call DominoBaseClass..ValidateClass(ENUM_CLASS__ABSTRACT,"DOMINOVIEWLISTENER")
If (Not Typename(Target) = "NOTESUIVIEW") Then End
Set iTarget = Target
End Sub
End Class
Class DominoDocumentListener As DominoListener
Sub New(Parent As Variant,Target As Variant)
If (Not Typename(Target) = "NOTESUIDOCUMENT") Then End
Set iTarget = Target
End Sub
End Class
The above classes are all abstract classes and demonstrate the use of a DominoBaseClass covered in a previous blog to implement/enforce class abstraction.
To wire two objects together I need to create a Listener class that defines the events to be monitored and the action to take when the event occurs. The following demonstrates a single event, but I could just as easily monitor multiple events by adding additional delegates/methods to the code.
Class OpenFeatureListener As DominoDocumentListener
Sub New(Parent As Variant,Target As Variant)
If Not Parent Isa "DominoFeature" Then End
Set iParent = Parent
On Event PostOpen From Target Call DelegatePostOpen
End Sub
Sub DelegatePostOpen(Source As NotesUIDocument)
Dim DesignListUIDoc As NotesUIDocument
Dim DesignListDoc As NotesDocument
Dim DesignList As DominoDesignList
Set DesignListDoc = New NotesDocument(Source.Document.ParentDatabase)
Call UIW.SetTargetFrame(ENUM_FRAME_DESIGN_LIST)
Set DesignListUIDoc = UIW.ComposeDocument("","",ENUM_FORM_DESIGN_LIST)
Set DesignList = New DominoDesignList(DesignListUIDoc)
DesignList.Title$ = iParent.Title$
Call DesignListUIDoc.Refresh()
End Sub
End Class
The listener is invoked in the parent object with a simple statement
Set Handler = New OpenFeatureListener(Me,Source)
Within a composite application an object may have the need to observe multiple objects. I have therefore created a DominoBroker class as a container for Listeners.
Class DominoBroker
Private iListeners As Variant
Sub Register(Listener As Variant)
If Not Listener Isa "DominoListener" Then End
If Not Isarray(iListeners) Then Redim iListeners(0) Else Redim Preserve iListeners(Ubound(iListeners)+1)
Set iListeners(Ubound(iListeners)) = Listener
End Sub
End Class
This is not unlike Nathan's totally cool concept of a unicache. Ideally I would like to have a single Broker for an entire Notes session, but presently I seem to be constrained by LotusScript to only being able to get a handle on UI objects that are currently active (one NotesUIDocument, one NotesUIView) or when LotusScript code launches a UI object (e.g. UIW.EditDocument). So far I have only been able to share the love (broker) around between UI objects that have been loaded by a common UI object (class). Documents opened in preview mode from a view, embedded views etc. are a Remote event black hole.
Combining the above with framesets (to contruct the presentation layer) allows me to implement design patterns in Notes 6 very similar to Notes 8 composite applications. The down side(?) is that I am restricted to only wiring Notes components and only for some very specific scenarios. On the plus side this is all Notes code with no Eclipse required.
Note: The above is designed for Notes 6/7 only. Notes 8 provides additional classes to support composite applications as well as a composite application editor.
1 Nathan T Freeman Permalink For the record, the unicache can get very difficult to work with. I did have some measure of success from embedded objects by doing callbacks from the embedded context. If you go back up to NotesUIWorkspace.currentDocument, you can trigger events on that
container, and you get a sharing opportunity. I would typically use
the Query/Post send events for that.
Did you get my email yesterday?
2 Tim Tripcony Permalink I just updated my "in-memory placeholders" example from last July... noticed that it wasn't the latest incarnation of my experiments binding to embedded contexts, so I uploaded the newest version:
http://www.timtripcony.com/blog.nsf/downloads/TTRY75G8CT.htm/$FILE/placeholders.zip
The example database just launches to a view inside a frameset, and
the view establishes a unicache. All document opens are redirected
in a way that allows the document to share a pointer to the
unicache with the view. The form embeds a view that also grabs a
pointer to the same unicache, and any document opened from the
embedded view gets a pointer as well. So anything that impacts
objects stored in the cache is accessible immediately from any
other open context.
For example, if you open the view and click "List Placeholders", it
tells you that you haven't added any yet. But if you open a
document, then open another from the embedded view, then open
another - either from the view embedded in the last document you
opened, or by tabbing back to the original view and opening a
different document - then click "Add to Placeholders", then you can
switch back to any tab you still have open and click "List
Placeholders" again, and it shows the document you just added. This
isn't writing anything to disk - it's not updating the user's INI,
stamping a profile document, etc. - this is all occurring in
RAM.
So in a listener context, you could add some object to the unicache
that has a method that responds to notifications from any context.
Say, for example, you have one document open in one frame, and a
related document open in another frame. When something occurs in
the first frame that affects the other, you call the shared
object's method, which has a handle on the related UI document and
can refresh it, change field values, etc. Pretty powerful stuff...
but, as Nathan mentions, this can be tricky to manipulate. Let me
know if you have any questions about this - particularly the timing
involved in linking everything together, which turns out to be the
most difficult aspect of this approach.
3 Peter Presnell Permalink Thanks for the tips guys, I really appreciate it. It turns out that I was relying on the LotusScript debugger to tell me what events are firing and when. For some reason the events for an embedded view dont get trapped in the LotusScript debugger. By placing teh following line in the PostOpen event I have been able to get a handle on the embedded view.
If Typename(UIW.CurrentDocument) = "NOTESUIDOCUMENT" Then Call
UIW.CurrentDocument.Send
I then just need to create a Listener object to respond to that
event.
@Nathan: No I didn't receive your e-mail.