# Creating Panning Buttons in C#

I am new to this and still trying to learn. I have a bunch of VBA code that I am trying to convert to C# and even the most basic things are hanging me up.

Currently I am trying to build a button that pans the display Left when pressed.

The original VBA code that I have looks as follows:

Sub pan_left()
On Error GoTo Errhandler

Dim pMxDocument As IMxDocument
Dim pactiveview As IActiveView
Dim penv As IEnvelope
Dim pxmax, pxmin, pymax, pymin As Double
Dim nxmax, nxmin, nymax, nymin As Double
Dim width, height As Double

Set pMxDocument = ThisDocument
Set pactiveview = pMxDocument.FocusMap
Set penv = pactiveview.Extent

pxmax = penv.XMax
pxmin = penv.XMin
pymax = penv.YMax
pymin = penv.YMin

width = pxmax - pxmin
height = pymax - pymin
nxmax = pxmax + (width / 1.08)
nxmin = pxmin + (width / 1.08)
nymax = pymax
nymin = pymin

penv.XMax = nxmax
penv.XMin = nxmin
penv.YMax = nymax
penv.YMin = nymin

pactiveview.Extent = penv
pactiveview.Refresh

ThisDocument.fTimer
Exit Sub
Errhandler:
On Error Resume Next

End Sub


I have been trying to write it using the Desk top Addins. I have found some code on ESRI's website found here: Pan Left (Not sure if I am even on the right track.)

Currently my code looks as follows:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.SystemUI;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.Framework;

namespace NAIP_QQv1

{

[ClassInterface(ClassInterfaceType.None)]

[Guid("593EDF4F-D1FE-4a8d-8076-C3B583C37F6B")]

public class PanDown : ICommand

{
#region COM Registration Function(s)
[ComRegisterFunction()]
[ComVisible(false)]
static void RegisterFunction(Type registerType)
{
// Required for ArcGIS Component Category Registrar support
ArcGISCategoryRegistration(registerType);

//
// TODO: Add any COM registration code here
//
}

[ComUnregisterFunction()]
[ComVisible(false)]
static void UnregisterFunction(Type registerType)
{
// Required for ArcGIS Component Category Registrar support
ArcGISCategoryUnregistration(registerType);

//
// TODO: Add any COM unregistration code here
//
}

#region ArcGIS Component Category Registrar generated code
/// <summary>
/// Required method for ArcGIS Component Category registration -
/// Do not modify the contents of this method with the code editor.
/// </summary>
private static void ArcGISCategoryRegistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
ControlsCommands.Register(regKey);

}
/// <summary>
/// Required method for ArcGIS Component Category unregistration -
/// Do not modify the contents of this method with the code editor.
/// </summary>
private static void ArcGISCategoryUnregistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
ControlsCommands.Unregister(regKey);

}

#endregion
#endregion

[DllImport("user32.dll")]
static extern IntPtr SetFocus(IntPtr hWnd);

private System.Drawing.Bitmap m_bitmap;
private IntPtr m_hBitmap;
private IHookHelper m_pHookHelper;

{

public Pan_Left()
{
//m_pHookHelper = new HookHelperClass ();

}

protected override void OnClick()
{//Get the active view
IActiveView pActiveView = m_pHookHelper.ActiveView;

//Get the extent
IEnvelope pEnvelope = (IEnvelope)pActiveView.Extent;

}

protected override void OnUpdate()
{
}
}
}
}

• So, you've only got the extent, does that have a value? You could trim a lot of the code out and just leave the onClick() to make it more readable. – Michael Stimson Feb 26 '15 at 0:23

The fist problem is it's not an add-in.

public class PanDown : ICommand


public class PanDown : ESRI.ArcGIS.Desktop.AddIns.Tool


or

public class PanDown : ESRI.ArcGIS.Desktop.AddIns.Button


Have a good read of this and start again from scratch, ensuring that you select an add-in type for your project when you start in Visual Studio. There are a lot of bits and pieces that are set/created in the project when you select Esri Add-in that would be very difficult to go back and change, so it's far easier to start with a blank slate.

Note the 'button' on the left is checked, that's instructing Visual Studio to make all the bits and pieces for a button. In the Category I use !MyStuff, when you go to add it to ArcMap in the customize dialog the tools with the exclamation mark come to the top.

After the basic template is created you need to add some more references, depending on what you want to do. The clues are in the ArcGis help:

This tells us that the IActiveView interface is in the Carto library. You can add this reference by right clicking on the references and select 'add ArcGis reference':

Will add Geometry (needed for IEnvelope).

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using ESRI.ArcGIS.Geometry;

namespace PanDown
{
{
public PanDownButton()
{
}

protected override void OnClick()
{
IEnvelope pEnv = ArcMap.Document.ActiveView.Extent;
double pEnvHeight = pEnv.Height;
pEnv.YMax -= pEnvHeight; // move top down by height of extent
pEnv.YMin -= pEnvHeight; // move bottom down by same amount
ArcMap.Document.ActiveView.Extent = pEnv;
ArcMap.Document.ActiveView.Refresh(); // redraw the map

//ArcMap.Application.CurrentTool = null; // turn this off so the current tool remains active
}
protected override void OnUpdate()
{
Enabled = ArcMap.Application != null;
}
}

}


Should do what you want.

There is another massive gotcha in visual studio if you are using 64bit windows. Esri is a 32bit program and can only talk to 32bit dlls', by default Visual Studio compiles to Any CPU - which means 64bit on 64bit windows. That needs to be changed to x86; if you have the Express version you need to follow another path to build for x86, because the configuration manager is disabled in Visual Studio express (this may be intentional) you must manually add <PlatformTarget>x86</PlatformTarget> to the build configurations.

In the vbproj or vcproj file copy:

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\Debug\</OutputPath>
<DocumentationFile>Class_and_Form.xml</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<RegisterForComInterop>true</RegisterForComInterop>
</PropertyGroup>


to

 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\Debug\</OutputPath>
<DocumentationFile>Class_and_Form.xml</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<RegisterForComInterop>true</RegisterForComInterop>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>


and in the .vbproj.user ensure that the debug StartProgram is replicated:

<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<StartProgram>C:\your\path\to\ArcMap.exe</StartProgram>
<StartAction>Program</StartAction>
</PropertyGroup>


to allow for step-debugging in Visual Studio.. when you run (F5) Studio will launch ArcMap, then when you click the tool execution will stop at your breakpoints and you can step through (F10 & F11)... invaluable!

and do the same for '$(Configuration)|$(Platform)' == 'Release|AnyCPU'. If you've done it right then the configuration manager will now be enabled. Again, these instructions are only for Visual Studio Express.

• A recommended read along with what Micheal provided in his answer above is this book: ArcGIS for Desktop Development Using .NET – Jason Miller Feb 26 '15 at 15:28
• The Esri help is a little counter-intuitive, you need to know what you're looking for to get any help on it so it's best to have a little bit of an understanding on how the common objects interact... snippets will only get you so far. – Michael Stimson Feb 26 '15 at 21:57
• Thanks for the prompt and detailed response. I was out of the office last week and been trying the code this week. I have purchased the text you recommended (thanks Jason) and I am working though it. I have tried the code you gave me and have changed Visual Studio (I'm using regular not express) to x86. The code builds fine, however the screen does not pan when I press the button. I am attempting to troubleshoot, however any more help you can give will be very much appreciated. – Ryan Mar 10 '15 at 22:03
• In addition when trying to troubleshoot I throw a breakpoint in after the OnClick(). I receive "The breakpoint will not currently be hit. No symbols have been used for this document." I have created a toolbar and put the Pan_Down button on the toolbar. I am assuming that I am missing something very simple. – Ryan Mar 10 '15 at 22:12
• If the tool isn't created with the wizard in visual studio the event isn't wired. Was the OnClick event there or did you include it? I recommended in my answer to start from scratch - that is fairly important. – Michael Stimson Mar 10 '15 at 22:18

After spending some time working though Beginning ArcGIS for Desktop Development using .NET, I came back and was reviewing what was going wrong for me. For whatever reason in the code block Michael gave me when I added in a geographic layer the pEnv.YMax and the pEnv.YMin were coming out as the same number, hence the ActiveView.Extent was staying the same and was not panning. I modified the code as follows storing the pEnv.YMax - pEnv.YMin in a variable Height. I did the same for Width as I will use that for panning Left and Right. I am slowly figuring this stuff out and appreciate everyone's help.

protected override void OnClick()
{
IEnvelope pEnv = ArcMap.Document.ActiveView.Extent;

//pEnv.YMax -= pEnv.Height; // move top down by height of extent
//pEnv.YMin -= pEnv.Height; // move bottom down by same amount
double Height = ((pEnv.YMax - pEnv.YMin) * 0.75); //Create Height of extent Variable because C# does not want to save it in above function, moves screen by 75%
double Width = ((pEnv.XMax - pEnv.XMin) * 0.75); //Create Width of extent Variable because C# does not want to save it in above function, moves screen by 75%

pEnv.YMax = (pEnv.YMax - Height); // move top down by height of extent
pEnv.YMin = (pEnv.YMin - Height); // move bottom down by same amount

//MessageBox.Show("Max: " + pEnv.YMax.ToString() + " Min: " + pEnv.YMin.ToString()); //Message Box Function used to trouble shoot.

ArcMap.Document.ActiveView.Extent = pEnv;
ArcMap.Document.ActiveView.Refresh(); // redraw the map

//ArcMap.Application.CurrentTool = null; // turn this off so the current tool remains active
}

• it's a good effort. Yes you need to copy your height (and/or width) to a new variable because the height of the envelope changes as soon as you do the first pEnv.YMax = pEnv.YMax - Height; (height now == 0). This is why you can't do pEnv.YMax = pEnv.YMax - pEnv.Height; – Michael Stimson Jul 21 '15 at 3:32