Netviewer extensions

It doesn’t take much to create a Netviewer extension:

  1. Derive a class from Microsoft.Practices.CompositeUI.ModuleInit
  2. Create a constructor with a [ServiceDependency()] WorkItem argument
  3. Modify your ProfileCatatalog.xml

The class would look like this:

namespace NetviewerLibrary
{
	#region -- Using directives --
	using System;
	using System.Reflection;
	using Microsoft.Practices.CompositeUI;
	using d = System.Diagnostics.Debug;
	#endregion

	public class Init: ModuleInit
	{
	    public Init([ ServiceDependency()] WorkItem rootWorkItem)
	    {
		   d.WriteLine( "Init");
	    }
	}
}

This code is using a pattern from the Microsoft Enterprise Library version 2.0 (no longer supported by Microsoft) for setting up an external library to be invoked by an application. The external library in this case is your extension, the application is Netviewer. All it takes for Netviewer to invoke the extension is a class derived from Microsoft.Practices.CompositeUI.ModuleInit and create a constructor with an Microsoft.Practices.CompositeUI.ServiceDependency argument. In line 10 a Microsoft.Practices.CompositeUI.ModuleInit-derived class is declared and at line 12 you can see the constructor. If your extension is compiled to an assembly called ‘NetviewerLibrary1’, you include your extension in the ProfileCatalog.xml file and add your command to IngrViewer.exe.config, Netviewer will invoke your extension.

This is the ProfileCatalog.xml :

<SolutionProfile xmlns="http://schemas.microsoft.com/pag/cab-profile/2.0">
  <Section Name="Apps">
    <Modules>
      <ModuleInfo AssemblyFile="GTechnology" />
      <ModuleInfo AssemblyFile="NetviewerCommand.dll" />
    </Modules>
  </Section>
</SolutionProfile>

This is not very useful other then illustrating what it takes for Netviewer to invoke your extension. Netviewer works with commands, and each extension can have one more or of it. The ProfileCatalog.xml is where you define your extensions, the IngrViewer.exe.config is where you define your commands. In order to do something useful, we need to add our command to IngrViewer.exe.config and extend our code with a command handler:

This is the line in IngrViewer.exe.config which adds the command:

<command name="NetviewerCommand1" autocreate="true" tooltip="NetviewerCommand1" label="CustomCommand1" />

This is the code:

namespace NetviewerLibrary
{
  #region -- Using directives --
  using System;
  using System.Reflection;
  using Microsoft.Practices.CompositeUI;
  using Microsoft.Practices.CompositeUI.Commands;
  using Intergraph.OneMobile.Infrastructure.Interface;
  using d = System.Diagnostics.Debug;
  #endregion

	public class Init: ModuleInit
	{
		public WorkItem WorkItem{   get; set;   }

		public Init([ ServiceDependency()] WorkItem rootWorkItem)
		{
			d.WriteLine( "Init");
			this.WorkItem = rootWorkItem;
		}

		public override void Load()
		{
			d.WriteLine( "Load");

			base.Load();

			ControlledWorkItem workItem = this.WorkItem.WorkItems.AddNew();
			workItem.Controller.Run();
		}
	}

	public partial class MyController :
		Intergraph.OneMobile.Infrastructure.Interface.WorkItemController
	{
		[ CommandHandler( "NetviewerCommand1")]
		public void MyCommand_Handler( object sender, EventArgs eventArgs)
		{
			d.WriteLine( "MyCommand_Handler");
		}
	}
}

If you now run Netviewer, there will be an extra button with a label “NetviewerCommand1” and if you click it, your commandhandler will be invoked.

Netviewer command
Netviewer command

Now let’s turn this extension in something useful, for instance placing a pinpoint. We want our command to ask the user to click in the Window and then place a pinpoint at the cursur. To do so, we need to extend our code with the following:

  1. A reference to the Netviewer MapviewService instance for setting up eventhandlers
  2. A Commandhandler for a left-mouse click
  3. Infrastructure code

The modified code looks like this:

namespace NetviewerLibrary
{
  #region -- Using directives --
  using System;
  using System.Reflection;
  using Microsoft.Practices.CompositeUI;
  using Microsoft.Practices.CompositeUI.Commands;
  using Microsoft.Practices.CompositeUI.EventBroker;
  using Intergraph.OneMobile.Infrastructure.Interface;
  using Intergraph.OneMobile.Map.Interface.Constants;
  using Intergraph.OneMobile.Map.Services;
  using Intergraph.Controls.oneViewer;
  using d = System.Diagnostics.Debug;
  #endregion

  public static class Global
  {
    public static int CallCount {   get; set;   }
  }

 public class Init: ModuleInit
 {
    public WorkItem WorkItem      {   get; set;   }

      public Init([ ServiceDependency()] WorkItem rootWorkItem)
      {
        d.WriteLine( string.Format( "{0}.{1} ({2}):{3}", GetType().Name, System.Reflection.MethodInfo.GetCurrentMethod().Name, Global.CallCount++, string.Empty));

        this.WorkItem = rootWorkItem;
      }

      public override void Load()
      {
        d.WriteLine( string.Format( "{0}.{1} ({2}):{3}", GetType().Name, System.Reflection.MethodInfo.GetCurrentMethod().Name, Global.CallCount++, string.Empty));

        base.Load();

        ControlledWorkItem workItem = this.WorkItem.WorkItems.AddNew();
        workItem.Controller.Run();
      }
  }

  public partial class MyController :
    Intergraph.OneMobile.Infrastructure.Interface.WorkItemController
  {
    #region -- Properties --
    public IMapViewService  MapviewService    {   get; set; }
    public string      OldMapLocateMode  {   get; set; }
    #endregion

    [ CommandHandler( "NetviewerCommand1")]
    public void MyCommand_Handler( object sender, EventArgs eventArgs)
    {
      d.WriteLine( string.Format( "{0}.{1} ({2}):{3}", GetType().Name, System.Reflection.MethodInfo.GetCurrentMethod().Name, Global.CallCount++, string.Empty));

      this.MapviewService.SetStatusBar( "Click to place pinpoint");

      SwitchMapMode  ( );
      SetupEventSink  ( );
    }

    [ EventSubscription( EventTopicNames.MapViewLoaded)]
    public void MapViewLoaded_Handler( object sender, EventArgs eventArgs)
    {
        d.WriteLine( string.Format( "{0}.{1} ({2}):{3}", GetType().Name, System.Reflection.MethodInfo.GetCurrentMethod().Name, Global.CallCount++, string.Empty));

        this.MapviewService = WorkItem.Services.Get();
    }

    private void SwitchMapMode( )
    {
      d.WriteLine( string.Format( "{0}.{1} ({2}):{3}", GetType().Name, System.Reflection.MethodInfo.GetCurrentMethod().Name, Global.CallCount++, string.Empty));

      this.OldMapLocateMode = this.MapviewService.GetMapMode( );
      this.MapviewService.SetMapMode( Intergraph.Controls.oneViewer.MapModes.Custom);
    }

    private void SetupEventSink( )
    {
      d.WriteLine( string.Format( "{0}.{1} ({2}):{3}", GetType().Name, System.Reflection.MethodInfo.GetCurrentMethod().Name, Global.CallCount++, string.Empty));

      this.MapviewService.RedlineLButtonDown    += new Intergraph.Controls.oneViewer.RedlineButtonEventHandler( MapviewService_RedlineLButtonDown );
    }

    void MapviewService_RedlineLButtonDown( object sender, Intergraph.Controls.oneViewer.RedlineButtonEventArgs e )
    {
      d.WriteLine( string.Format( "{0}.{1} ({2}):{3}", GetType().Name, System.Reflection.MethodInfo.GetCurrentMethod().Name, Global.CallCount++, string.Empty));

      string  point    = string.Format( "{0}, {1}", e.XWorldPos, e.YWorldPos);
      string  pointType  = "UOR";
      string  fontName  = "G_MapPins";
      short  fontSize  = 24;
      string  fontColor  = "FF0000"; // --- Red
      string  charValue  = "F";    // --- Solid filled push pin

      this.MapviewService.PlaceLocationPinAtPoint(
                    point,
                    pointType,
                    fontName,
                    fontSize,
                    fontColor,
                    charValue,
                    "POI");
      StopCommand( );
    }

    private void StopCommand()
    {
      d.WriteLine( string.Format( "{0}.{1} ({2}):{3}", GetType().Name, System.Reflection.MethodInfo.GetCurrentMethod().Name, Global.CallCount++, string.Empty));

      RestoreMapMode  ( );
      ReleaseEventSink( );

      this.MapviewService.SetStatusBar( string.Empty);
   }

    private void RestoreMapMode( )
    {
      d.WriteLine( string.Format( "{0}.{1} ({2}):{3}", GetType().Name, System.Reflection.MethodInfo.GetCurrentMethod().Name, Global.CallCount++, string.Empty));

      this.MapviewService.SetMapMode( this.OldMapLocateMode);
    }

    private void ReleaseEventSink( )
    {
      d.WriteLine( string.Format( "{0}.{1} ({2}):{3}", GetType().Name, System.Reflection.MethodInfo.GetCurrentMethod().Name, Global.CallCount++, string.Empty));

      this.MapviewService.RedlineLButtonDown -= new Intergraph.Controls.oneViewer.RedlineButtonEventHandler( MapviewService_RedlineLButtonDown );
    }
  }
}

After running this command, a pinpoint is placed to indicate some point of interest :

pinpoint
pinpoint

You can download the complete source for this example over here.

Related Posts

Increasing G/Netviewer performance

Note: The monitoring and tests are done on G/Netviewer 9.4 environment. But I think it is also applicable for version 10. After some in-depth monitoring of our G/Netviewer webserver for…

Debugging G/Technology 10.2 with Visual Studio

In an earlier post Stephan mentions that it was not possible to debug G/Technology 10.2 in “edit and continue” mode. You could only attach Visual Studio to the G3E.exe…

Debugging Netviewer serverside pages

If you need to build server side pages in Netviewer, changes are likely that you want to debug them. First thing to do is to change the…

Product components (lower part)

Debugging Oracle PL/SQL from Visual Studio

I use TOAD for my day-to-day Oracle development, but I find it’s debugging experience very poor compared to Visual Studio. Using ODP.Net version 11 you can use…

This Post Has 6 Comments

  1. I have a Gcomms Custom Command that I want to also include in NetViewer. I am having difficulty making this work. My custom command pops up a window for the user to type information into. Do you have any tips on how to do this?

    1. Custom Commands don’t work in NetViewer, Netviewer has a complete different programming model so you need to convert your custom command into a Netviewer Extension to get it to work.

  2. Here is my skeleton code. This is my very first Gcomms Custom Command. It’s in two files denoted by the filenames. This code pops up a window and allows the user to type in an address, intersection, POI or business category and it zooms them to that location inside Gcomms. If the query returns more than one result(POI and Category Searches) the results are listed out with LinkLabels for the user to click and choose whichever they want.

    My problem is that I have never setup a NetViewer Custom Command before and have not idea how to start. Using your blog post here I have been able to move around the buttons on toolbars but not been able to add the custom command button as you show. I have also took the example code, added the references and built it. I have moved the resulting dll to the Gcomms Program folder.

    I have tried to use your example code and model the Gcomms Custom Command after it, but I have not had any luck with it.

    –AddressSearch.cs——————————————
    using System;
    using System.Collections.Generic;
    using System.Collections;
    using System.IO;
    using System.Net;
    using System.Text;
    using System.Windows.Forms;
    using System.Drawing;

    namespace NameSpace1
    {
    public partial class frmAddressSearch : Form
    {
    //Code here
    LatLongZoom latlongzomm = new LatLongZoom();

    public frmAddressSearch()
    {
    InitializeComponent();
    this.Activate();
    this.Show();
    }

    private void btnSearch_Click(object sender, EventArgs e)
    {
    //build data to send to requestdata()
    RequestData(data.ToString());

    }

    public void RequestData(string data)
    {
    //request lat/long and determine what to do with it
    }
    private void frmAddressSearch_Load(object sender, EventArgs e)
    {
    //initilization stuff here
    }

    private void llResponse1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
    //do this when link is clicked
    }

    private void llResponse2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
    //do this when link is clicked
    }

    private void llResponse3_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
    //do this when link is clicked
    }

    private void llResponse4_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
    //do this when link is clicked
    }

    private void llResponse5_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
    //do this when link is clicked
    }

    }
    }
    –LatLongZoom.cs——————————-
    using System;
    using System.Windows.Forms;
    using System.ComponentModel;
    using System.Collections.Generic;
    using Intergraph.GTechnology.API;
    using Intergraph.GTechnology.Interfaces;
    using Intergraph.CoordSystems;

    namespace NameSpace1
    {
    class LatLongZoom : IGTCustomCommandModal

    {

    private IGTTransactionManager m_oTransactionManager = null;
    IGTApplication GTapplication = GTClassFactory.Create();

    public IGTTransactionManager TransactionManager
    {
    get { return m_oTransactionManager; }
    set { m_oTransactionManager = value; }
    }

    public void Activate()
    {
    Application.EnableVisualStyles();
    Application.Run(new frmAddressSearch());
    }

    public void ZoommToLatLong(double lat,double lon)
    {

    IGTMapWindow oWindow = GTapplication.ActiveMapWindow;

    //Convert Lat/Long to Gcomms Points
    List XYZList = convertLatLongToProjectionCoord(lon,lat);

    //assign converted lat long to points

    //create screen extent from points

    //Zoom to extent

    }

    public List convertLatLongToProjectionCoord(double Lon, double Lat)
    {
    //Convert Lat/long

    }
    }
    }

  3. James,

    this is a custom command which will only work on G/Technology, if you want to customize Netviewer you need to create a new type of extension dedicated to Netviewer-client, a Netviewer extension. Do you have a working netviewer environment, and followed the instructions I gave in my Blog Post about netviewer extensions ?

  4. I tried to use your example code to change my Gcomms Custom Command so it would work with NetViewer but I am running into issues with it. I think the main issue is where I create an object of the class LatLongZoom. In the gcomms code the constructor takes zero arguments, but after I changed it to match your example it takes [ ServiceDependency()] WorkItem rootWorkItem as an argument.

    I then tried to just use your example code and have it run in NetViewer. I was not able to get the custom extension button to show up in the toolbar. I was able to move around the already existing buttons so I know I am editing the correct file.

    1. Hi James,

      This is really a mater of C# coding, if you have it running you should be able to call a constructor and further develop it.

      If you really can’t get it to work mail to stephan.deckers@merkator.info then I’ll see what I can do.

      regards, Stephan

Leave a Reply

Your email address will not be published. Required fields are marked *