最近在搞应用于Windows平板电脑的系统开发,需要开发适用于平板电脑的地图控制控件取代ArcEngine自带的AxTOCContrl。
搞控件开发太费脑啦,需要注意的逻辑关系很复杂 都晕倒啦!
控件实现:地图图层控制(单个控制、图层集控制、整体控制)、图层收缩、控件图层节点拖拽,基本可以替代AE中的AxTocControl。
本控件开发周期为1.5天,解决主要技术问题有节点的动态加载、收缩与展开高度的自动计算、节点控制图层的关闭与打开、节点的拖拽功能。
控件XUTocControl实现,只有三个文件,如图:
图层节点XUBarNode关键代码:
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
using System.Runtime.InteropServices;
namespace GC_Controls
{
//定义节点
public partial class XUBarNode : UserControl
{
//绑定的图层名
private string _LayerName;
//是否关闭
private bool _IsClose = false;
//地图窗体
private AxMapControl mapCtrl;
#region 定义属性
//父节点容器
public XUNaviBar ParentBarNode;
public string Text
{
get {
return this.NodeLabel.Text;
}
set
{
this.NodeLabel.Text = value;
}
}
public Button Close
{
get {
return this.btnClose;
}
}
//节点字体
public Font TextFont
{
set { this.NodeLabel.Font = value; }
}
//图层值
public string Value
{
get {
return this._LayerName;
}
set {
this._LayerName = value;
}
}
#endregion
#region 构造函数
public XUBarNode(XUNaviBar ParentBar,string strText)
{
InitializeComponent();
ParentBarNode = ParentBar;
this.Text = strText;
mapCtrl=((AxMapControl)ParentBar.Tag);
}
public XUBarNode(XUNaviBar ParentBar, string strText,string strValue)
:this(ParentBar,strText)
{
this.Value = strValue;
}
public XUBarNode(XUNaviBar ParentBar, string strText, string strValue,object tag)
: this(ParentBar,strText,strValue)
{
this.Tag = tag;
}
#endregion
/// <summary>
/// 打开关闭图层
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnClose_Click(object sender, EventArgs e)
{
CloseLayer(_IsClose);
mapCtrl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);
_IsClose = !_IsClose;
}
/// <summary>
/// 从外部控制:打开或关闭图层
/// </summary>
/// <param name="bClose"></param>
public void CloseLayer(bool bClose)
{
if (!bClose)
{
this.btnClose.Text = "打开";
//TODO :关闭图层
if (this.Tag != null)
{
ILayer2 tmpLayer = (ILayer2)this.Tag;
tmpLayer.Visible = bClose;
}
}
else
{
this.btnClose.Text = "关闭";
//TODO :关闭图层
if (this.Tag != null)
{
ILayer2 tmpLayer = (ILayer2)this.Tag;
tmpLayer.Visible = bClose;
}
}
}
/******************** 下面是实现拖拽的关键代码部分*********************/
private void XUBarNode_MouseDown(object sender, MouseEventArgs e)
{
//设置拖拽前的边框类型
this.BorderStyle = BorderStyle.Fixed3D;
}
/// <summary>
/// 获取进入控件里的拖拽进行为类型
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void XUBarNode_DragOver(object sender, DragEventArgs e)
{
if (e.AllowedEffect == DragDropEffects.Move)
{
e.Effect = DragDropEffects.Move;
}
}
/// <summary>
/// 实现菜单拖拽
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void XUBarNode_DragDrop(object sender, DragEventArgs e)
{
XUBarNode item = (XUBarNode)e.Data.GetData(typeof(XUBarNode));
item.Dock = DockStyle.Bottom;
item.BorderStyle = BorderStyle.FixedSingle;
//获取原来的父节点XUNaviBar,更新节点列表和长度
XUNaviBar parentBar =item.ParentBarNode;
parentBar.NodeList.Remove(item);
//重新计算高度
parentBar.Height=parentBar.BarHeight;
//获取当前父节点,并更新列表和长度
XUNaviBar newPBar = this.ParentBarNode;
int insertPosition=newPBar.NodeList.IndexOf(this);
newPBar.NodeList.Insert(insertPosition, item);
//重新计算高度
newPBar.Height = newPBar.BarHeight;
//加入控件(TODO:这里当前是将节点插件放在最后端,需要更改)
this.Parent.Controls.Add(item);
}
private void XUBarNode_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button==MouseButtons.Left)
{
//开始对菜单节点项实施拖拽
this.DoDragDrop(sender, DragDropEffects.Move);
}
}
}
}
父节点XUNaviBar 代码:
{
public partial class XUNaviBar : UserControl
{
#region 定义共有变量
private int _nodeHeight = 30;
private bool IsExpand = true;
//地图窗体
private AxMapControl mapCtrl;
private XUNaviBar _ParentBar;
public ILayer2 CompositeLayer;
#endregion
#region 定义属性
public ArrayList NodeList=new ArrayList();
public ArrayList BarList = new ArrayList();
public object Tag
{
get
{
return mapCtrl;
}
set
{
mapCtrl = (AxMapControl)value;
}
}
public string Text
{
get
{
return this.BarText.Text;
}
set
{
this.BarText.Text = value;
}
}
public int NodeHeight
{
get {
return this._nodeHeight;
}
set {
this._nodeHeight = value;
}
}
public int BarHeight
{
get
{
if (!IsExpand)
{
this.Height = this.topPanel.Height;
}
else
{
this.Height = this.topPanel.Height + this.NodeList.Count * this._nodeHeight+ GetAllBarHeight();
}
return this.Height;
}
set
{
this.Height += value;
}
}
#endregion
public XUNaviBar()
{
InitializeComponent();
}
public XUNaviBar(XUNaviBar parentBar)
{
//设置父节点
this._ParentBar = parentBar;
InitializeComponent();
}
public XUNaviBar(XUNaviBar parentBar,string layerName):this(parentBar)
{
this.BarText.Text = layerName;
}
public XUNaviBar(XUNaviBar parentBar, ILayer2 layer):this(parentBar)
{
this.BarText.Text =layer.Name;
this.CompositeLayer = layer;
}
/// <summary>
/// 添加树形节点BarNode
/// </summary>
/// <param name="tmp"></param>
public void AddBarNode(XUBarNode tmp)
{
//设置高度
tmp.Height = this._nodeHeight;
this.NodeList.Add(tmp);
//设置图层节点的高度
this.Height = this.topPanel.Height + this.NodeList.Count * tmp.Height+ GetAllBarHeight();
this.contentPanel.Controls.Add(tmp);
this.contentPanel.Controls.SetChildIndex(tmp, 0);
tmp.Dock = DockStyle.Top;
if (!this.btnClose.Enabled)
if (this.NodeList.Count > 0)
{
this.btnClose.Enabled = true;
this.btnExpand.Enabled = true;
}
}
/// <summary>
/// 添加NaviBar大节点
/// </summary>
/// <param name="navBar"></param>
public void AddBar(XUNaviBar navBar)
{
this.BarList.Add(navBar);
//设置图层节点的高度
this.Height = this.topPanel.Height +GetAllBarHeight() + this.NodeList.Count * _nodeHeight;
this.contentPanel.Controls.Add(navBar);
this.contentPanel.Controls.SetChildIndex(navBar, 0);
navBar.Dock = DockStyle.Top;
if(!this.btnClose.Enabled&&this.BarList.Count > 0)
{
this.btnClose.Enabled = true;
this.btnExpand.Enabled = true;
}
}
/// <summary>
/// 展开NaviBar下节点
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnExpand_Click(object sender, EventArgs e)
{
if (IsExpand)
{
this.Height = this.topPanel.Height;
this.btnExpand.Text = "展开";
}
else
{
if (this.BarList.Count > 0)
{
this.Height = this.topPanel.Height + this.NodeList.Count * this._nodeHeight + GetAllBarHeight();
}
else
{
this.Height = this.topPanel.Height + this.NodeList.Count * this._nodeHeight;
}
this.btnExpand.Text = "收缩";
}
if (_ParentBar != null)
{
//补丁,展开父节点时,需执行首节点的该操作
_ParentBar.Height = _ParentBar.BarHeight;
this._ParentBar.Parent.Height = _ParentBar.Height;
}
IsExpand = !IsExpand;
}
/// <summary>
/// 获得当前节点高度
/// </summary>
/// <returns></returns>
private int GetAllBarHeight()
{
int barHeight=0;
foreach (XUNaviBar tmpBar in BarList)
{
barHeight += tmpBar.Height;
}
return barHeight;
}
/// <summary>
/// 打开或关闭图层
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnClose_Click(object sender, EventArgs e)
{
//图层集合
if (this.CompositeLayer != null)
{
this.CompositeLayer.Visible = !this.CompositeLayer.Visible;
mapCtrl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);
this.btnClose.Text = this.CompositeLayer.Visible ? "关闭" : "打开";
foreach (XUBarNode tmpNode in this.NodeList)
{
tmpNode.CloseLayer(this.CompositeLayer.Visible);
}
}
else // 顶级图层
{
if(this.btnClose.Text =="关闭" )
{
CloseLayers(false);
this.btnClose.Text = "打开";
}
else
{
CloseLayers(true);
this.btnClose.Text = "关闭";
}
}
}
//关闭图层
private void CloseLayers(bool bClose)
{
//存在图层集合
if(this.BarList.Count>0)
foreach(XUNaviBar tmpBar in BarList)
{
tmpBar.CompositeLayer.Visible = bClose;
tmpBar.btnClose.Text =bClose?"关闭":"打开";
foreach (XUBarNode tmpNode in tmpBar.NodeList)
{
tmpNode.CloseLayer(bClose);
}
}
foreach(XUBarNode tmpNode in this.NodeList)
{
tmpNode.CloseLayer(bClose);
}
this.mapCtrl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);
}
private void contentPanel_DragOver(object sender, DragEventArgs e)
{
if (e.AllowedEffect == DragDropEffects.Move)
{
e.Effect = DragDropEffects.Move;
}
}
private void contentPanel_DragDrop(object sender, DragEventArgs e)
{
Control item = (Control)e.Data.GetData(typeof(XUBarNode));
MessageBox.Show("1111");
this.Controls.Add(item);
}
private void XUNaviBar_DragOver(object sender, DragEventArgs e)
{
if (e.AllowedEffect == DragDropEffects.Move)
{
e.Effect = DragDropEffects.Move;
}
}
private void XUNaviBar_DragDrop(object sender, DragEventArgs e)
{
Control item = (Control)e.Data.GetData(typeof(XUBarNode));
item.Dock = DockStyle.Bottom;
this.contentPanel.Controls.Add(item);
}
}
XUTocControl控件的最终组合代码:
{
public partial class XUTocControl : UserControl
{
private ESRI.ArcGIS.Controls.AxMapControl _MapCtrl;
public XUTocControl()
{
InitializeComponent();
}
public XUTocControl(AxMapControl mapCtrl)
{
InitializeComponent();
_MapCtrl = mapCtrl;
InitTOC(mapCtrl.Map, this.MainBar);
}
/// <summary>
/// 初始化图层工具
/// </summary>
/// <param name="map"></param>
/// <param name="mainBar"></param>
private void InitTOC(IMap map,XUNaviBar mainBar)
{
MainBar.Text = map.Name;
//保存地图对象
MainBar.Tag = _MapCtrl;
for (int j = 0; j < map.LayerCount; j++)
{
ILayer2 pLayer = map.get_Layer(j) as ILayer2;
if (pLayer is ESRI.ArcGIS.Carto.IGroupLayer)
{
ICompositeLayer pCompositelayer;
pCompositelayer = (ICompositeLayer)pLayer;
XUNaviBar nvBar = new XUNaviBar(mainBar, pLayer);
nvBar.Tag = _MapCtrl;
int i= 0;
ILayer2 pLyr;
for (i = 0; i < pCompositelayer.Count; i++)
{
pLyr = pCompositelayer.get_Layer(i) as ILayer2;
XUBarNode tmpNode = new XUBarNode(nvBar, pLyr.Name, pLyr.Name);
//保存图层对象
tmpNode.Tag = pLyr;
nvBar.AddBarNode(tmpNode);
}
MainBar.AddBar(nvBar);
nvBar.Dock = DockStyle.Top;
}
else
{
XUBarNode tmpNode = new XUBarNode(mainBar, pLayer.Name, pLayer.Name);
//保存图层对象
tmpNode.Tag = pLayer;
MainBar.AddBarNode(tmpNode);
}
}
}
private void node_click(object sender, EventArgs e)
{
_MapCtrl.ActiveView.Refresh();
}
}
调用控件的示例代码:该控件使用时,需要ArcEngine 9.3 Runtime.
private void FormTest_Load(object sender, EventArgs e)
{
this.axMapControl1.LoadMxFile(Application.StartupPath + "\\geodata\tgwGD.mxd");
XUTocControl tocCtrl = new XUTocControl(this.axMapControl1);
this.panel1.Controls.Add(tocCtrl);
tocCtrl.Dock = DockStyle.Fill;
}
实现控件的节点拖拽功能参照了下列资料,在此表示感谢!
http://bbs.zbitedu.com/viewthread.php?tid=8711
http://www.cnblogs.com/ttc/archive/2008/08/21/1273172.html
http://www.cppblog.com/AutomateProgram/archive/2010/08/26/124890.html
http://kb.cnblogs.com/a/1671126/
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.dodragdrop.aspx#Y6514
此外,WorldWind里也有拖拽文件功能WorldWind学习系列十六:3D Cross Section插件功能分析——TerrainViewer ,拖拽控件和拖拽文件本质上是一样的,只是拖拽控件是将控件作为数据用于传递而已。
本博客声明:本人的技术探索过程中,得到了国信司南公司方面支持。今后,本人博客里的所有技术探索成果将归“无痕客”、“国信司南”和“博客园”三方共同所有,原创作品如需转载,请注明本博客声明。