It doesn’t take much to create a Netviewer extension:
- Derive a class from
Microsoft.Practices.CompositeUI.ModuleInit
- Create a constructor with a
[ServiceDependency()] WorkItem
argument - 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.
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:
- A reference to the Netviewer MapviewService instance for setting up eventhandlers
- A Commandhandler for a left-mouse click
- 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 :
You can download the complete source for this example over here.