公司的WebGIS平台采用Silverlight技术实现,根据Silverlight技术目前的发展情况,要做到团队成员之间组件化配置开发,是有一定复杂度的。既然决定了使用Silverlight技术开发WebGIS平台,那么就只能在Silverlight技术领域寻找解决方案。

第一个映入眼帘的就是MEF,地址为http://mef.codeplex.com/,在下载了示例程序并运行之后,发现其思路极其符合我们要求的:团队中只有一个Silverlight地图框架,其它的功能都是xap包,能够加载并运行于Silverlight地图框架之上,并获得地图句柄,进行地图的相关操作。示例程序中,通过Import和Export机制,解决了不同xap包之间传递不同对象的技术难题,一下子将我们预期完成时间缩短了非常多。一切都在顺利的进行着,直到组件xap需要通过地图控件句柄操作地图控件的时候,问题来了:组件获取不到地图控件句柄。在解决了几天仍然没方法的时候,另一个解决方案出现了:Prism,地址为http://compositewpf.codeplex.com/

Prism在国内应用情况要比MEF好很多,有很多中文技术blog专门介绍了Prism,其中有许多写的非常好,可以去google。此方案实在是太强大,有兴趣的可以看中文介绍。我在了解几天之后,无奈的将它放弃,不是因为它不好,实在是用不起来。经过和MEF的对比,决定还是在MEF上下功夫,来解决当下遇到的惟一技术问题。

说来也巧,因为开发组件式Silverlight,一定要继承一个自定义类,此自定义类里面写着一些公共的方法,包括获取当前用户信息、部门信息等,然后此类继承UserControl类。最初的想法是通过框架中Export地图控件,在组件中Import地图控件,行不通之后,就仿照权限这一块,将地图控件作为一个属性加入到了自定义的类中,每个继承此类的Silverlight用户控件,自然会访问到地图控件属性,问题一下就解决了。

实现方法:
1、下载Mef_Preview_9
2、新建一个Silverlight应用程序,一个Silverlight类库。Silverlight类库中的程序就把Mef_Preview_9中示例程序Hello的Contracts拿过来就行。Silverlight应用程序引用Contracts和System.ComponentModel.Composition.dll两个dll。Silverlight应用程序中,MainPage.xaml.cs内容如下

public partial class MainPage : UserControl, IPartImportsSatisfiedNotification
{
	[Import]
	public IDeploymentCatalogService CatalogService { get; set; }
	public MainPage()
	{
		InitializeComponent();
		CompositionInitializer.SatisfyImports(this);
		this.Loaded += new RoutedEventHandler(MainPage_Loaded);
		ESRI.ArcGIS.Client.ArcGISTiledMapServiceLayer tileMapServiceLayer = new ESRI.ArcGIS.Client.ArcGISTiledMapServiceLayer();
		tileMapServiceLayer.Url = "http://192.168.0.205/ArcGIS/rest/services/ghj_map/MapServer";
		tileMapServiceLayer.ID = "基础地图";
		tileMapServiceLayer.Visible = true;
		//MapControl = m_MapControl;
		m_MapControl.Layers.Add(tileMapServiceLayer);
	}
	void MainPage_Loaded(object sender, RoutedEventArgs e)
	{
	}

	private void Menu1_Click(object sender, RoutedEventArgs e)
	{
		CatalogService.AddXap("WebGIS.UnitSearch.xap");//加载单元查询组件
	}
	[ImportMany(AllowRecomposition = true)]
	public Lazy<Contracts.MapModule, IWidgetMetadata>[] Widgets { get; set; }
	#region IPartImportsSatisfiedNotification 成员

	public void OnImportsSatisfied()
	{
		TopWidgets.Items.Clear();
		BottomWidgets.Items.Clear();

		foreach (var widget in Widgets)
		{
			widget.Value.MapControl = this.m_MapControl;//此处是关键中的关键
			if (widget.Metadata.Location == WidgetLocation.Top)
				TopWidgets.Items.Add(widget.Value);
			else if (widget.Metadata.Location == WidgetLocation.Bottom)
				BottomWidgets.Items.Add(widget.Value);
		}
	}
	#endregion

	private void Menu2_Click(object sender, RoutedEventArgs e)
	{
		CatalogService.RemoveXap("WebGIS.UnitSearch.xap");
	}
}

MainPage.xaml内容如下:

<UserControl x:Class="WebGISFramework.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
    xmlns:uriMapper="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
    xmlns:esri="clr-namespace:ESRI.ArcGIS.Client;assembly=ESRI.ArcGIS.Client"
	xmlns:esriConverters="clr-namespace:ESRI.ArcGIS.Client.ValueConverters;assembly=ESRI.ArcGIS.Client"
	xmlns:toolkit="clr-namespace:ESRI.ArcGIS.Client.Toolkit;assembly=ESRI.ArcGIS.Client.Toolkit"
	xmlns:symbols="clr-namespace:ESRI.ArcGIS.Client.Symbols;assembly=ESRI.ArcGIS.Client"
    xmlns:divtools="clr-namespace:Divelements.SilverlightTools;assembly=Divelements.SilverlightTools"
	xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:esriSymbols="clr-namespace:ESRI.ArcGIS.Client.Symbols;assembly=ESRI.ArcGIS.Client"
    d:DesignWidth="640" d:DesignHeight="480">

    <Grid x:Name="LayoutRoot">
        <Grid Canvas.Top="0" Canvas.ZIndex="9">
            <ItemsControl x:Name="TopWidgets" Height="Auto" FontSize="30"></ItemsControl>
            <Canvas>
                <Button x:Name="Menu1" Click="Menu1_Click" Width="80" Height="25" Content="地块查询"></Button>
                <Button x:Name="Menu2" Click="Menu2_Click" Width="80" Height="25" Canvas.Left="50" Content="去掉地块查询"></Button>
            </Canvas>
        </Grid>
        <Grid Canvas.Top="20" Canvas.ZIndex="8">
            <esri:Map x:Name="m_MapControl">
            </esri:Map>
            <ItemsControl x:Name="BottomWidgets" Height="Auto" FontSize="30"></ItemsControl>
        </Grid>
    </Grid>
</UserControl>

App.xaml.cs中也要做一些修改:

private void Application_Startup(object sender, StartupEventArgs e)
{
	DeploymentCatalogService.Initialize();
	RootVisual = new MainPage();
}

以上就是Silverlight应用程序的代码。
3、再新建一个Silverlight应用程序,作为组件,可以加载并运行于框架Silverlight之上。这里惟一要注意就是要继承自定义类,不能继承UserControl,否则得不到地图控件句柄。

至此,一个基于Silverlight的WebGIS就OK了。