ArcGis Server ADF Toolbar与Map的客户端和服务端交互过程分析

 

1、Toolbar的初始化步骤

   [1]、Toolbar.FrameworkInitialize()
         
        在这里建立所有的Tool实例(但是未初始化完其所有属性)。
         
   [2]、Toolbar.OnLoad()
         
        在这里主要是调用了AddToolItems()方法建立Map里边的工具实例。Toolbar的工具类名是ToolbarItem,而Map里边的工具类名是MapToolItem,两者并不一样。
         
        AddToolItems()具体工作:
         
        (1)、取出Toolbar的所有ToolbarItem(s)
        (2)、foreach(循环)
             {
                1、调用callToolbarItemInitialize()对ToolbarItem进行进一步的初始化,而callToolbarItemInitialize()内部又调用LoadClassFromAssembly(),其实就是建立起ToolBarItem的ServerActionClass的实例,这个实例通过一个Hashtable保存,让ToolbarItem名称和Object配对。
                 
                2、以ToolbarItem为原型,建立MapToolItem实例,并添加到Map的内部工具集中。
                 
                   MapToolItem有一个属性ServerToolAction,用来保存该Item对应的ServerActionClass的实例。
             }
            
   [3]、ToolbarBase.RenderContents() -> RenderRuntime()
   
        这个步骤主要用来生成工具条及工具按钮的Client HTML代码
   
            RenderRuntime()
            {
                ......           
                   
                //
                // 调用ToolbarItem的Render()方法,让每一个ToolBarItem都产生自己
                // 对应的方法
                //
                foreach (ToolbarItem item in this.ToolbarItems)
                {
                    item.Render(this, num2, num3, false, builder);
                }
                ...
            }
           
        Tool.Redner()里边有很多工作,但是最关键的是以下几行代码:
       
            Render()
            {
                ...
                if (!inDesignMode && !base.Disabled)
                {
                    builder.Append(string.Format("  onMouseDown=\"ToolbarMouseDown( '{0}', '{1}', 'Tool', event);\"\n", toolbar.ClientID, this.Name));
                    builder.Append(string.Format("  onMouseOver=\"ToolbarMouseOver('{0}', '{1}');\"\n", toolbar.ClientID, this.Name));
                    builder.Append(string.Format("  onMouseOut=\"ToolbarMouseOut( '{0}', '{1}');\"\n", toolbar.ClientID, this.Name));
                }
                ...
            }  
           
        这个代码的意思就是说,如果用户单击了工具条上的按钮,就执行Javascript的函数ToolbarMouseDown()等,这个ToolbarMouseDown()出现在ESRI.ADF.UI.Toolbar.js里边。
           
           
2、客户端如何发送信息到服务端?

   当我们单击工具条上的一个按钮时,根据上面的分析,ToolbarMouseDown()被执行。
   
   我们来看看ToolbarMouseDown()具体干啥:
   
       function ToolbarMouseDown(toolbarName, toolbarItemName, buttonType, e)
       {
            .......                   
         if (buttonType == "Tool") {
             ......
          var clientAction =  toolbar.items[toolbarItemName].clientAction;
          if (clientAction != null)
          {
           var clientActions = "";
              if (!toolbar.items[toolbarItemName].isClientActionCustom)
              {
            var buddies = toolbar.buddyControls;
            if (buddies != null) {
             for (var i = 0; i < buddies.length; i++)
             {
              var modeField = f.elements[buddies[i] + "_mode"];
              if (modeField != null)
               modeField.value = toolbarItemName;
              var cursor = toolbar.items[toolbarItemName].cursor;
              if (cursor != null)
               clientActions = clientActions + clientAction + " ( '" + buddies[i] + "' , '" + toolbarItemName + "', " + toolbar.items[toolbarItemName].showLoading + ",'" + cursor + "'); ";
              else
               clientActions = clientActions + clientAction + " ( '" + buddies[i] + "' , '" + toolbarItemName + "', " + toolbar.items[toolbarItemName].showLoading + "); ";
             }
            }
           }
           else
           {
            clientActions = clientAction;
           }
           
           ...
           if (toolbar.items[toolbarItemName].preExecFunction != null)
            clientActions += toolbar.items[toolbarItemName].preExecFunction;
           
                                // 调用ClientAction
           var clientActionFunction = new Function(clientActions);
           clientActionFunction.call(null);
           
           //select this tool and unselect others
           Toolbars[toolbarName].selectTool();
           Toolbars[toolbarName].refreshGroup();
          }
         }
         ....
       }
               
   这段代码的简单意思就是说,如果ClientAction是我们自定义的,则直接执行。如果不是,则构造如下形式的调用:
        
        Function.Call("Map[XXX]('Map1','[ToolName]',showLoading,cursor");
    
   MapXxx(..)其实是Map内部提供的JS函数(ESRI.ADF.UI.Map.js里边),总共有如下类型:
    
     MapDragImage = ESRI.ADF.MapTools.MapDragImage = function(mapid, mode, showLoading, cursor);
     MapDragRectangle = ESRI.ADF.MapTools.DragRectangle = function(mapid, mode, showLoading, cursor) ;
     MapBox = MapDragBox = ESRI.ADF.MapTools.DragRectangle;
     MapPoint = ESRI.ADF.MapTools.Point = function(mapid, mode, showLoading, cursor) ;
     MapLine = ESRI.ADF.MapTools.Line = function(mapid, mode, showLoading, cursor, vectorToolbarState) ;
     MapPolyline = ESRI.ADF.MapTools.Polyline = function(mapid, mode, showLoading, cursor, vectorToolbarState) ;
     MapPolygon = ESRI.ADF.MapTools.Polygon = function(mapid, mode, showLoading, cursor, vectorToolbarState) ;
     MapDragCircle = MapCircle = ESRI.ADF.MapTools.Circle = function(mapid, mode, showLoading, cursor, vectorToolbarState) ;
     MapDragOval = MapOval = ESRI.ADF.MapTools.Oval = function(mapid, mode, showLoading, cursor, vectorToolbarState) ;
    
   现在我们以MapDragRectangle()为来自来分析:
    
        MapDragRectangle = ESRI.ADF.MapTools.DragRectangle = function(mapid, mode, showLoading, cursor)
        {
         var map = $find(mapid);
         if(!map) { map=Pages[mapid]; }
         if(ESRI.ADF.UI.Map.isInstanceOfType(map))
         {
          if(mode==='MapZoomIn')
          {
           map.set_mouseMode(ESRI.ADF.UI.MouseMode.ZoomIn);
          }
          else if(mode==='MapZoomOut')
          {
           map.set_mouseMode(ESRI.ADF.UI.MouseMode.ZoomOut);
          }
          else
          {
           var onComplete = Function.createDelegate(map, function(geom) {
            var geomString = geom.get_xmin()+':'+geom.get_ymin()+'|'+geom.get_xmax()+':'+geom.get_ymax();
            this.doCallback('EventArg=DragRectangle&coords='+geomString+'&'+mapid+'_mode='+mode,this);  
           });
           
           map.getGeometry(ESRI.ADF.Graphics.ShapeType.Envelope,onComplete,function(){map.__activeToolMode=null;},ESRI.ADF.MapTools.lineColor,ESRI.ADF.MapTools.fillColor,cursor, true);
           map.__activeToolMode = mode;
          }
          ..
          }
         }
         ...
        };
       
   当Tool Name是MapZoomIn和MapZoomOut时,Map内部提供支持。否则就执行一些操作之后,然后把操作信息通过doCallback()发送到Server端。所谓执行一些操作,实际上就是画图,通过调用map.getGeometry(...,onComplete,...)来执行,当执行完成之后,onComplete被执行。
   
  var onComplete = Function.createDelegate(map, function(geom)
  {
   var geomString = geom.get_xmin()+':'+geom.get_ymin()+'|'+geom.get_xmax()+':'+geom.get_ymax();
   this.doCallback('EventArg=DragRectangle&coords='+geomString+'&'+mapid+'_mode='+mode,this);  
  });
  
   执行操作完成之后,map会构造图形的geom实例,并作为onComplete()的参数。通过上面的代码我们状况,这个时候只是简单执行map.doCallback()把信息发送回到Server。
    
    map.doCallback()用到一个重要的内部变量callbackFunctionString,这个变量的值由Server端ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl控件生成,看看它的生成代码:
     
        protected internal virtual string CallbackFunctionString
        {
            get
            {
                if (this.RequiresPartialPostback)
                {
                    this.callbackFunctionString = string.Format("__esriDoPostBack('{0}','{1}', argument, ESRI.ADF.System.ProcessMSAjaxCallbackResult, context)", this.UniqueID, this.ClientID);
                }
                else
                {
                    //生成语句:WebForm_DoCallback('__Page',argument,ESRI.ADF.System.processCallbackResult,context,postBackError,true);
                   
                    this.callbackFunctionString = this.Page.ClientScript.GetCallbackEventReference(this, "argument", "ESRI.ADF.System.processCallbackResult", "context", "postBackError", true);
                   

                }

                return this.callbackFunctionString;
            }
        }
       
   RequiresPartialPostback在什么情况为TRUE呢?就是当你的Page中存在一个ScriptManager时,这个值为TRUE,执行部分Postback。如果没有ScriptManager,将执行Callback。通过上面代码我知道,如果是postback,则执行:
          __
        __esriDoPostBack(this.UniqueID,this.ClientID, argument, ESRI.ADF.System.ProcessMSAjaxCallbackResult, context)
         
   如果是callback,则执行:    

        WebForm_DoCallback('__Page',argument,ESRI.ADF.System.processCallbackResult,context,postBackError,true);           
    
   不管是Postback还是Callback,信息发送到服务端之后,将由服务端进行处理,然后再回传信息到客户端,由CallbackFunctionString里边定义的JS函数处理.如果是postback,则由ESRI.ADF.System.ProcessMSAjaxCallbackResult()处理,否则由ESRI.ADF.System.processCallbackResult()处理。
       
3、服务端如何处理信息?        

   当信息达到Server端之后,系统通过调用RaiseCallbackEvent(arg)或者RaisePostbackEvent(arg)把参数传递到Map内部。然后系统调用Map.GetCallackResult()取得Map要传回给客户端的信息。
  
   Map.GetCallbackResult()的大概执行步骤:
  
   [1]、通过当前的Tool Item得到对应的ServerActionClass的实例。这个实例是在callToolbarItemInitialize()被建立的,具体看前面的分析。
  
   [2]、先把参数拆分成Name/Value值对,然后调用handlePostBack()对“EventArg”的参数进行处理,
      
           handlePostback()
           {
              ....
              if(这是一个选择矩形操作?)
              {
                    ....
                    Point[] pointArray = Utility.ConvertVectorStringToDoublePointArray(str);
                    args = new MapRectangleEventArgs(this, str2, new Envelope(pointArray[0], pointArray[1]));
                    serverToolAction.ServerAction(args);
                    ...
              }
              ....
           }
      
       在这一步,我们自己定义的IMapServerToolAction.ServerAction()终于被执行了。

   [3]、 最后通过代码以下代码把信息返回给客户端。

             return this.CallbackResults.ToString();
          
        注意这个CallbackResults,它是CallbackResultCollection,里边可以添加不同的CallbackResult,比如我们也可以在我们的ServerAction()里边添加我们自己的CallbackResult到里边,让Map统一返回。
       
        CallbackResult类构造函数如下:
       
            public CallbackResult( string controlType,string controlID,string eventArg,params object[] parameters)
       
        其中,eventArg只能是content, innercontent, image, or javascript(还有set、invoke)。如果是javascript,则前面两个参数没有作用,否则必须填写。有关部门CallbackResult的具体使用可以参照其它例子。
        
4、客户端如何处理服务端返回的信息?

   服务端返回的信息由

       ESRI.ADF.System.processCallbackResult()

   进行处理,具体可以看ESRI.ADF.System.debug.js的代码。
  
5、扩展

   从上面看到,释放鼠标导致画图操作完成之后,map就发出callback了,如果我们想再执行一些操作再发回怎么办?可以自己定义ClientAction来达到这个目的,如设置ClientAction成“MyDragRectangle()“,
  
       function MyDragRectangle_Simple()
       {
            alert("selct rectangle now..");
           
            //
            // 第二个参数,是Tool Name,如果是MapZoomIn或者MapZoomOut,则
            // 将由Map内置的功能处理。
            //
            // 为了定义自己的选择操作,必须使用其它名字。
            //
            MapDragRectangle('Map1','Select',false,'hand');
        }
      
       function MyDragRectangle_Adv()
       {
         var map = $find(mapid);
   
         if(ESRI.ADF.UI.Map.isInstanceOfType(map))
         {
         var onComplete = Function.createDelegate(map, function(geom)
                {
      var geomString = geom.get_xmin()+':'+geom.get_ymin()+'|'+geom.get_xmax()+':'+geom.get_ymax();
       
                    //........干我们自己的活..(比如添加图形到Map上,不让它消失等)
       
      this.doCallback('EventArg=DragRectangle&coords='+geomString+'&'+mapid+'_mode='+mode,this);  
         });
       
                map.getGeometry(ESRI.ADF.Graphics.ShapeType.Envelope,onComplete,function(){map.__activeToolMode=null;},ESRI.ADF.MapTools.lineColor,ESRI.ADF.MapTools.fillColor,cursor, true);
                map.__activeToolMode = mode;
            }      
       }
       

 

posted @ 2009-10-15 10:58  鹰王  阅读(987)  评论(5编辑  收藏  举报