打造自己地图AxTOCContrl树形控件——XUTocControl(成功解决节点控件拖拽问题)

   最近在搞应用于Windows平板电脑的系统开发,需要开发适用于平板电脑的地图控制控件取代ArcEngine自带的AxTOCContrl。

       搞控件开发太费脑啦,需要注意的逻辑关系很复杂 都晕倒啦!

        控件实现:地图图层控制(单个控制、图层集控制、整体控制)、图层收缩、控件图层节点拖拽,基本可以替代AE中的AxTocControl。

      

       本控件开发周期为1.5天,解决主要技术问题有节点的动态加载、收缩与展开高度的自动计算、节点控制图层的关闭与打开、节点的拖拽功能。

       控件XUTocControl实现,只有三个文件,如图:

      

      图层节点XUBarNode关键代码:

View Code
using System;
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, 
nullnull);
            _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 代码:

View Code
namespace GC_Controls
{
    
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, 
nullnull);
                
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, nullnull);
        }


        
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控件的最终组合代码:

View Code
namespace GC_Controls
{
    
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 ,拖拽控件和拖拽文件本质上是一样的,只是拖拽控件是将控件作为数据用于传递而已。

       本博客声明:本人的技术探索过程中,得到了国信司南公司方面支持。今后,本人博客里的所有技术探索成果将归“无痕客”、“国信司南”和“博客园”三方共同所有,原创作品如需转载,请注明本博客声明。

  

原文地址:https://www.cnblogs.com/wuhenke/p/2039488.html