Thursday, July 28, 2011

How to create a drop target for layers in ArcMap?

This post as originally published 3/16/2007 on Microsoft Live Spaces.

How to create a drop target for layers in ArcMap?

Drag and drop is a convenient and intuitive method of exchanging data between windows (and applications). ESRI applications like ArcCatalog and ArcMap have drop and drop capabilities, for example, you can drag layer into the Buffer geoprocessing tool window (see above).

Using the sample code below you can add this capability to your .NET application, that is, you can make your window/control a drop target for layers dragged from the ArcMap table of contents. This code can be used in a window hosted by an ESRI application such as an ArcMap dockable window or as a standalone application.

To enable this feature, a utility class called EsriDataObject is provided below to assist with the deserialization of dropped objects from ArcMap.

The first code sample demonstrates how to use EsriDataObject in a .NET Form. The code will display the concatenate list of layer names that were dropped on the form. The code assumes there is Form called form1 and a TextBox called textBox1.

How to use "EsriDataObject" in a .NET Form.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using ESRI.ArcGIS.Carto;

namespace MyNamespace {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
            this.textBox1.AllowDrop = true;
            this.textBox1.DragEnter +=
new DragEventHandler(this
.textBox1_DragEnter); this.textBox1.DragOver +=
new DragEventHandler(this
.textBox1_DragOver); this.textBox1.DragDrop +=
new DragEventHandler(this
.textBox1_DragDrop); } private void textBox1_DragEnter(object sender, DragEventArgs e) { e.Effect = EsriDataObject.IsValid(e.Data) ?
DragDropEffects.All : DragDropEffects
.None; } private void textBox1_DragOver(object sender, DragEventArgs e) { e.Effect = EsriDataObject.IsValid(e.Data) ?
DragDropEffects.All : DragDropEffects
.None; } private void textBox1_DragDrop(object sender, DragEventArgs e) { EsriDataObject esriDataObject =
EsriDataObject
.ConvertToEsriDataObject(e.Data); foreach (ILayer layer in esriDataObject.LayerCollection) { this.textBox1.Text += layer.Name + " "; } } } }

Sample 2: Source code to "EsriDataObject".

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.esriSystem;

namespace MyNamespace {
    public class EsriDataObject {
        private const string DATAOBJECT_ESRILAYERS = "ESRI Layers";
        private const string INTERFACE_ILAYER =
"{34C20002-4D3C-11D0-92D8-00805F7C28B0}"
; private const string INTERFACE_ITABLEPROPERTY =
"{4657D951-5FFB-11D3-9F6C-00C04F6BC886}"; private readonly List<ILayer> _lc = new List<ILayer>(); private readonly List<ITableProperty> _tpc = List<ITableProperty>(); // // PROPERTIES // public List<ILayer> LayerCollection { get { return this._lc; } } public List<ITableProperty> TablePropertyCollection { get { return this._tpc; } } // // STATIC METHODS // public static bool IsValid(IDataObject dataObject) { return dataObject.GetDataPresent(DATAOBJECT_ESRILAYERS); } public static EsriDataObject ConvertToEsriDataObject(
IDataObject
dataObject) { // EsriDataObject esriDataObject = new EsriDataObject(); // Exit if dropped object is invalid if (EsriDataObject.IsValid(dataObject)) { // Get Byte Array from DataObject object esriLayers = dataObject.GetData(DATAOBJECT_ESRILAYERS); MemoryStream memoryStream = (MemoryStream)esriLayers; byte[] bytes = memoryStream.ToArray(); // Load Byte Array into a Stream (ESRI Wrapper of IStream) IMemoryBlobStreamVariant memoryBlobStreamVariant =
new MemoryBlobStreamClass
(); memoryBlobStreamVariant.ImportFromVariant(bytes); IMemoryBlobStream2 memoryBlobStream =
(
IMemoryBlobStream2
)memoryBlobStreamVariant; IStream stream = (IStream)memoryBlobStream; // Load Stream into an ESRI ObjectStream IObjectStream objectStream = new ObjectStreamClass(); objectStream.Stream = stream; // Get Number of Layers in Dropped Object byte pv; uint cb = sizeof(int); uint pcbRead; objectStream.RemoteRead(out pv, cb, out pcbRead); int count = Convert.ToInt32(pv); // Define Guids Guid guidLayer = new Guid(INTERFACE_ILAYER); Guid guidTable = new Guid(INTERFACE_ITABLEPROPERTY); // Get Dropped Layers for (int i = 0; i < count; i++) { object o = objectStream.LoadObject(ref guidLayer, null); ILayer layer = (ILayer)o; esriDataObject.LayerCollection.Add(layer); } // Get Dropped TableProperties for (int i = 0; i < count; i++) { object o = objectStream.LoadObject(ref guidTable, null); if (o == null) { continue; } ITableProperty tableProperty = (ITableProperty)o; esriDataObject.TablePropertyCollection.Add(tableProperty); } } return esriDataObject; } } }

Additional information:

Dragging and dropping objects from ArcCatalog is a lot easier than ArcMap. In ArcMap, a dragged object is packaged as an "ESRI Layer" object in the DataObject. The code above describes how to unpack an "ESRI Layer" object. In ArcCatalog, a dragged object is packaged as an "ESRI Names" object in the DataObject. Fortunately, there are ArcObjects available to pack and unpack this object.

To create an "ESRI Names" object use the following:
INameFactory.PackageNames Method

To unpackage an "ESRI Names" object use:
INameFactory.UnpackageNames Method

Lastly, here is an online example showing how to unpackage an "ESRI Names" object:
esriSystem NameFactory Example

No comments:

Post a Comment