关于C#开发GIS应用简明教程的说明
Web上的GIS,我个人觉得C#+MAPX并不是一个好的解决办法,因为有许多的GIS工具更适合于WEB环境和C#语言,只是我自己在这方面也没什么研究。所以只能对大家说抱歉了。
第一章   准备
使用C#开发GIS应用,你应该首先在系统中安装Visual Studio .Net(建议使用2003版本),然后还需要安装MapInfo公司开发的MapX 5.0版本.MapX

的4.X版本和.Net的兼容有一些问题.可能无法实现本教程介绍的某些功能.
另外,你还应该尽可能地收集所有关于.NET,C#和MapX有关的资料,教程非常实用,专门解决技术问题,而不打算列举资料,所以,最好手头上有一些

备查的资料.如果实在找不到,最起码要求能看懂MSDN.Met和MapX的帮助.
关于.Net的安装很多教程都有涉及,此处不再赘述
MapX 5.0的安装很容易,按照提示一步步就可以安装,和一般的软件安装没什么区别,安装完控件以后千万别忘了同时安装附带的地图数据.
安装好以上软件后,还需要在.Net编程环境中加入MapX 5.0控件.
在.Net编程环境中新建一个WindeosApplication(Windows应用程序)项目,然后在菜单中选择”项目/添加引用”,打开如下图的窗口,在窗口中选

择COM标签,在组件名称列表中双击MapInfo MapX V5.单击"确认"按钮,即可将Map5控件加入到.Net的工具箱中.
现在,所有的准备工作都已经完成了,为了测试安装是否正确,我们做个简单的地图放大程序.
在刚刚打开的项目中,从工具栏中选择Mapinfo MapX V5控件,把它画到项目中的Form1窗口上,如下图:
增加一个Button控件button1,将它的Text属性改为”放大”,如下图:
在设计窗口中双击按钮并编写代码如下:
private void button1_Click(object sender, System.EventArgs e)
{
axMap1.CurrentTool=MapXLib.ToolConstants.miZoomInTool;
}
编译,运行程序.如果没有什么错误的话,准备工作就已经完成了.
练习:1.     完成应用程序,在窗口中实现放大,缩小,漫游功能.
2.     熟悉MapInfo MapX中的GeoSet Manager程序.
3.     了解GIS的有关知识.(推荐到"程序员资源大联盟"网站查看相关版块的内容.


第二章入门
 1.工具(ToolConstants)
在上一章里我们试着做了一个将地图放大的应用程序.在这章里,我们将接触到更多的工具,并介绍自定义工具.
MapX为开发人员提供一系列的工具,这些工具的作用各有不同,我们通过开发一个应用程序来了解这些工具的作用,至于这些工具的具体说明,可

以在MapX的帮助文档中,通过查找"Available Standard Tools" 看到相应的解释.一些MapX的相关资料也有介绍.
在.Net编程环境中新建一个C#的WindeosApplication(Windows应用程序)项目,然后在菜单中选择”项目/添加引用”,打开如下图的窗口,在窗口

中选择COM标签,在组件名称列表中双击MapInfo MapX V5.单击"确认"按钮,将Map5控件加入到.Net的工具箱中.
接着,将MapInfo MapX V5 控件画到窗体上,再在窗体上画一个ComboBox控件comboBox1.如下图:
双击设计窗体,编写Form1_Load代码如下:
private void Form1_Load(object sender, System.EventArgs e)
{
    ArrayList ToolsList=new ArrayList();
    ToolsList.Add(MapXLib.ToolConstants.miArrowTool);
    ToolsList.Add(MapXLib.ToolConstants.miCenterTool);
    ToolsList.Add(MapXLib.ToolConstants.miLabelTool);
    ToolsList.Add(MapXLib.ToolConstants.miPanTool);
    ToolsList.Add(MapXLib.ToolConstants.miPolygonSelectTool);
    ToolsList.Add(MapXLib.ToolConstants.miRadiusSelectTool);
    ToolsList.Add(MapXLib.ToolConstants.miSymbolTool);
    ToolsList.Add(MapXLib.ToolConstants.miTextTool);
    ToolsList.Add(MapXLib.ToolConstants.miZoomInTool);
    ToolsList.Add(MapXLib.ToolConstants.miZoomOutTool);
    comboBox1.DataSource=ToolsList;
}
以上代码通过一个数组ToolsList,将MapXLib的工具加入到comboBox1中.MapX还有一些其它的工具,它们的作用是往地图上加上点,线,多边型和

圆.因为这些工具要求有操作图层,一并在介绍图层的时候介绍.关于ArrayList的用法,请参考C#的有关资料.
接着,双击comboBox1,并编写代码如下
private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
axMap1.CurrentTool=(MapXLib.ToolConstants)comboBox1.SelectedItem;
}
应注意,在C#中必须要显式地把comboBox1的选择项目SelectedItem(数据类型为object)转换为MapXLib.ToolConstants.因为ToolConstants是

MapX自己定义的一个枚举数据类型,C#不能自动完成这种枚举成员变量到object的数据转换.
编译执行程序,从comboBox1中选择不同的工具,在地图上进行操作,你可以了解到MapX工具集的强大功能.
实际上,MapX提供的工具集就好像Photoshop,AutoCAD的工具栏一样,为你提供了一些控制地图的工具.但是,在开发GIS的时候,这些工具是不能完

全满足要求的.所以,还应该学会自定义工具.
下面,通过一个在地图上测距的例子,来介绍一下如何在C#下自定义MapX工具
如前所述建立一个C#的Windows应用程序并将Mapinfo MapX V5 控件加到窗体上.并加入一个Button控件button1.将button1的Text属性改为"测

距",再在窗口中放上两个Label控件label1,label2,将它们的Text属性设置为""空字符串,如下图:
双击设计窗体,编写Form1_Load代码如下:
private void Form1_Load(object sender, System.EventArgs e)
{
axMap1.CreateCustomTool(100,MapXLib.ToolTypeConstants.miToolTypePoly,
MapXLib.CursorConstants.miCrossCursor,null,null,null);
}
在加载窗口的时候,我们定义了一个工具.它的编号是100(不要和MapX本身的工具编号重复),它的类型是一个多义线,采用十字光标.
关于此函数的用法,建议查看MapX的开发手册和相关资料.
现在我们定义了一个编号为100的工具,可以在程序中使用它了.双击设计窗体中的button1,编写它的Click事件处理代码如下:
private void button1_Click(object sender, System.EventArgs e){
axMap1.CurrentTool=(MapXLib.ToolConstants)100;
}
现在编译运行,单击button1,就可以在地图上使用这个工具了.但是还有一部分重要的代码没有完成:测距!
完成测距功能的代码在C#使用MapX开发GIS中非常具有代表性,也比较有难度.我在首次使用C#+MapX开发GIS的时候,被这个问题困扰了很久.项目

经理也来研究,过两天说有结果了,但他给出的例子根本就行不通.当然,会了就不难了.其实,也很简单.
首先,在窗口类中声明两个私有全局变量以保存测出的距离和总距,注意声明代码的位置:
public class Form1 : System.Windows.Forms.Form
{private AxMapXLib.AxMap axMap1;
 private System.Windows.Forms.Button button1;
 private System.Windows.Forms.Label label1;
 private System.Windows.Forms.Label label2;
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;
private double Dis=0,DisSum=0;
现在来编写工具的事件.注意,在MapX画多义线的时候,它触发消息的方式和一般的工具是不同的,首先,它并不是用鼠标一点击就完成了的,还可

以继续画下去,所以,不应该在MapX控件的ToolUsed事件中编写,而应该在PolyToolUsed事件中实现测距.代码如下:
private void axMap1_PolyToolUsed(object sender, AxMapXLib.CMapXEvents_PolyToolUsedEvent e){  
MapXLib.PointsClass pts=new MapXLib.PointsClass();  
switch(e.flags)
{
case (int)MapXLib.ToolFlagConstants.miToolInProgress:
pts=(MapXLib.PointsClass)e.points;
Dis=axMap1.Distance(pts._Item(pts.Count-1).X,
pts._Item(pts.Count-1).Y,
pts._Item(pts.Count).X,
pts._Item(pts.Count).Y);
DisSum+=Dis;
break;
default:
Dis=0;
DisSum=0;
break;
}
label1.Text="距离:"+Dis.ToString("#.00");
label2.Text="总距"+DisSum.ToString("#.00");
}
 这段代码虽短.但要注意的地方很多.

首先,定义一个MapXLib.PointsClass类型的变量pts,注意,是PointsClass,不是PointClass.前者是点集,后者是点.为什么要增加那么一个变量

呢?因为MapX的PolyToolUsed事件的返回参数e的成员points不是MapXLib.PointsClass类型,而是object.类型.所以需要这么一个变量来转换它,

当然,你也可以在程序使用pts的地方直接使用(MapXLib.PointsClass)e.points,但那样一来程序就比较难懂了;

其次,要判断事件的标识e.flags的值,它指出工具当前的状态,是刚开始画多义线呢,还是正在画多义线,或者已经结束了,或者结束退出.我们只

要在画的时候测距就可以了.其它时候将距离和总距都设置为0;

接着,还要注意的是e.points的点数据保存方式,e.points首先是一个object,当在画多义线的时候,它被初试化为一个MapXLib.PointsClass的变

量,并以二维数组的方式保存点集.这个数组是从1开始的,而不是从0开始的.它保存了多义线上每个转折点的坐标,鼠标每点一下,就增加一个新

的数据到点集,我们计算最后一条直线长度,应该从这个数组的末尾往前取.计算好距离以后再加入到总距中.许多测距的程序例子都要做一个循

环,其实是不必要的.

最后,请注意数字转换到字符串的格式问题.在这个例子中我们保留两位小数.

补充一点,因为没有设置地图的地理坐标系统,所以测出来的距离单位是英里,如果要改为公里,把MapX控件的MapUnit属性改为miUnitKilometer

就可以了.要提高测量精度,除了可以通过转换格式的时候增加小数位,还应该注意到地图的测绘精度.否则,再多的小数位也是没有意义的.
练习

1.建立一个应用程序,加入MapX控件和一个ComboBox控件,两个:Label控件,在ComboBox中加入第一个例子中的所有工具和测距工具,在ComboBox

中选择测距工具时实现测距功能.

2.将测距工具的光标改成箭头光标.并以米为单位显示测距数据.

2.图层和图元(Layers and Features)

有关图层和图元的概念,请参照相关资料.

图层的应用分为几个方面,我们分别加以介绍.

1)图层的创建:

创建永久图层:

在C#中,使用以下方法创建永久图层:

MapXLib.Layer lyr;
lyr=axMap1.Layers.CreateLayer("MyLayer","D:\\MapTest\\MyLayer.Tab",0,32,axMap1.DisplayCoordSys);
当执行这两句程序时,在指定的路径生成了一系列文件.它们是:
MyLayer.Dat:图层的数据文件,它保存的是图层的数据库数据;
MyLayer.ID:图层数据的唯一的,自动生成的编码,用以区分不同的图元;
MyLayer.IND:图层数据的索引文件,以实现图层上图元的快速查找;
MyLaer.MAP:图层上图元的图形数据;
MyLaer.TAB:这是一个文本文件,它的作用是将图层的有关信息保存起来,供GST地图文件或其它程序调用图层.
用记事本打开MyLayer.TAB文件,看到如下内容:
!table
!version 450
!charset WindowsSimpChinese
Definition Table
Description "MyLayer"
Type Native charset "WindowsSimpChinese"
Fields 1
GEONAME char (32) Index 1 ;
第一行总是"!table",说明这是一个图层表的文件;
第二行指出图层文件的版本号,MapInfo MapX 5.0生成的图层版本号是450;
第三行指出生成图层的操作系统;
接着是对图层的定义段:
首先指出图层的描述,就是我们上面程序代码中的"Mylayer".
接着说明了字符集类型是简体中文;
然后指出表格中只有一个字段,这个字段的名称是"GEONAME",是长度为32的字符类型字段,在表中的列索引为1.
2)添加现有的图层:
添加现有图层的方法和一般的程序语言没有很大的区别,我们在当前图层上添加刚才创建的永久图层,程序代码如下:
MapXLib.LayerInfo li;
li=new MapXLib.LayerInfoClass();
li.Type=MapXLib.LayerInfoTypeConstants.miLayerInfoTypeTab;
li.AddParameter("FileSpec","D:\\MapTest\\MyLayer.tab");
li.AddParameter("Visible",false);
li.AddParameter("AutoCreateDataset",true);
li.AddParameter("DatasetName","MyLayer");
axMap1.Layers.Add(li,0);
axMap1.Layers.LayersDlg("","");
axMap1.SaveMapAsGeoset("测试","D:\\MapTest\\MyMap.GST");
在程序的最后,我们显示了图层信息对话框,以观察图层是否已经添加到当前的地图中.可以看到,地图中添加了我们创建的图层"MyLayer".放在

第0层.
然后,将地图保存在一个MyMap.GST的地图文件中,这个地图的标题是"测试".
3)移除图层:
好,接着我们上面做的工作,将工程的axMap1的GeoSet属性设置为刚刚生成的地图文件: "D:\MapTest\MyMap.GST".现在我们将MyLayer从地图中

移除.
添加一个按钮,在按钮的Click事件中编写如下代码:
axMap1.Layers.LayersDlg("","");
axMap1.Layers.Remove(1);
axMap1.Layers.LayersDlg("","");
我们使用两次显示图层对话框的方式查看程序的效果,应该注意的是在Remove第0层的时候使用的是Remove(1),如果不清楚图层的位置,就要作一

个循环,将图层的位置取出来再移除,如下:
int lyrind=0;
axMap1.Layers.LayersDlg("","");
for( int i=1;i<axMap1.Layers.Count;i++)
{
 if (axMap1.Layers[i]._Name.Trim()=="MyLayer")
    {
       lyrind=i;
       break;
    }
}
axMap1.Layers.Remove(lyrind);
axMap1.Layers.LayersDlg("","");
移除操作只在内存中进行,也就是说,程序并不删除任何文件,也没有将图层真正地从地图集合中去掉,当程序重新启动的时候,MyLayer图层仍然

在地图中.
3)移除所有图层:
使用axMap1.Layers.RemoveAll();就可以移除所有图层,用法和Remove相似.
4)图层定位:
和其它编程语言一样,使用axMap1.Move(1,2)函数就可以将图层的位置改变.
5)创建临时图层
临时图层和永久图层不同,它只存放在内存中,当关闭程序以后该图层将不存在.
在这里我们将使用LayerInfo对象来创建临时图层,这和传统的MAPX程序相近,但是引入了C#编程的一些特色:
MapXLib.LayerInfoClass li=new MapXLib.LayerInfoClass();
MapXLib.Features ftrs=null;
MapXLib.FieldsClass flds=new MapXLib.FieldsClass();
MapXLib.Fields Myflds=null;
MapXLib.Dataset dts=null;
flds.Add("State","State_Name",
MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeString);
dts=axMap1.DataSets.Add(MapXLib.DatasetTypeConstants.miDataSetLayer,
axMap1.Layers._Item(1),"MyLayer",0,0,0,flds,false);
Myflds=dts.Fields;
ftrs=axMap1.Layers._Item("USA").Selection.Clone();
li.Type=MapXLib.LayerInfoTypeConstants.miLayerInfoTypeTemp;
li.AddParameter("Name","USA Temp Layer");
li.AddParameter("Fields",Myflds);
li.AddParameter("Features",ftrs);
axMap1.Layers.Add(li,1);
axMap1.Layers.LayersDlg("","");
这段程序有两个关键的地方:
一个是在C#中DataSets.Add的用法,在许多编程语言中,都可以使用空的参数或者干脆不用参数来调用这个函数,但是在C#中是不行的,必须8个参

数全部指定.而且,还应该事先初始化Fields参数.这个函数的使用涉及到许多方面的知识,包括对MAPX相关概念的理解和C#编程的认识,是一个很

重要,也比较难掌握的技术.后面的章节中我们还会作进一步的探讨.
另一个是li.AddParameter和li.Type的配合使用问题,这在许多的MAPX书籍中都有论述,此处不再重复.
以上程序最好能自己多琢磨琢磨.才能更好地掌握C#开发GIS的要领.
6)缩放图层:
所谓的缩放图层,并不是指将单个图层缩放.而是指定图层的可见范围比例,例如,设置一个图层在缩小显示大于5英里的时候隐藏.小于5英里的时

候显示.和其它编程语言一样,只要设置Layer的ZoomMin和ZoomMax就可以了.
7)显示整个图层:
这里要提及的一个技巧是在C#下面怎样显示整个图层.众所周知,在VB下面只要:
Map1.Bounds = Map1.Layers("USA").Bounds
一句程序就可以轻松实现.但是要是在C#中这样写的话,百分百会出错.其实,这里有一个小小的技巧,聪明的你一定能看出来:
axMap1.CtlBounds=axMap1.Layers._Item("USA").Bounds;
8)在图层上绘制永久图形:
我们在介绍工具的时候,有一些工具没有介绍,这些工具其实是用来在图层上创建永久图形对象的(图元).当在图层上绘制了图形以后,这些图形

将以数据记录的形式保存在图层表中,也就是创建了一个图元.如果不想保存图元,可以在临时图层里绘制.
axMap1.Layers._Item("USA Temp Layer").Editable=true;
axMap1.Layers.InsertionLayer=axMap1.Layers._Item("USA Temp Layer");
axMap1.CurrentTool=MapXLib.ToolConstants.miAddLineTool;
上面的程序使用了画线的工具,在地图上拖动鼠标就可以在临时图层上画线了.这些工具使用的前提是必须指定axMap1的插入图层

(InsertionLayer).才能在图层表中插入数据.
关于图层的关键技术就介绍到这里,掌握了这些技术以后,在作进一步的研究时,例如动画图层和绘制图层的开发,遇到的困难应该不大.
下面我们介绍C#对MAPX图元的编程技术.
9)在图层上创建图元:
根据MAP Info提供的MapX 5.0开发手册,创建图元有两种方法,用两段代码说明这两种代码在C#的实现方法:
第一种实现方法:直接使用Feature类创建图元
MapXLib.Style sty=new MapXLib.StyleClass();
MapXLib.Feature ftr=new MapXLib.FeatureClass();
ftr.Attach(axMap1.GetOcx());
ftr.Type=MapXLib.FeatureTypeConstants.miFeatureTypeText;
sty.TextFontColor=255;
sty.TextFont.Size=12;
ftr.Style=sty;
ftr.Caption="New Feature";
ftr.Point.Set(axMap1.CenterX,axMap1.CenterY);
axMap1.Layers._Item("US Top 20 Cities").Style=sty;
ftr=axMap1.Layers._Item("US Top 20 Cities").AddFeature(ftr,new MapXLib.RowValuesClass());
ftr.Update(ftr,new MapXLib.RowValuesClass());
有几个要注意的地方:
ftr.Attach(axMap1.GetOcx());
如果在VB6下,这句程序应该是这样的:
ftr.Attach Map1
从这里可以看到在C#中因为严格的类型管理所带来的一些变化.如果不进行GetOcx()的转换,即使在VB.Net下也是错误的.
ftr=axMap1.Layers._Item("US Top 20 Cities").AddFeature(ftr,new MapXLib.RowValuesClass());
我们在这个程序中加入了一行空的数据,这也就代表着我们加入的图元不包含任何数据信息.这是为了在介绍图元的时候方便大家掌握,并不意味

着这样增加图元就不能将数据保存进去,实际上,通过设置一个新的RowValues变量,是可以将数据信息保存到图元中的.我们将在后面的章节中介

绍如何将数据保存在图元中.
ftr.Update(ftr,new MapXLib.RowValuesClass());
这句程序的作用是将创建的图元保存到图层表中,这种保存是永久保存的.除非是在临时图层上增加的图元,否则下次打开地图文件的时候,将看

到创建的图元仍然保留在地图上.因此,在对图元操作之前请备份好地图文件,避免不必要的损失.
另外,关于图元的类型(Type)和风格(Style),应该和其所在的图层相对应,比如上面程序中关于Type和Style的设置,都是和"US Top 20 Cities"

图层的类型对应的.有关类型和风格的设置,请查阅相关手册.
第二种实现方法:使用FeatureFactory创建图元
MapXLib.Feature ftr=new MapXLib.FeatureClass();
MapXLib.Point p=new MapXLib.PointClass();
p.Set(axMap1.CenterX,axMap1.CenterY);
ftr=axMap1.Layers._Item("US Top 20 Cities").AddFeature(axMap1.FeatureFactory.CreateText
(p,"New Feature",MapXLib.PositionConstants.miPositionCC,
new MapXLib.StyleClass()),
new MapXLib.RowValuesClass());
这段代码和上面和第一种实现方法类似,我们在这里不再设置图元的Type和Style.
FeatureFactory类似于设计模式中的Factory模式,从它可以产生各种类型的图元,这个过程相当于设置了图元的Type.
10)查找图元:
查找图元是很简单的,可以用下边的代码实现:
MapXLib.FindFeature fRes=null;
fRes=axMap1.Layers._Item("US Top 20 Cities").Find.Search("New York","");
axMap1.CenterX=fRes.CenterX;
axMap1.CenterY=fRes.CenterY;
上面这段代码将找到的图元放置在地图中央.这里要注意的是不能使用
MapXLib.FindFeature fRes=new MapXLib.FindFeatureClass();
来创建FindFeature对象,否则将会出错:
"带有 CLSID {436052C3-43E3-11D0-83EB-00AA00BD34FC}的COM对象无效或未注册。"这是MapX 5.0的一个Bug.要避开它,只要在创建对象的时候

赋null值就可以了.
下面我们对程序作一些改进,来避免找不到图元的时候出错,并在找到图元以后选择该图元:
MapXLib.FindFeature fRes=null;
fRes=axMap1.Layers._Item("US Top 20 Cities").Find.Search("New York","");
if (fRes.FindRC % 10 ==1)
{
    axMap1.CenterX=fRes.CenterX;
    axMap1.CenterY=fRes.CenterY;
    axMap1.Layers._Item("US Top 20 Cities").Selection.Add(fRes);
}
11)图元的修改:
图元的增加,修改和删除都是非事务性的,也就是说,所作的修改都永久性地对图层表数据产生影响.这可以使用户对多个图元进行操作后一次更

新地图,但是在更新之前并不能看到更新后的效果.我们在使用第一种方法创建图元的时候,在最后更新:
ftr.Update(ftr,new MapXLib.RowValuesClass());
但是在FeatureFacory创建图元的时候是不用Update的,因为FeatureFactory的相关方法中已经包含了Update.根据MapX 5.0的开发手册介绍,修

改了某个图元并更新时应使用Feature.Update方法,当使用其它图元取代某个图元,应使用Layers.UpdateFeature方法.
12)图元的删除:

和其它编程语言一样,删除图元使用DeleteFeature方法,有关介绍请参看MapX的联机帮助和开发手册.

练习:

1.结合以上例子,并参考相关资料,在地图上建立一个临时图层,并在该图层上加入图标类型(.bmp文件)的图元.
2.自定义一个工具,使用该工具删除图元.
注意: 在对图元操作之前请备份好地图文件,避免不必要的损失.

第三章数据处理

在介绍数据处理之前,我们先在MapInfo中生成自己的地图,在本章中将全部使用自己生成的地图.这是因为MapX本身提供的地图和实际应用的地

图在数据结构上有较大的差异.不适合用来介绍MapX的数据处理.
首先安装好MapInfo,最好能使用7.0以上的版本.以下操作在MapInfo Professional 7.0版本下完成.
打开MapInfo,界面如下图:
单击工具栏最左边的  按钮,新建一个图层数据表.如下图在"New Table"窗口中按照上图选择后单击"Create..."按钮,建立一个新的数据图

层.MapInfo会弹出一个窗口,如下图:
在这个窗口中设置好数据表的字段属性.单击"Add Field"按钮将字段添加到表中,我们在这里将添加4个字段:ID,名称,月供年限和水电费.设置

好的字段如下图:
设置好以后,单击"Create..."按钮,MapInfo将询问建立图层表的文件路径.将你的图层表文件名改为"HouseLayer.Tab",存放到合适的路径下.例

如:" E:\Test\DataMap\".
保存完毕以后,得到一个空白的图层表文件,MapInfo打开了这个文件显示如下:
这个图层表上现在还没有任何图形,我们在图层上增加两个图元,并把数据添加上去.
在Drawing工具条上选择画矩形的工具,在HouseLayer Map上画一个矩形,然后使用拷贝粘贴的功能将其复制为另一个矩形.
选择图元后,对两个图元稍做修饰.如下图:
现在,简单的作图就完成了.
接着输入和图形相关的数据.选择如下图指示的按钮,在图层上点选图元,打开输入数据的窗口.
在输入数据的窗口中输入以下两条数据:
ID    名称 月供年限 水电费

0001  A栋   25        18.77
0002  B栋   30        25.12
保存图层.MapInfo的画图工作就完成了.
你还需要在MapX 5.0自带的工具Geoset Manager里把刚刚画好的图层放到一个地图文件(.GST格式)里,才能被MapX调用,我们把这个文件命名为"

小区.GST".
 好,准备工作就绪,现在我们开始介绍MapX的数据处理.
1.数据读取 (Data Reading)
先对程序的功能作一些说明:把小区地图放在MapX控件上,并且在窗口的左边放置一个ListView,在这个ListView中显示所有的HouseLayer图层表

中的图元数据.
和以往不同的是,我们的程序界面做了一些改动,以跟踪数据的变化.
下图是窗口设计界面:
可以看到,我们在窗口左边放置了一个ListView控件,并在ListView中增加了4列(Columns)来显示数据.同时,把MapX控件的地图换成了我们自己

制作的"小区"地图.
在button1的单击事件button1_Click中编写如下代码:
MapXLib.Fields flds=new MapXLib.FieldsClass();
MapXLib.Layer lyr=axMap1.Layers._Item("HouseLayer");
MapXLib.Dataset dts=null;
flds.Add("ID","ID",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeString);
flds.Add("名称","名称",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeString);
flds.Add("月供年限","月供年限",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeInteger);
flds.Add("水电费","水电费",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeFloat);
dts=axMap1.DataSets.Add(MapXLib.DatasetTypeConstants.miDataSetLayer,lyr,lyr.Name,0,0,0,flds,false);
listView1.Items.Clear();
for(int i=1;i<=dts.RowCount;i++)
{
listView1.Items.Add( new ListViewItem(new string[]{dts[i,1].ToString(),
dts[i,2].ToString(),
dts[i,3].ToString(),
dts[i,4].ToString()}));
}
axMap1.DataSets.RemoveAll();
代码依旧很短,是对以前所介绍知识的进一步深化.这里要注意的是对dts的应用.运行程序,可以看到程序把所有的图元数据都正确地读取出来了

.
在程序末尾,我们将MapX控件中的DataSets全部移除,这是为了以后再使用DataSets方便.
2.添加数据(Data Adding)
在图层表上添加数据实际上就是添加图元,只不过这个图元是带有数据信息的.
仍然使用读取数据的例子,我们在窗口上增加一个按钮button2,编写它的单击事件代码button2_Click如下:
MapXLib.Feature ftr=new MapXLib.FeatureClass();
MapXLib.Points pts=new MapXLib.PointsClass();
MapXLib.Point pt=new MapXLib.PointClass();
MapXLib.RowValues rvs=new MapXLib.RowValuesClass();
MapXLib.Fields flds=new MapXLib.FieldsClass();
MapXLib.Layer lyr=axMap1.Layers._Item("HouseLayer");
MapXLib.Dataset dts=null;
flds.Add("ID","ID",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeString);
flds.Add("名称","名称",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeString);
flds.Add("月供年限","月供年限",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeInteger);
flds.Add("水电费","水电费",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeFloat);
dts=axMap1.DataSets.Add(MapXLib.DatasetTypeConstants.miDataSetLayer,lyr,lyr.Name,0,0,0,flds,false);
rvs=dts.get_RowValues(0);
rvs._Item("ID").Value="0003";
rvs._Item("名称").Value="C栋";
rvs._Item("月供年限").Value=20;
rvs._Item("水电费").Value=21.73;
ftr.Attach(axMap1.GetOcx());
ftr.Style=axMap1.Layers._Item("HouseLayer").Style;
ftr.Style.RegionPattern=MapXLib.FillPatternConstants.miPatternSolid;
ftr.Style.RegionColor=16711680;
ftr.Type=MapXLib.FeatureTypeConstants.miFeatureTypeRegion;
pt.Set(1,1);
pts.Add(pt,1);
pt.Set(2,1);
pts.Add(pt,2);
pt.Set(2,2);
pts.Add(pt,3);
pt.Set(1,2);
pts.Add(pt,4);
ftr=axMap1.FeatureFactory.CreateRegion(pts,ftr.Style);
ftr=axMap1.Layers._Item("HouseLayer").AddFeature(ftr,rvs);
ftr.Update(ftr,rvs);
axMap1.DataSets.RemoveAll();
在上面的程序中我们实现了将一个新的图元加入到图层表中,这个图元的数据信息如下表:
ID    名称  月供年限  水电费
0003  C栋     20      21.73
应该要注意的是我们首先还是使用了DataSets.Add将数据读出来,再使用get_RowValues得到行数据结构,然后才是对行数据的赋值.
运行程序,单击button1,可以看到左边的ListView中只有两条记录,再单击button2,看到图层上增加了一个蓝色的矩形图元,这就是我们增加的带

数据的图元,再单击button1,可以看到新增图元的数据信息出现在左边的ListView中.
3.修改数据(Data Modifying)
现在,要在窗口中添加另一个按钮button3了.这个按钮要实现的功能是将"A栋"的名称改成"A1栋".也就是说,我们要对A栋的数据进行修改.这个

按钮的代码如下:
MapXLib.FindFeature ftrRes=null;
MapXLib.Points pts=new MapXLib.PointsClass();
MapXLib.Point pt=new MapXLib.PointClass();
MapXLib.RowValues rvs=new MapXLib.RowValuesClass();
MapXLib.Fields flds=new MapXLib.FieldsClass();
MapXLib.Layer lyr=axMap1.Layers._Item("HouseLayer");
MapXLib.Dataset dts=null;
ftrRes=axMap1.Layers._Item("HouseLayer").Find.Search("0001","");
if (ftrRes.FindRC % 10==1)
{
flds.Add("ID","ID",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeString);
flds.Add("名称","名称",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeString);
flds.Add("月供年限","月供年限",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeInteger);
flds.Add("水电费","水电费",MapXLib.AggregationFunctionConstants.miAggregationSum,
MapXLib.FieldTypeConstants.miTypeFloat);
dts=axMap1.DataSets.Add(MapXLib.DatasetTypeConstants.miDataSetLayer,lyr,lyr.Name,0,0,0,flds,false);for (int

i=0;i<dts.RowCount;i++)
{
if (dts.get_Value(i,"ID").ToString().Trim()=="0001")
{
 rvs=dts.get_RowValues(i);
 rvs._Item("名称").Value="A1栋";
 ftrRes.Update(ftrRes,rvs);
  break;
 }
 }
axMap1.DataSets.RemoveAll();
}

可以看到,我们使用了前面章节所介绍的查找图元的技术.然而在给行数据赋值的时候仍然不得不在数据集中进行循环,以取得其它行数据,实际

上,如果通过控件(例如DataGrid或ListView)取到了图元的数据,是不必进行这样的循环的,直接对RowValue的所有字段都进行赋值就可以了.
4.删除数据(Data Deletion)
删除数据其实就是删除图元,只要找到图元,直接删除就可以了.但是要注意的是,因为图元包含了数据,所以在删除数据以后还要紧缩表.好在紧

缩表也十分简单.
仍然使用我们前面的例子,加一个button4,在它的单击事件中编写代码如下:
MapXLib.FindFeature ftrRes=null;
ftrRes=axMap1.Layers._Item("HouseLayer").Find.Search("0003","");
if (ftrRes.FindRC % 10 ==1)
{
axMap1.Layers._Item("HouseLayer").DeleteFeature(ftrRes);
}
//紧缩表
axMap1.Layers._Item("HouseLayer").Pack(MapXLib.LayerPackConstant.miPackAll);
如果在删除图元以后没有紧缩表的话,单击button1,将会看到ListView中多了一条空白的数据.
本章讲述的数据处理技术重在活学活用.关于数据处理还有一些重要的内容,例如专题图等技术,将在后面更为深入的章节中讲述.
练习:
1.在DataGrid中读取HouseLayer的所有图元数据,并将DataGrid内修改后的数据更新到图层表中.
2.使用自定义工具增加和删除带数据的图元.

在C#中怎样获取打印机的当前状态 ?
要获得打印机的状态,应该定义一个联合.
enum PrinterStatus
{
 其他状态= 1,
 未知,
 空闲,
 正在打印,
 预热,
 停止打印,
 打印中,
 离线
}

其中的"正在打印"和"打印中"是同一个意思,至于为什么会是这样,目前还不知道.

在程序中添加System.Management的引用:using System.Management;

然后编写获取打印机状态的函数如下:

/// <summary>
  /// 获取打印机的当前状态
  /// </summary>
  /// <param name="PrinterDevice">打印机设备名称</param>
  /// <returns>打印机状态</returns>
  private PrinterStatus GetPrinterStat(string PrinterDevice)
  {
   PrinterStatus ret=0;
   string path=@"win32_printer.DeviceId='"+PrinterDevice+"'";
   ManagementObject printer=new ManagementObject(path);
   printer.Get();
   ret=(PrinterStatus)Convert.ToInt32(printer.Properties["PrinterStatus"].Value);
   return ret;
  }

调用示例:

MessageBox.Show(GetPrinterStat("\\\\192.168.0.88\\Samsung SCX-4x16 Series").ToString());


实现两个ComboBox互动的代码
private const string m_strConnection=@"Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Registry Path=;Jet OLEDB:Database

Locking Mode=1;Jet OLEDB:Database Password=;Data Source=""F:\源码\Study\TestTwoCombo\bin\Debug\data.mdb"";Password=;Jet

OLEDB:Engine Type=5;Jet OLEDB:Global Bulk Transactions=1;Provider=""Microsoft.Jet.OLEDB.4.0"";Jet OLEDB:System database=;Jet

OLEDB:SFP=False;Extended Properties=;Mode=Share Deny None;Jet OLEDB:New Database Password=;Jet OLEDB:Create System

Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;Jet OLEDB:Compact Without Replica Repair=False;User ID=Admin;Jet

OLEDB:Encrypt Database=False";
private OleDbConnection cn=new OleDbConnection();
private OleDbDataAdapter da;
private DataTable dtbDept=new DataTable();
private DataTable dtbEmp=new DataTable();
private void Form1_Load(object sender, System.EventArgs e)
{
 m_ConnectToDB();
 da=new OleDbDataAdapter(@"SELECT * FROM 部门",cn);
 da.Fill(dtbDept);
 da.SelectCommand.CommandText=@"SELECT * FROM 员工";
 da.Fill(dtbEmp);
 m_CloseConnection();

 m_LoadDept();
}

/// <summary>
/// 连接数据库
/// </summary>
private void m_ConnectToDB()
{
 cn.ConnectionString=m_strConnection;
 if(cn.State==ConnectionState.Closed)
 {
  cn.Open();
 }
}

/// <summary>
/// 关闭数据库连接
/// </summary>
private void m_CloseConnection()
{
 if(cn.State==ConnectionState.Open)
 {
  cn.Close();
 }
}

/// <summary>
/// 加载部门信息
/// </summary>
private void m_LoadDept()
{
 if(dtbDept.Rows.Count>0)
 {
  cmbDept.DataSource=dtbDept;
  cmbDept.DisplayMember="部门名称";
 }
}

private void m_LoadEmp()
{
 if(cmbDept.SelectedIndex>=0)
 {
  dtbEmp.DefaultView.RowFilter="部门ID='"+dtbDept.Rows[cmbDept.SelectedIndex]["部门ID"].ToString().Trim()+"'";
  cmbEmp.DataSource=dtbEmp.DefaultView;
  cmbEmp.DisplayMember="姓名";
 }
}

private void cmbDept_SelectedIndexChanged(object sender, System.EventArgs e)
{
 m_LoadEmp();
}

posted on 2007-11-12 03:02  夜帝  阅读(2061)  评论(3编辑  收藏  举报