Difference between revisions of "Diagram viewer control"

From Catglobe Wiki
Jump to: navigation, search
 
Line 1: Line 1:
<accesscontrol>Administrators,,Cem,,Maysunshine</accesscontrol>
+
<accesscontrol>Main:MyGroup</accesscontrol>
 
[[Category:Miscellaneous]]
 
[[Category:Miscellaneous]]
  

Latest revision as of 12:43, 17 October 2013

<accesscontrol>Main:MyGroup</accesscontrol>

New Report Design - 2009 => Diagram viewer control

Introduction

In current implementation, there is no specific implementation for a DiagramViewer while it should. There are couple of places where diagram can be viewed but with separate implementation, just to name some:

  • Diagram viewer
  • Report viewer
  • Monitor
  • Dashboard
  • Tabulation result viewer

So, for the sake of consistency, I decided to make a DiagramViewer control that can be used all over the places.

Design Decisions

  • Use the model from Microsoft AJAX.NET that has been applied for resource-ex controls. See the instruction for implementation here
  • The design must reuse the nice features of asynchronous processing that have been applied by Chuong and Dennis
  • AjaxPro is used as a mean for calling server's code from client-side
  • Crash message must be controlled by the control itself (no dump screen), instead user has the ability to enable/disable viewing of full error message

Detail implementation

Code

Code can be found under: /trunk/CatGlobeCustomControls/Report/DiagramViewer folder, there are couple of files under this folder

  • DiagramViewer.cs: control's server code
  • DiagramViewer.js: control's client code
  • progress_bar.gif: the image that is displayed when the diagram is being generated
  • progress_bar_EN.gif: locale dependent loading image (English)
  • progress_bar_DA.gif: locale dependent loading image (Dannish)

Combine MS AJAX.NET and Ajax.Pro

MS AJAX.NET provide a great model for building client-server control which provide separation of client code and creating a link between client's object and server's object. However, to invoke server using AJAX it requires either a page method or a web-service method.

  • Page method: method must be put onto page while we are developing a control and has no idea of what page will use the control
  • Webservice method: require a separate web service implementation which spread the implementation of control to more than one place

Either way are suitable for our need which is putting the server method on the same place as control's server code. AjaxPro appears to be the perfect implementation in this way. However, normal implementation of AjaxPro introduce a naming conflict.

/ajaxpro/CatGlobe.Web.UI.WebControls.Report.DiagramViewer,CatGlobe.Web.UI.WebControls.Report.ashx

Following this link we will have such below code:

if (CatGlobe == undefined) CatGlobe = new();
if (CatGlobe.Web == undefined) CatGlobe.Web = new();
if (CatGlobe.Web.UI == undefined) CatGlobe.Web.UI = new();
if (CatGlobe.Web.UI.Report == undefined) CatGlobe.Web.UI.Report = new();

CatGlobe.Web.UI.WebControls.Report.DiagramViewer_class = function() { };
Object.extend(CatGlobe.Web.UI.WebControls.Report.DiagramViewer_class.prototype, Object.extend(new AjaxPro.AjaxClass(), {
   BeginGenerate: function(diagramResourceId, diagramInfo) {
      return this.invoke("BeginGenerate", { "diagramResourceId": diagramResourceId, "diagramInfo": diagramInfo }, this.BeginGenerate.getArguments().slice(2));
   },
   EndGenerate: function(guid) {
      return this.invoke("EndGenerate", { "guid": guid }, this.EndGenerate.getArguments().slice(1));
   },
   url: '/ajaxpro/CatGlobe.Web.UI.WebControls.Report.DiagramViewer,CatGlobe.Web.UI.WebControls.ashx'
}));

CatGlobe.Web.UI.WebControls.Report.DiagramViewer = new CatGlobe.Web.UI.WebControls.Report.DiagramViewer_class()
  • The first 4 lines define the namespace => not important
  • The last line define the object for ready to use => not important
  • The remain middle lines define the server methods' proxies => important

DiagramViewer.js

While following this link we have a template as below

Type.registerNamespace("CatGlobe.Web.UI.WebControls.Report");

CatGlobe.Web.UI.WebControls.Report.DiagramViewer = function(element) {
   CatGlobe.Web.UI.WebControls.Report.DiagramViewer.initializeBase(this, [element]);
}

CatGlobe.Web.UI.WebControls.Report.DiagramViewer.prototype = {
   initialize: function() {
      CatGlobe.Web.UI.WebControls.Report.DiagramViewer.callBaseMethod(this, 'initialize');
   },

   dispose: function() {
      //Add custom dispose actions here
      CatGlobe.Web.UI.WebControls.Report.DiagramViewer.callBaseMethod(this, 'dispose');
   },
}

CatGlobe.Web.UI.WebControls.Report.DiagramViewer.registerClass('CatGlobe.Web.UI.WebControls.Report.DiagramViewer', Sys.UI.Control);

if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
  • The first line define the namespace
  • The next code block define the class
  • The next code block define content of the class
  • The remaining are required to work with the control template

Merging the 2 codes

As the code shown, there are name conflicting when using the 2 frameworks together. So, the only way to merge the 2 frameworks together is to omit some code from one of them. And due to the fact that namespace definition of AjaxPro is not as important as of AJAX.NET. More than that, the code of AJAX.NET acts as the main frame for the control's client code and AjaxPro code is just a mean for talking to server. Then the code is merged as below (no server code is required for registering AjaxPro):

Type.registerNamespace("CatGlobe.Web.UI.WebControls.Report");

CatGlobe.Web.UI.WebControls.Report.DiagramViewer = function(element) {
   CatGlobe.Web.UI.WebControls.Report.DiagramViewer.initializeBase(this, [element]);
}

///
/// Register proxy using Ajax.Pro
/// This code is taken from /ajaxpro/CatGlobe.Web.UI.WebControls.Report.DiagramViewer,CatGlobe.Web.UI.WebControls.Report.ashx
/// in case we call AjaxPro.Utility.RegisterTypeForAjax on server's code
///
CatGlobe.Web.UI.WebControls.Report.DiagramViewer_proxy = function() { };
Object.extend(CatGlobe.Web.UI.WebControls.Report.DiagramViewer_proxy.prototype, Object.extend(new AjaxPro.AjaxClass(), {
   BeginGenerate: function(diagramResourceId, diagramInfo) {
      return this.invoke("BeginGenerate", { "diagramResourceId": diagramResourceId, "diagramInfo": diagramInfo }, this.BeginGenerate.getArguments().slice(2));
   },
   EndGenerate: function(guid) {
      return this.invoke("EndGenerate", { "guid": guid }, this.EndGenerate.getArguments().slice(1));
   },
   url: '/ajaxpro/CatGlobe.Web.UI.WebControls.Report.DiagramViewer,CatGlobe.Web.UI.WebControls.ashx'
}));


CatGlobe.Web.UI.WebControls.Report.DiagramViewer.prototype = {
   initialize: function() {
      CatGlobe.Web.UI.WebControls.Report.DiagramViewer.callBaseMethod(this, 'initialize');
   },

   dispose: function() {
      //Add custom dispose actions here
      CatGlobe.Web.UI.WebControls.Report.DiagramViewer.callBaseMethod(this, 'dispose');
   },

   ///
   /// Readonly property
   ///
   get_proxy: function() {
      if (this._proxy == undefined) {
         // Setup the server object
         this._proxy = new CatGlobe.Web.UI.WebControls.Report.DiagramViewer_proxy();
      }
      return this._proxy;
   }
}

CatGlobe.Web.UI.WebControls.Report.DiagramViewer.registerClass('CatGlobe.Web.UI.WebControls.Report.DiagramViewer', Sys.UI.Control);

if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();

And now, the 2 frameworks can work together. Note that there is an addition get method for retrieving the proxy, in order to call server method client code just need to get the proxy by call get_proxy method and then call the desired method. On server side, as normal, we just need to define 2 server method as below:

      [AjaxMethod(HttpSessionStateRequirement.ReadWrite)]
      public static string BeginGenerate(string diagramResourceId, DiagramInfo diagramInfo);

      [AjaxMethod(HttpSessionStateRequirement.ReadWrite)]
      public static string EndGenerate(string guid);

Generation steps

Diagram generation require a DiagramInfo object to be passed. See below links for more detail:

The following sequence diagram describe the generation steps (client-server communication details): DiagramViewer - Update process.gif

NOTE: the client side classes have an underscore prefix just to solve naming problem in Enterprise Architect, in real implementation class name does no have an underscore prefix.

The generation is a 2 phases process:

  1. Register the generation task
  2. Get the generation result

For a more detail:

  1. User click on a button (or something else) which triggers the update function of client side DiagramViewer
  2. Stepped through input validation, update method passes processing to proxy class DiagramViewer_proxy wint asynchronous mode and specify _endGenerate method as the callback
  3. AjaxPro framework now handles the work and make a remote invoke into server
  4. Server method BeginGenerate validates the input again, and with a valid input it register the work with ConcurrentAjaxRequest which put the work into ThreadPool for processing and return back a task id
  5. The AjaxPro now pass the task id to the callback which is _endGenerate
  6. _endGenerate first check all condition to make sure the generation was successfully registered with server. If nothing is wrong, then the method once again delegate its processing to proxy's EndGenerate method passing the task id and given method _processResult as a callback
  7. AjaxPro does the same work again by calling server's EndGenerate which in turn get the result back from ConcurrentAjaxRequest's ThreadPool. The result which is HTML code is returned back to client
  8. Now, method _processResult is called by AjaxPro as responding to returned result. And _processResult is free at processing the returned HTML, which is currently displaying the HTML code

There are additional methods on client side DiagramViewer for handling exceptions like:

  • _onerror: display error message
  • _ontimeout: display timeout message

In fact, there are additional processing that gives layout processing like displaying a loading image before call _beginGenerate and replace the loading image with returned HTML at the end of _endGenerate.

Document revisions

Version No. Date Changed By Description Svn revision
0.1 30.07.2009 Nguyen Trung Chinh Create first version 54885