Interaction between Commands, Tools, and Forms

A scenario that comes up for me quite often is the need to pass information from a form to a command or tool and vice versa. At first it seemed a difficult task because I wasn’t all that familiar with COM programming and was under the impression that COM objects were some sort of special entities that somehow functioned differently from normal objects. This really was a misconception on my part. While it is true that COM classes are a special type of class and their implementation differs from normal object classes, they are still at heart normal object classes. The same is true for Form classes. All that is needed to pass information from one class to another is a property or method and there is no reason why you can’t add your own to COM classes and forms alike.

The sample project that accompanies this article contains a sample dialog with two buttons. The first demonstrates how to pass information from a form to a custom ArcMap command. The second shows how to pass information from a custom ArcMap tool to a form.

Looking at the code behind the first button, you’ll see that we are calling a custom command that performs a fixed zoom in operation. This custom command is similar to the built-in Fixed Zoom In command found in ArcMap. The difference is that our custom command allows the zoom factor to be specified, thus changing how much the map is zoomed. Looking at the implementation of the custom command you’ll see that it has a property named ZoomFactor. The trick to gaining access to this property is shown in the form code. The code gets a reference to the command by calling ICommandBars::Find(). We can get a reference to the underlying ICommand object from ICommandItem::Command. Because this reference is typed as ICommand we need to cast to the actual class type of our command class. Once we’ve done this we have access to the ZoomFactor property. We can then set the property and call Execute() to run the command.

An alternative method of doing this would be to create a custom interface that defines the ZoomFactor property and have our command class implement this interface in addition to implementing ICommand. This would certainly keep more to the COM model than simply defining the property directly on the class but it isn’t really necessary. If you have a set of properties and methods that you want to be common across multiple command and tool classes, then you might want to consider using a custom interface to ensure consistency. Otherwise you may find it simpler to do it as shown.

The second example demonstrates how to pass information from a tool to a form. Looking at the code behind the button you’ll see that it is calling a custom select features tool similar to the one shown in my last article. The difference here is that this tool will update the calling form to display the number of selected features. In order to do this, the tool class will need a reference to the form. Looking at the tool class you’ll see that a property has been added to allow the form reference to be set. This property is set in the form code the same way as in the previous example. Looking at the form class you’ll see a method has been added that will update the label text on the form using the value passed in as a parameter. The code in the custom tool calls this method after the features have been selected and passes in the selection count.

The real trick here is getting a reference to the form. In this particular example it’s easy because the tool is being called from the form in question. All we have to do is pass that instance of the form. A more common scenario requires updating a form displayed by another command or tool class. In this case you will need to add a property to that command or tool that will return an instance of the form it displays. The code in the tool class that needs to update that form will first need to get a reference to the command or tool that displays the form. This is done by calling ICommandBars::Find() just like we did in these two examples. Once you have that command or tool reference you can get the form reference from the property you added and pass it to your custom tool.

As with anything else in the programming world there are other solutions than these but I think the two we have demonstrated here are relatively simple and easy to understand. Hopefully this will get you thinking about how individual components can work together within your applications.

Advertisements

Additions to the GISi Team

GISi is proud to announce the addition of five new employees to the company:  Michael Hatmaker, Denise Hakanson, John Love, Rick Price, and Beth Lusk. Read more of this post

Creating a Print Service for High-quality Web GIS Output

Clients with Web GIS projects commonly ask to include high-quality print output. It’s a challenging requirement because the browser is not natively a platform for high-quality printing (try thinking of a non-GIS website out there with such a feature). The latest print task in the ArcGIS Server Web ADF does reasonably good output, but clients familiar with ArcGIS Desktop layouts want something comparable in their websites.

Clients most often want PDF output for printing, and it’s possible to create PDFs in code using one of the many PDF generators out there. That option is certainly flexible. But the MXD-based ArcGIS layout offers other advantages. One key advantage is the ability for clients themselves to create and modify print templates. They can add and remove features like titles, north arrows, and scalebars. They can also rearrange and resize the elements in the layout.

The task is to employ the ArcGIS components (ArcObjects) for layouts in the context of a server-based application. A few efforts are out there already, such as the Layout Server Object Extension (SOE), available on the ArcGIS Server user-supplied Code Gallery/ArcScripts. This sample works with the ArcGIS Server .NET Web ADF. We’ve used that code successfully, but found some reasons to look for a different approach. Some clients claimed the Layout SOE impacted the overall performance of their website. More importantly, we needed a solution that would work for multiple platforms, such as the JavaScript and Flex APIs for ArcGIS Server. Those are client-side applications, which require an entirely different approach from a server-side component.

The obvious solution is a Web service, one that both browser clients and server-based applications could call. We developed such a Web service in the last few months through the efforts of several of our staff. The client application passes in parameters such as the map services to include on the layout, the map extent, the path of the MXD template to use, and the GIS server that will perform the export. The service then connects to the GIS server and uses ArcObjects to build the request for the layout export. The service passes the URL of the output file back to the client, which allows the user to download and display it. The output format can of course be any that ArcObjects supports, such as PDF, JPEG, PNG, EPS or SVG.mobile_output_sm

We were able to make the service flexible so that map services could be plugged into the layout template dynamically. In other words, the layers in the print were not pre-set in the map document (MXD), but instead were dynamically pulled in from one or more map services. At first we used “local” connections to the map server, but soon realized we needed to also print web service-based map services, such as ArcGIS Online services. We were able to implement the GIS client components (IAGSServerConnection and IMapServerLayer) to make the connection to the map services.

Another challenge was to add graphics dynamically onto the map. Graphics, such as user markup on the map, are implemented differently in each client API, and each of those is different from graphics in ArcObjects. We had to create our own structures for passing graphics to the print service, so that client applications could take their own graphic types and translate them to something our web service could use. In some cases the capabilities do not match across APIs, which causes some issues for applications. For instance, ArcObjects doesn’t support partial transparencies (e.g., alpha transparency) in polygon fills for graphics. So client applications could not add such graphics to the map and expect them to be reproduced in the exported layout.

Since we started using the service in client applications, we’ve had requests for enhancements, not surprisingly. We’ve added support for ArcGIS Image Services, the ability to update text elements on the layout (title, author, etc.), and options to set the overview map extent and north arrow style, among others. One aspect we are still working on is displaying legends. Since the service dynamically displays multiple map services with potentially many layers, the legend can grow beyond its designated container. But we’ve been pretty successful in generating output that clients like and that is flexible for various applications and APIs.

Calling Commands and Tools from a Custom Dialog

Now that we have a couple of classes that help us implement modeless dialogs, I thought it would be good to start looking at a few things we can do with them. One question I’ve seen asked numerous times on the forums is how to call commands or tools from a dialog instead of putting them onto a toolbar. The sample project that accompanies this article has several examples that show how to call built-in ArcMap commands and tools as well as your own custom commands and tools.

Looking at the code behind each of the buttons on the sample dialog, you’ll see the first thing we do is assign the GUID for the command or tool we want to execute. For built-in ArcMap commands and tools, you can find the GUIDs that you need on the ESRI Resources web site:

http://resources.esri.com/arcgisdesktop/dotnet/index.cfm

Click Programming with .NET in the Getting Started box on the right. This will take you to the main .NET page. In the table of contents, click General Reference and then click Names and Ids. You will see a page for each of the Desktop applications. Click the page for the appropriate Desktop application and find the desired command or tool in the list. For your own custom commands and tools you’ll need to use the ClassId found in the COM GUIDs section of your class code.

Once we’ve assigned the GUID we can use it to get a reference to the command or tool. We do this by calling the Find() method as shown in the sample code. After that, all that’s left is to execute the command or tool. The Execute() method handles both commands and tools. For commands, it will call the OnClick() method for the command. For tools, it will call the OnClick() method and make the tool the application’s current tool. We will get the same behavior as we would by putting the command or tool on a toolbar and clicking on it.

The sample project demonstrates calling commands and tools from a button on the dialog. You could do the same thing using a wide range of form controls such as toolbars, menus, listboxes, or comboboxes. The code will be the same; you’ll just put it inside whatever event is appropriate for the control you’ve decided to use.

There’s an alternative method to the code used in the sample that involves calling the OnClick() method yourself and setting the current tool manually by calling IApplication::CurrentTool. You can use this method if you like but I prefer the method shown in the example because it’s simple and uses the same code to handle both commands and tools.

GIS and Computer-Human Interaction

Today a coworker reiterated how much benefit both clients and development staff received from designing an application’s interface prior to application development.  The client gets a clear idea of how the software will appear and how they can expect it to behave.  Developers use the interface design to better understand application workflow and its functional requirements.  An entire industry has sprung up around this idea of user focused application design based on computer-human interaction (CHI).  I have heard the discipline referred to by several names, but Usability Engineering was the one most prevalent in work environments I have been in.  The goal for a Usability Engineer was to rapidly prototype multiple interface design options (wire frames) from requirements, “present” or test the options with real system end users, capture the test results, and recommend an interface design based on these results.  This concept of end user interface design and GIS Web development could not have met one another soon enough.

At the end of the 1990’s GIS Web development began to take shape.  Initially, the thought was to replicate desktop functionality on a web browser.  Anyone remember ArcView IMS?  Many accomplished GIS developers applied the same concepts to their Web development they had learned in developing desktop applications– only a GIS professional will be using this application.  One person I worked with summed up this mindset by adding, “If it was hard to write, it should be hard to understand” to their email signature.  Even the next generation of ESRI software for Internet development, MapObjects IMS, was originally based on desktop development.  The concept of there being GIS Web application end users who could be your average Joe was not given serious thought.  Obviously that concept has changed.

As Web technology has continued to evolve, the idea of and need for non-GIS professionals using GIS-enabled applications was brought to the industry’s attention.  From this GIS developers began to utilize and receive the added benefits of usability analysis that mainstream Web development had already begun to adopt.   I found that many developers in the GIS career field, including myself, had to adjust our perception of who the target audience of our application would be.  The progression of CHI design processes being incorporated in GIS software development was brought center stage at the 2008 ESRI Developer Summit.  Alan Cooper, considered by many to be the primary pioneer of CHI design principals, was the keynote speaker at the conference.  In the end we can thank those early Internet GIS applications that presented mapping in a simplified manner for their contribution to the paradigm shift in the presentation layer of Web based GIS applications.

Tour de Flex

I found myself looking for some new design ideas late the other night for two of our current Flex projects that are in the design phase.  I had stumbled across the Adobe Tour de Flex component explorer.  After downloading the Adobe Air version (Desktop), I spent several hours reviewing the custom controls and it even had several ESRI samples not on the ESRI sample site.   Tour de Flex has over 200 live samples, each with source code.  I spent much of my time reviewing the Core Components, Cloud APIsData Visualization, Mapping, Doug McCaune 3D Containers Axiis Open Source Project, and Degrafa

After browsing, I was inspired by all of the bundled Flex components, along with help and source code.  This is a brilliant tool.  The site itself is a Flex application and inspiring (Inspiration sample design thanks to Bryan Rayo).

Exploring Tour de Flex is a great way to get an overview of what is possible in Flex and provides Flex developers with an illustrated reference tool.  It also provides commercial and non-commercial Flex developers with a place to showcase their work.  Maybe we will diversify and showcase our work on this site.  I look forward to our future Flex projects.