Forms Messaging: Difference between revisions
No edit summary |
|||
Line 56: | Line 56: | ||
FormsMessagePartner = (IFormsMessagePartnerInterface)AFormsMessage.MessageObject; | FormsMessagePartner = (IFormsMessagePartnerInterface)AFormsMessage.MessageObject; | ||
Implementation detail: | |||
The <code>ProcessFormsMessage</code> Method ought to return true or false, depending whether it reacted to the Forms Message, or not. This is so that the 'broadcaster' can be informed about if/how many 'listeners' reacted to its message. (This count is not used yet - but it could be useful at some point.) | |||
==''Dangers of Asynchronism''== | ==''Dangers of Asynchronism''== |
Revision as of 13:24, 25 April 2014
Overview
OpenPetra has a facility to send typed 'messages' between Forms on the client side. This Facility is called 'Forms Messaging'.
Forms Messaging is usually used in scenarios where an action in one Form should trigger an action in the other Form (e.g. data is saved in 'Form A' and 'Form B' needs to re-load that data and re-display it so it is refreshed). Example: The Partner Edit screen sends a Form Message if new Partner gets saved, and another Form Message if an existing Partners' data got saved. That Message could be listened for by arbitrary Forms and they could react appropriately.
Explanation of the Concept
There is always one 'Sending Form' and there are 0..n 'Listening Forms'.
Yes, zero 'Listening Forms' are allowed - it doesn't cause a failure - and the reason for that is that the 'Sending Form' cannot know at run time whether any of the Forms that would potentially be 'listening' for that particular 'message' are indeed instantiated at the time when the 'Sending Form' broadcasts the 'message'. Also, when a developer adds the 'broadcasting' of a 'Form Message' to a Form (and turns it into a 'Sending Form' by doing so) the developer doesn't need to implement the 'listening' for that 'Form Message' in any Form yet - no error will be caused at run time when the 'Forms Message' is 'broadcasted'. (Obviously nothing will happen in any other Form in that case, too!)
Details of the Implementation
Facilitating Classes
- The Class file for the definition of the typed 'messages' is
\csharp\ICT\Petra\Client\CommonForms\FormsMessaging.cs
.- The 'Sending Form' creates an instance of the
TFormsMessage
Class and specifies the kind of typed 'message' using theTFormsMessageClassEnum
in the Constructor of that Class.
- The 'Sending Form' creates an instance of the
- The Class
TFormsList
found inC:\openpetraorg\trunk\csharp\ICT\Petra\Client\CommonForms\FormsList.cs
has the public MethodBroadcastFormMessage
that is used by a 'Sending Form' to 'broadcast' a 'message'.
Forms Messaging is Synchronous
Forms Messaging is synchronous, that is, 'messages' are 'broadcasted' in a synchronous manner and if a 'Listening Form' reacts to a 'message' then the 'broadcasting of that message' stops until that Form has finished whatever processing it will do in response to the 'message', then the 'broadcasting of that message' will continue to the next form, and so on.
See also Dangers of Asynchronism!
How the 'broadcasting' Works Under The Hood
The Method TFormsList.GFormsList.BroadcastFormMessage
iterates all currently opened Forms and checks whether any have got a ProcessFormsMessage
Method by using .NET Reflection. If a Form instance does have that Method then TFormsList.GFormsList.BroadcastFormMessage
runs that Method (synchronously) through .NET Reflection.
Caveats
- A Form can 'listen' to any number of different 'Form Messages'!
- A Form can 'broadcast' any number of different 'Form Messages' in any situation!
- A Form can both be the 'sender' of and the 'listener' for Form Messages at the same time!
- Check out the file
\csharp\ICT\Petra\Client\MPartner\Gui\PartnerEdit.ManualCode.cs
to see code of a Form that does both!
- Check out the file
How-to: 'Broadcasting' of a 'Form Message'
This is best illustrated with a concrete example from OpenPetras' Partner Edit screen. Code for the 'sending' of the FormsMessage can be found in the 'SaveChanges' Method in \csharp\ICT\Petra\Client\MPartner\Gui\PartnerEdit.ManualCode.cs, inside the if (ReturnValue)
code block at the end of the Method.
- A Variable '
TFormsMessage BroadcastMessage;
' is declared at the beginning of that code block. - An instance is created:
BroadcastMessage = new TFormsMessage(TFormsMessageClassEnum.mcNewPartnerSaved, FCallerContext);
.- The Argument '
AMessageClass
' is required and defines the type of 'Form Message' that is being created (and which will be 'broadcasted' later), the Argument 'AMessageContext
' is optional (it allows the 'listening Form(s)' the decision whether they want to act on the 'Forms Message' in a given 'context', or not).- If you want to 'broadcast' new types of 'Form Messages' then you will need to
- create a new value for TFormsMessageClassEnum;
- add a new Method 'SetMessageDataXXX' that can accept typed 'Message Data'.
- If you want to 'broadcast' new types of 'Form Messages' then you will need to
- The Argument '
- Typed 'Message Data' is set by the following code:
BroadcastMessage.SetMessageDataPartner(FPartnerKey,SharedTypes.PartnerClassStringToEnum(FPartnerClass),PartnerShortNameForBroadcast,FMainDS.PPartner[0].StatusCode);
- That typed 'Message Data' is specific to the type of 'Form Message' that was specified when the
TFormsMessage
instance was created. In this case it specifies data that pertains to a specific Partner. - If you want to 'broadcast' new types of 'Form Messages' then you will need to call your new Method 'SetMessageDataXXX' and supply the pertaining typed 'Message Data'
- That typed 'Message Data' is specific to the type of 'Form Message' that was specified when the
- The Form Message is 'broadcasted' through a call to
TFormsList.GFormsList.BroadcastFormMessage(BroadcastMessage);
.- You will need to add Forms that 'Listen' to the 'Forms Message' if anything should happen when this 'broadcast' is issued!
How-to: 'Listening' for a 'Form Message' and Reacting on it
Turning any Form into a Form that 'listens' for 'Form Messages' is easy: you only need to declare a public Method ProcessFormsMessage(TFormsMessage AFormsMessage)
in the *ManualCode.cs file of the Form and do whatever processing is needed in there. Enclose it in a C# Region called 'Forms Messaging Interface Implementation'.
The suggestion is to copy an existing ProcessFormsMessage
Method from another Form and then adapt it to your needs. That way the approaches for processing of Form messages will be kept similar and the XML Comments will match across different ProcessFormsMessage
Method implementations.
Behaviour at Run-time
The ProcessFormsMessage
Method will get called when any Form 'broadcasts' any Form Message (they do that by calling TFormsList.GFormsList.BroadcastFormMessage
)! That Method therefore ought to inspect the MessageClass
of the passed in TFormsMessage
instance to determine if that Method should react on that particular Form Message. If it should do that then the Method will need to 'unpack' the data contained in that instance by casting its MessageObject
to the appropriate Interface.
Example from the Partner Find screens' ManualCode.cs file:
FormsMessagePartner = (IFormsMessagePartnerInterface)AFormsMessage.MessageObject;
Implementation detail:
The ProcessFormsMessage
Method ought to return true or false, depending whether it reacted to the Forms Message, or not. This is so that the 'broadcaster' can be informed about if/how many 'listeners' reacted to its message. (This count is not used yet - but it could be useful at some point.)
Dangers of Asynchronism
'Forms Messaging' is synchronous. The recommendation is to not try to make elements of it synchronous by starting a Thread for 'broadcasting' in the 'Sending Form' or by starting a Thread inside the 'listening' Method of any 'Listening Form(s)'.
The reason for this recommendation is that OpenPetra is limited to only one DB call at any given time (because of an ADO.NET constraint and RDBMS ADO.NET implementation constraints). If Forms Messaging is made to be asynchronous then there is a great likelihood that this could create 'clashes' of DB calls as they will get executed in an asynchronous way.
If the actions that the 'broadcasting' of a 'Forms Message' trigger will not cause DB calls then one could try to make either the 'broadcasting' or the 'listening' asynchronous - but beware that this introduces complexity that should well be warranted! To conserve memory resources and for reasons of less execution time you should not start (a) new Thread(s) but get (a) new Thread(s) from the .NET Thread Pool in this case.