Archive for April, 2008

Using SpatialAce in WPF applications

Tuesday, April 29th, 2008

It’s been a long while since I blogged about SpatialAce. It’s even got a new name since the last time i wrote about it: Carmenta Engine. So I thought I’d write one last post about this excellent GIS map engine; this time about using it in a Windows Presentation Foundation (WPF) application.

Since Carmenta Engine is an ActiveX control, you need to use a WindowsFormsHost wrapper class. I will show a technique where we will create an intermediate WPF wrapper that will enable us to implement a few custom dependency properties that we can use for setting configurations files and view names in XAML.

Start by adding a new WPF Library project to your solution. Name the new project CarmentaEngine. Then add a new Windows Forms UserControl and name it CarmentaEngineFormsControl. Make sure that you choose a Windows Forms control, not the WPF equivalent. In this UserControl, you drop your Carmenta Engine ActiveX control and set it’s dock property to fill.

Now create a WPF UserControl in the same project and drop a WindowsFormsHost into it. Name the WPF UserControl CarmentaEngineControl. We can now add the just created Windows Forms UserControl to the WPF UserControl. Sounds a little complicated? Well, the benefit here is that we can implement dependency properties for Carmenta Engine in the WPF UserControl that we can use from XAML later.

The file CarmentaEngineControl.xaml should look something like this:

  <UserControl x:Class="CarmentaEngine.CarmentaEngineControl"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:my="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
      xmlns:code="clr-namespace:CarmentaEngine"
      Height="300" Width="300" >
      <Grid>
          <my:WindowsFormsHost Name="windowsFormsHost1">
              <code:CarmentaEngineFormsControl x:Name="carmentaEngineFormsControl"/>
          </my:WindowsFormsHost>
      </Grid>
  </UserControl>


We’d like to expose the ConfigurationFile and ViewName property of the Carmenta Engine ActiveX control as dependency properties. This is very straightforward code, the only thing to look out for is that we should not set the property values until we have both of them. Setting ViewName without a ConfigurationFile value does not make sense. Here’s the code behind from CarmentaEngineControl.xaml.cs:

  public partial class CarmentaEngineControl : UserControl {

      public static readonly DependencyProperty ConfigurationFileProperty;
      public static readonly DependencyProperty ViewNameProperty;

      private string viewName = null;
      private string path = null;

      public CarmentaEngineControl() {
          InitializeComponent();
      }

      static CarmentaEngineControl() {
          ConfigurationFileProperty = DependencyProperty.Register
              ("ConfigurationFile", typeof (string), typeof (CarmentaEngineControl),
               new FrameworkPropertyMetadata
               (null, FrameworkPropertyMetadataOptions.None, OnConfigurationFileChanged));

          ViewNameProperty = DependencyProperty.Register
              ("ViewName", typeof(string), typeof(CarmentaEngineControl),
               new FrameworkPropertyMetadata
               (null, FrameworkPropertyMetadataOptions.None, OnViewNameChanged));
      }

      private static void OnViewNameChanged
          (DependencyObject d, DependencyPropertyChangedEventArgs e) {
          CarmentaEngineControl control = d as CarmentaEngineControl;
          if (control != null) {
              control.viewName = (string) e.NewValue;
              control.InitializeEngine();
          }
      }

      private static void OnConfigurationFileChanged
          (DependencyObject d, DependencyPropertyChangedEventArgs e) {
          CarmentaEngineControl control = d as CarmentaEngineControl;
          if (control != null) {
              control.path = (string) e.NewValue;
              control.InitializeEngine();
          }
      }

      public string ConfigurationFile {
          get { return (string) GetValue(ConfigurationFileProperty); }
          set { SetValue(ConfigurationFileProperty, value); }
      }

      public string ViewName {
          get { return (string)GetValue(ViewNameProperty); }
          set { SetValue(ViewNameProperty, value); }
      }

      private void InitializeEngine() {
          if (path != null && viewName != null) {

              FileInfo fileInfo = new FileInfo(path);
              if (fileInfo.Exists) {
                  AxSpaceX spaceX = carmentaEngineFormsControl.axSpaceX1;
                  spaceX.ConfigurationFile = path;
                  spaceX.NameSpace = Path.GetFileNameWithoutExtension(path);
                  spaceX.ViewName = viewName;

                  spaceX.Tool = new RoamToolClass();
              }
          }
      }
  }


That’s basically all there is to it. Now, in your original WPF application project, reference the WPF Library project from above and add the following code to your XAML:

  <CarmentaEngine:CarmentaEngineControl
            Margin="31,0,475.75,19" Height="177" VerticalAlignment="Bottom"
            ConfigurationFile="path to configuration file"
            ViewName="the name of your view"/>


Don’t forget to reference the CarmentaEngine project in your header:

  xmlns:CarmentaEngine="clr-namespace:CarmentaEngine;assembly=CarmentaEngine"


Since last week, I no longer work at Carmenta so I can’t verify the syntax. I based the code on some notes on how I did this a few weeks ago and I hope I got all the details right. I should also say that this was my first encounter with WPF and I found the MSDN documentation to be a little terse. In other words: there might be syntax errors above and better ways to do this, but I found the technique to have a separate WPF library with custom dependency properties to be pretty powerful.