前面的开发系列均是使用server开发模板程序Web Mapping Application,工具条上的基本工具是已经在模板中定制好的,在实际项目应用中,我们需要的工具远远不仅如此,如何在工具条中增加新的自定义工具是开发系列(五)所要描述的,其中使用ASP.Net 2.0 Callback framework进行异步刷新地图是重点。
目标:
自定义工具按钮进行矩选查询,高亮显示所选择的地图要素,页面下方的Gridview显示所选择要素的属性信息。
准备工作:
1.了解ESRI.ArcGIS.Server.WebControls.IMapServerToolAction接口
2.了解ASP.Net 2.0 Callback framework
3.新建一个网站,在ArcGIS Web Controls控件中拖动如下控件:Toolbar、Map、Toc、MapResourceManager,以及常用控件Label、DropDownList、Gridview。
4.设置控件属性,Toolbar、Toc的BuddyControls均为Map1,Toolbar的BuddyControlType为Map,Map控件的MapResourceManager为MapResourceManager1。
5.更改MapResourceManager属性,添加两个Resource:Selection和NorthAmerica,类型分别是Graphics Layer和ArcGIS Server Internet。
最后视图效果:
![](/Article/UploadFiles/200901/20090113103044173.gif)
思路:
现在重新想想我们要做什么,首先要自定义一个工具按钮,使用该工具后在地图上进行矩形选择,对选择的要素高亮显示,同时gridview显示出这些要素的属性信息。整个过程看似容易,实际上需要在客户端和服务器端之间来回切换,异步调用,这里用到了ASP.Net Callback framework,其实Server中很多地图操作都基于asp.net callback,或是实现了ICallbackEventHandler接口,理解了这一段程序开发有利于深入了解Server地图刷新、Task等组件的工作机制。
首先在Toolbar上新增一个按钮Select Features,上图其实已经加入了,加入的方法是,选择Toolbar控件属性ToobarItems,添加一个Tool,设置以下值:
Text:Select Feature
CientAction
![](/Article/UploadFiles/200901/20090113103044830.gif)
ragRectangle
Name:SelectTool
ServerActionAssembly:App_Code
ServerActionClass:SelectFeatures
OK,搞定!
除了上述属性外,还可以设置该按钮各种状态下的图片显示、ToolTip等等,这里就省了,纵观这些属性,可以看出既有js脚本的交互(已经封装了,通过 ToolEventArgs传入),也有服务器端功能的实现,这时我们需新建一个类SelectFeatures,并实现 IMapServerToolAction接口,类中实现IMapServerToolAction的方法ServerAction。
![](/Article/UploadFiles/200901/20090113103044124.gif)
public
class SelectFeatures : IMapServerToolAction
{
public
void ServerAction(ToolEventArgs args)
} 代码实现:
1.获取矩形框的屏幕坐标
要查询矩选的地图信息,首先应知道矩形的坐标,在服务器端如何获取呢?
Map mapctrl = null;
mapctrl = (Map)args.Control;
// 获取下拉框中的数据,在后面实现
string targetlayername = (string)mapctrl.Page.Session["TargetLayer"];
![](/Article/UploadFiles/200901/20090113103044514.gif)
RectangleEventArgs rectargs = null;
// 强制类型转换为RectangleEventArgs
rectargs = (RectangleEventArgs)args;
![](/Article/UploadFiles/200901/20090113103044514.gif)
// 获取矩形选择框的屏幕坐标
System.Drawing.Rectangle rect = rectargs.ScreenExtent;
ESRI.ArcGIS.ADF.Web.Geometry.Point minpnt = ESRI.ArcGIS.ADF.Web.Geometry.Point.ToMapPoint(rect.Left, rect.Bottom, mapctrl.Extent, (int)mapctrl.Width.Value, (int)mapctrl.Height.Value);
ESRI.ArcGIS.ADF.Web.Geometry.Point maxpnt = ESRI.ArcGIS.ADF.Web.Geometry.Point.ToMapPoint(rect.Right, rect.Top, mapctrl.Extent, (int)mapctrl.Width.Value, (int)mapctrl.Height.Value);
![](/Article/UploadFiles/200901/20090113103044514.gif)
ESRI.ArcGIS.ADF.Web.Geometry.Envelope mappoly = null;
// minpnt、maxpnt分别是左下、右上坐标点
mappoly = new ESRI.ArcGIS.ADF.Web.Geometry.Envelope(minpnt, maxpnt); 所有的信息都是通过args获取,它是一个ESRI.ArcGIS.ADF.Web.UI.WebControls.ToolEventArgs对象,包含了客户端Map控件和当前客户端工具的信息,RectangleEventArgs是它的子类,强制性转换后得到矩选的矩形坐标,最后得到一个 Envelope,用于spatialfilter.Geometry属性。
2.查询所选择的要素并对Graphics Layer进行渲染实现高亮
这部分内容完全可以参考《
ArcGIS Server 开发系列(三)--漫游 Graphics data sources》,只需要注释掉WhereClause属性赋值,再增加一行代码:
ESRI.ArcGIS.ADF.Web.SpatialFilter spatialfilter = new ESRI.ArcGIS.ADF.Web.SpatialFilter();
spatialfilter.ReturnADFGeometries = false;
spatialfilter.MaxRecords = 1000;
//spatialfilter.WhereClause = txtQuery.Text;
spatialfilter.Geometry = mappoly; 3.异步刷新Gridview显示地图要素的属性
GridView gdview = (GridView)mapctrl.Page.FindControl("GridView1");
![](/Article/UploadFiles/200901/20090113103044514.gif)
object[] oa = new
object[1];
string showtable = "'visible'";
![](/Article/UploadFiles/200901/20090113103044514.gif)
// datatable为矩选时所选择的地图要素,绑定到gridview
gdview.DataSource = datatable;
gdview.DataBind();
![](/Article/UploadFiles/200901/20090113103044514.gif)
string returnstring = null;
![](/Article/UploadFiles/200901/20090113103044514.gif)
using (System.IO.StringWriter sw = new System.IO.StringWriter())
![](/Article/UploadFiles/200901/20090113103044124.gif)
![](/Article/UploadFiles/200901/20090113103044592.gif)
{
HtmlTextWriter htw = new HtmlTextWriter(sw);
gdview.RenderControl(htw);
htw.Flush();
returnstring = sw.ToString();
}
![](/Article/UploadFiles/200901/20090113103044514.gif)
// innercontent相当于innerhtml
CallbackResult cr = new CallbackResult("div", "griddiv", "innercontent", returnstring);
// 通过回调将信息从服务器端传输到客户端
mapctrl.CallbackResults.Add(cr);
![](/Article/UploadFiles/200901/20090113103044514.gif)
if (datatable.Rows.Count > 1)
![](/Article/UploadFiles/200901/20090113103044124.gif)
![](/Article/UploadFiles/200901/20090113103044592.gif)
{
showtable = "'visible'";
}
else
![](/Article/UploadFiles/200901/20090113103044124.gif)
![](/Article/UploadFiles/200901/20090113103044592.gif)
{
showtable = "'hidden'";
}
![](/Article/UploadFiles/200901/20090113103044514.gif)
string sa = "var griddiv = document.getElementById('griddiv');";
sa += "griddiv.style.visibility = "
+ showtable +
";";
oa[0] = sa;
![](/Article/UploadFiles/200901/20090113103044514.gif)
CallbackResult cr1 = new CallbackResult(null, null, "javascript", oa);
mapctrl.CallbackResults.Add(cr1); 这段代码最关键的类是CallbackResult,它简化了web adf framework中客户端回调的处理,不用再创建自己的客户端和服务器端逻辑,使用CallbackResult就可以将信息传回客户端,更新客户端页面的内容、图片或执行js脚本。关于CallbackResult构造方法第三个参数,下面js代码写的很详细:
![](/Article/UploadFiles/200901/20090113103044124.gif)
if (action=="content")
{
o = document.getElementById(actions[1]);
if (o != null)
![](/Article/UploadFiles/200901/20090113103044779.gif)
![](/Article/UploadFiles/200901/20090113103044830.gif)
{
o.outerHTML=actions[3];
}
}
![](/Article/UploadFiles/200901/20090113103044124.gif)
else
if (action=="innercontent")
{
o = document.getElementById(actions[1]);
if (o != null)
![](/Article/UploadFiles/200901/20090113103044779.gif)
![](/Article/UploadFiles/200901/20090113103044830.gif)
{
o.innerHTML=actions[3];
}
}
else
if (action=="image")
![](/Article/UploadFiles/200901/20090113103044124.gif)
![](/Article/UploadFiles/200901/20090113103044592.gif)
{
o = document.images[actions[1]];
if (o != null)
![](/Article/UploadFiles/200901/20090113103044779.gif)
![](/Article/UploadFiles/200901/20090113103044830.gif)
{
o.src = actions[3];
}
else alert (actions[1] +
" was null");
}
![](/Article/UploadFiles/200901/20090113103044124.gif)
else
if (action=="javascript")
{
eval(actions[3]);
} 4.填充DropDownList
DropDownList显示的是ArcGIS Server Internet地图数据源所包含的图层名称,选择哪个图层,矩选时就对哪个图层进行查询,DropDownList的填充在Page_PreRender过程中。
if (!IsPostBack)
![](/Article/UploadFiles/200901/20090113103044124.gif)
![](/Article/UploadFiles/200901/20090113103044592.gif)
{
ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality mf = (ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality)Map1.GetFunctionality(1);
![](/Article/UploadFiles/200901/20090113103044882.gif)
ESRI.ArcGIS.ADF.Web.DataSources.IGISResource gisresource = mf.Resource;
bool supported = gisresource.SupportsFunctionality(typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality));
![](/Article/UploadFiles/200901/20090113103044882.gif)
if (supported)
![](/Article/UploadFiles/200901/20090113103044779.gif)
{
ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality qfunc = (ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)gisresource.CreateFunctionality(typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), null);
![](/Article/UploadFiles/200901/20090113103044882.gif)
string[] lids;
string[] lnames;
qfunc.GetQueryableLayers(null, out lids, out lnames);
for (int i =
0; i < lnames.Length; i++)
![](/Article/UploadFiles/200901/20090113103044779.gif)
{
LayerDropDownList1.Items.Add(lnames);
}
Session["TargetLayer"] = LayerDropDownList1.Items[0].Value;
}
} 5.实现ICallbackEventHandler接口
Default.aspx.cs的_Default实现ICallbackEventHandler接口,在类中实现RaiseCallbackEvent和GetCallbackResult两个方法,做ASP.Net 2.0的对ICallbackEventHandler应该是再熟悉不过了
![](/Article/UploadFiles/200901/20090113103044933.gif)
但下来框显示图层为什么要用到callback?
protected void Page_Load(object sender, EventArgs e)
![](/Article/UploadFiles/200901/20090113103044124.gif)
![](/Article/UploadFiles/200901/20090113103044592.gif)
{
if (!IsPostBack)
![](/Article/UploadFiles/200901/20090113103044779.gif)
{
Session["TargetLayer"] = "";
}
LayerDropDownList1.Attributes.Add("onchange", "ChangeLayer()");
sADFCallBackFunctionInvocation = Page.ClientScript.GetCallbackEventReference(this, "message", "processCallbackResult", "context", "postBackError", true);
}
![](/Article/UploadFiles/200901/20090113103044514.gif)
public void ChangeDropDownListServer(string ea)
![](/Article/UploadFiles/200901/20090113103044124.gif)
![](/Article/UploadFiles/200901/20090113103044592.gif)
{
![](/Article/UploadFiles/200901/20090113103044779.gif)
char[] parser_char =
{ ',' };
string[] messages = ea.Split(parser_char);
string dll1 = messages[1];
Session["TargetLayer"] = dll1;
}
![](/Article/UploadFiles/200901/20090113103044514.gif)
![](/Article/UploadFiles/200901/20090113103044592.gif)
ICallbackEventHandler 成员#region ICallbackEventHandler 成员
public void RaiseCallbackEvent(string eventArgument)
![](/Article/UploadFiles/200901/20090113103044779.gif)
![](/Article/UploadFiles/200901/20090113103044830.gif)
{
if (eventArgument.Contains("ddl1"))
![](/Article/UploadFiles/200901/20090113103044779.gif)
{
ChangeDropDownListServer(eventArgument);
}
}
public
string GetCallbackResult()
![](/Article/UploadFiles/200901/20090113103044779.gif)
![](/Article/UploadFiles/200901/20090113103044830.gif)
{
return returnstring;
} 原因就在这里,改变Session ["TargetLayer"]的值,SelectFeatures需要知道是对哪个图层进行查询的,从而对在那个图层选择要素进行高亮及属性显示,这里 callback仅仅是在做了变量值的处理。最后在页面之间加入js脚本ChangeLayer()。
<script type="text/javascript" language="javascript">
var context;
function ChangeLayer()
![](/Article/UploadFiles/200901/20090113103044124.gif)
![](/Article/UploadFiles/200901/20090113103044592.gif)
{
var message;
var ddl1value = document.getElementById('LayerDropDownList1').value;
message = 'ddl1';
message += ',' + ddl1value;
<%=sADFCallBackFunctionInvocation%>
}
</script> 运行程序:
![](/Article/UploadFiles/200901/20090113103044672.gif)
其中黄色区域就是Select Features按钮矩选的要素,下方gridview显示了查询到的属性结果。
程序中有两个地方用到了异步刷新,一个是ASP.Net 2.0原有接口ICallbackEventHandler,另一个是Web ADF framework的CallbackResult类,最初认为简单异步刷新用自己写的XMLHttpRequest请求更为简单,如上例中对 session存储值的改变,不用ICallbackEventHandler,但是在server地图互操作的过程中, ICallbackEventHandler给我们提供了更多的便利。
继续思考:
1.本例实现了根据地图查询属性,反过来根据属性查询几何图形怎么实现呢?其实前面《
ArcGIS Server 开发系列(三)--漫游 Graphics data sources》已经讲到了,只不过需要将条件查询的信息,更改为在gridview或其他地方选择的属性信息,然后高亮显示相应的几何要素。
2.这种几何要素图形和属性信息的关联可以应用于各种不同的业务需求中,如图形和属性的同步删除、位置定位、类似结果查询等等。
3.如何改进或提升这种图形和属性的异步刷新带来的用户体验?