ArcObjects Library Reference  

MyDynamicLayerClass

About the Dynamic display layer Sample

[C#]

MyDynamicLayerClass.cs

using System;
using System.Data;
using System.Drawing;
using System.Timers;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.SystemUI;
using ESRI.ArcGIS.ADF;

namespace MyDynamicLayer
{
  /// <summary>
  /// This layer implements a DynamicLayer based on the DynamicLayerBase base-class
  /// </summary>
  [Guid("27FB44EB-0426-415f-BFA3-D1581056C0C4")]
  [ComVisible(true)]
  [ClassInterface(ClassInterfaceType.None)]
  [ProgId("MyDynamicLayer.MyDynamicLayerClass")]
  public sealed class MyDynamicLayerClass : ESRI.ArcGIS.ADF.BaseClasses.BaseDynamicLayer
  {
    #region class members
    private IPoint                    m_point                   = null;
    private bool                      m_bDynamicGlyphsCreated   = false;
    private bool                      m_bOnce                   = true;
    private IDynamicGlyph[]           m_markerGlyphs            = null;
    private IDynamicGlyph             m_textGlyph               = null;
    private Timer                     m_updateTimer             = null;
    private double                    m_extentMaxX              = 1000.0;
    private double                    m_extentMaxY              = 1000.0;
    private double                    m_extentMinX              = 0.0;
    private double                    m_extentMinY              = 0.0;
    private IDynamicGlyphFactory2      m_dynamicGlyphFactory     = null;
    private IDynamicSymbolProperties  m_dynamicSymbolProps      = null;
    private IDynamicCompoundMarker    m_dynamicCompoundMarker   = null;
		private DataTable									m_table										= null;
		private const int									m_nNumOfItems							= 100;
    #endregion

    #region class constructor
    public MyDynamicLayerClass() : base()
    {
			m_table = new DataTable ("rows");
			m_table.Columns.Add("OID",			typeof (int));	//0
      m_table.Columns.Add("X",        typeof(double)); //1
      m_table.Columns.Add("Y",        typeof(double)); //2
      m_table.Columns.Add("STEPX",    typeof(double)); //3
      m_table.Columns.Add("STEPY",    typeof(double)); //4
      m_table.Columns.Add("HEADING",  typeof(double)); //5
      m_table.Columns.Add("TYPE",     typeof(int));    //6

      //set the ID column to be AutoIncremented
      m_table.Columns[0].AutoIncrement = true;

      m_point = new PointClass();

      //set an array to store the glyphs used to symbolize the tracked object
      m_markerGlyphs = new IDynamicGlyph[3];

      //set the update timer for the layer
      m_updateTimer = new Timer(50);
      m_updateTimer.Enabled = false;
      m_updateTimer.Elapsed += new ElapsedEventHandler(OnLayerUpdateEvent);

    }
    #endregion

    #region overriden methods
    /// <summary>
    /// The dynamic layer draw method
    /// </summary>
    /// <param name="DynamicDrawPhase">the current drawphase of the dynamic drawing</param>
    /// <param name="Display">the ActiveView's display</param>
    /// <param name="DynamicDisplay">the ActiveView's dynamic display</param>
    public override void DrawDynamicLayer(ESRI.ArcGIS.Display.esriDynamicDrawPhase DynamicDrawPhase, ESRI.ArcGIS.Display.IDisplay Display, ESRI.ArcGIS.Display.IDynamicDisplay DynamicDisplay)
    {
      try
      {
        //make sure that the display is valid as well as that the layer is visible
        if (null == DynamicDisplay || null == Display || !this.m_visible)
          return;

        //make sure that the current drawphase is immediate. In this sample there is no use of the
        //compiled drawPhase. Use the esriDDPCompiled drawPhase in order to draw semi-static items (items
        //which have update rate lower than the display update rate).
				if (DynamicDrawPhase != esriDynamicDrawPhase.esriDDPImmediate)
          return;

        if (m_bOnce)
        {
          //cast the DynamicDisplay into DynamicGlyphFactory
          m_dynamicGlyphFactory = DynamicDisplay.DynamicGlyphFactory as IDynamicGlyphFactory2;
          //cast the DynamicDisplay into DynamicSymbolProperties
          m_dynamicSymbolProps = DynamicDisplay as IDynamicSymbolProperties;
          //cast the compound marker symbol
          m_dynamicCompoundMarker = DynamicDisplay as IDynamicCompoundMarker;

					IntializeLayerData (Display.DisplayTransformation);

          GetLayerExtent();

          m_bOnce = false;
        }

        //get the display fitted bounds
        m_extentMaxX = Display.DisplayTransformation.FittedBounds.XMax;
        m_extentMaxY = Display.DisplayTransformation.FittedBounds.YMax;
        m_extentMinX = Display.DisplayTransformation.FittedBounds.XMin;
        m_extentMinY = Display.DisplayTransformation.FittedBounds.YMin;

        //create the dynamic symbols for the layer
        if (!m_bDynamicGlyphsCreated)
        {
          this.CreateDynamicSymbols(m_dynamicGlyphFactory);
          m_bDynamicGlyphsCreated = true;
        }

        double X, Y, heading;
        int type;
        //iterate through the layers' items
        foreach (DataRow r in m_table.Rows)
        {
          if (r[1] is DBNull || r[2] is DBNull)
            continue;

          //get the item's coordinate, heading and type
          X = Convert.ToDouble(r[1]);
          Y = Convert.ToDouble(r[2]);
          heading = Convert.ToDouble(r[5]);
          type = Convert.ToInt32(r[6]);

          //assign the items' coordinate to the cached point
          m_point.PutCoords(X, Y);

          //set the symbol's properties
          switch (type)
          {
            case 0:
              //set the heading of the current symbols' text
              m_dynamicSymbolProps.set_Heading(esriDynamicSymbolType.esriDSymbolText, 0.0f);
              m_dynamicSymbolProps.set_Heading(esriDynamicSymbolType.esriDSymbolMarker, 0.0f);

              //set the symbol alignment so that it will align with the screen
              m_dynamicSymbolProps.set_RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker, esriDynamicSymbolRotationAlignment.esriDSRAScreen);

              //set the text alignment so that it will also align with the screen
              m_dynamicSymbolProps.set_RotationAlignment(esriDynamicSymbolType.esriDSymbolText, esriDynamicSymbolRotationAlignment.esriDSRAScreen);

              //scale the item
              m_dynamicSymbolProps.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 0.8f, 0.8f);
              //set the items' color (blue)
              m_dynamicSymbolProps.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 0.0f, 0.0f, 1.0f, 1.0f); // Blue
              //assign the item's glyph to the dynamic-symbol
              m_dynamicSymbolProps.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker, m_markerGlyphs[0]);
              //set the labels text glyph
              m_dynamicSymbolProps.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolText, m_textGlyph);
              //set the color of the text
              m_dynamicSymbolProps.SetColor(esriDynamicSymbolType.esriDSymbolText, 1.0f, 1.0f, 0.0f, 1.0f); // Yellow

              //draw the item as a compound marker. This means that you do not have to draw the items and their
              //accompanying labels separately, and thus allow you to write less code as well as get better
              //performance.  
              m_dynamicCompoundMarker.DrawCompoundMarker4
                (m_point,
                //"TOP",
                //"BOTTOM",
                "Item " + Convert.ToString(r[0]),
                heading.ToString("###.##"),
                m_point.X.ToString("###.#####"),
                m_point.Y.ToString("###.#####"));
              break;
            case 1:
              //set the heading of the current symbol
              m_dynamicSymbolProps.set_Heading(esriDynamicSymbolType.esriDSymbolMarker, (float)heading);

              //set the symbol alignment so that it will align with towards the symbol heading
              m_dynamicSymbolProps.set_RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker, esriDynamicSymbolRotationAlignment.esriDSRANorth);

              m_dynamicSymbolProps.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f);
              m_dynamicSymbolProps.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 0.0f, 1.0f, 0.6f, 1.0f); // GREEN
              m_dynamicSymbolProps.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker, m_markerGlyphs[1]);

              //draw the current location
              DynamicDisplay.DrawMarker(m_point);
              break;
            case 2:
              //set the heading of the current symbol
              m_dynamicSymbolProps.set_Heading(esriDynamicSymbolType.esriDSymbolMarker, (float)heading);

              //set the symbol alignment so that it will align with towards the symbol heading
              m_dynamicSymbolProps.set_RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker, esriDynamicSymbolRotationAlignment.esriDSRANorth);

              m_dynamicSymbolProps.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 1.1f, 1.1f);
              m_dynamicSymbolProps.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f, 1.0f, 1.0f); // WHITE
              m_dynamicSymbolProps.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker, m_markerGlyphs[2]);

              //draw the current location
              DynamicDisplay.DrawMarker(m_point);
              break;
          }
        }

       // by setting immediate flag to false, we signal the dynamic display that the layer is current.
        base.m_bIsImmediateDirty = false;
				
      }     
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }
		
    /// <summary>
    /// Returns the UID (ProgID or CLSID)
    /// </summary>
    public override UID ID
    {
      get
      {
        m_uid.Value = "MyDynamicLayer.MyDynamicLayerClass";
        return m_uid;
      }
    }
    #endregion

    #region public methods
    public void Connect()
    {
      m_updateTimer.Enabled = true;
    }

    public void Disconnect()
    {
      m_updateTimer.Enabled = false;
    }

		public DataRow NewItem ()
		{
			if (m_table == null)
				return null;
			else
				return m_table.NewRow ();
		}

		public void AddItem (DataRow row)
		{
			if (row == null)
				return;
			else
				m_table.Rows.Add (row);
		}
    #endregion

    #region private utility methods
    /// <summary>
    /// create the layer's glyphs used to set the symbol of the dynamic-layer items
    /// </summary>
    /// <param name="pDynamicGlyphFactory"></param>
    private void CreateDynamicSymbols(IDynamicGlyphFactory2 pDynamicGlyphFactory)
    {
      try
      {
        //set the background color
				IColor color = ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor (Color.FromArgb (255, 255, 255)) as IColor;

        // Create Character Marker Symbols glyph
        // --------------------------------------
				ICharacterMarkerSymbol characterMarkerSymbol = new CharacterMarkerSymbolClass ();
				characterMarkerSymbol.Color = color as IColor;
                characterMarkerSymbol.Font = ESRI.ArcGIS.ADF.Connection.Local.Converter.ToStdFont(new Font("ESRI Environmental & Icons", 32));
				characterMarkerSymbol.Size = 40;
				characterMarkerSymbol.Angle = 0;
				characterMarkerSymbol.CharacterIndex = 36;

        //create the glyph from the marker symbol
        m_markerGlyphs[0] = pDynamicGlyphFactory.CreateDynamicGlyph(characterMarkerSymbol as ISymbol);

        characterMarkerSymbol.Size = 32;
        characterMarkerSymbol.CharacterIndex = 224;

        //create the glyph from the marker symbol
        m_markerGlyphs[1] = pDynamicGlyphFactory.CreateDynamicGlyph(characterMarkerSymbol as ISymbol);

        // Create the glyph from embedded bitmap
        // -----------------------------------
        // Sets the transparency color
        IColor transparentColor = ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.FromArgb(255, 255, 255)) as IColor;

				Bitmap bitmap = new Bitmap (GetType (), "B2.bmp");
				m_markerGlyphs[2] = pDynamicGlyphFactory.CreateDynamicGlyphFromBitmap(esriDynamicGlyphType.esriDGlyphMarker, bitmap.GetHbitmap ().ToInt32 (), false, transparentColor);
     
        // Create a glyph for the labels text, use the first 'internal' text glyph 
        // ------------------------------------------------------------------------
        m_textGlyph = pDynamicGlyphFactory.get_DynamicGlyph(1, esriDynamicGlyphType.esriDGlyphText, 1);

      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }
    
    /// <summary>
    /// timer elapsed event handler, used to update the layer
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    /// <remarks>This layer has synthetic data, and therefore need the timer in order
    /// to update the layers' items.</remarks>
    private void OnLayerUpdateEvent(object sender, ElapsedEventArgs e)
    {
      try
      {
        double X, Y, stepX, stepY, heading;

        //iterate through the layers' records
				foreach (DataRow r in m_table.Rows)
        {
          if (r[1] is DBNull || r[2] is DBNull)
            continue;

          //get the current item location and the item's steps
          X = Convert.ToDouble(r[1]);
          Y = Convert.ToDouble(r[2]);
          stepX = Convert.ToDouble(r[3]);
          stepY = Convert.ToDouble(r[4]);

          //increment the item's location
          X += stepX;
          Y += stepY;

          //test that the item's location is within the fitted bounds
          if (X > m_extentMaxX) stepX = -Math.Abs(stepX);
          if (X < m_extentMinX) stepX = Math.Abs(stepX);
          if (Y > m_extentMaxY) stepY = -Math.Abs(stepY);
          if (Y < m_extentMinY) stepY = Math.Abs(stepY);
          //calculate the item's heading
          heading = (360.0 + 90.0 - Math.Atan2(stepY, stepX) * 180 / Math.PI) % 360.0;

          //update the item's record
          r[1] = X;
          r[2] = Y;
          r[3] = stepX;
          r[4] = stepY;
          r[5] = heading;
          lock (m_table)
          {
            r.AcceptChanges();
          }

					//set the dirty flag to true in order to let the DynamicDisplay that the layer needs redraw.
					base.m_bIsImmediateDirty = true;
        }
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }

    /// <summary>
    /// Calculates the layer extent
    /// </summary>
    private void GetLayerExtent()
    {
      if (null == m_table)
        return;

      IEnvelope env = new EnvelopeClass();
      env.SpatialReference = base.m_spatialRef;
      IPoint point = new PointClass();
			foreach (DataRow r in m_table.Rows)
      {
        if (r[1] is DBNull || r[2] is DBNull)
          continue;

        point.Y = Convert.ToDouble(r[3]);
        point.X = Convert.ToDouble(r[4]);

        env.Union(point.Envelope);
      }

      base.m_extent = env;
    }

		/// <summary>
		/// Initialize the synthetic data of the layer
		/// </summary>
		private void IntializeLayerData (IDisplayTransformation displayTransformation)
		{
			try
			{

				//get the map's fitted bounds
				IEnvelope extent = displayTransformation.FittedBounds;

				//calculate the step for which will be used to increment the items
				double XStep = extent.Width / 5000.0;
				double YStep = extent.Height / 5000.0;

				Random rnd = new Random ();
				double stepX, stepY;

				//generate the items
				for (int i = 0; i < m_nNumOfItems; i++)
				{
					//calculate the step for each item
					stepX = XStep * rnd.NextDouble ();
					stepY = YStep * rnd.NextDouble ();

					//create new record
					DataRow r = NewItem ();
					//set the item's coordinate
					r[1] = extent.XMin + rnd.NextDouble () * (extent.XMax - extent.XMin);
					r[2] = extent.YMin + rnd.NextDouble () * (extent.YMax - extent.YMin);
					//set the item's steps
					r[3] = stepX;
					r[4] = stepY;
					//calculate the heading
					r[5] = (360.0 + 90.0 - Math.Atan2 (stepY, stepX) * 180 / Math.PI) % 360.0;

					//add a type ID in order to define the symbol for the item
					switch (i % 3)
					{
						case 0:
							r[6] = 0;
							break;
						case 1:
							r[6] = 1;
							break;
						case 2:
							r[6] = 2;
							break;
					}

					//add the new item record to the table
					AddItem (r);
				}
			}
			catch (Exception ex)
			{
				System.Diagnostics.Trace.WriteLine (ex.Message);
			}
		}
    #endregion

  }
}

[Visual Basic .NET]

MyDynamicLayerClass.vb

Imports Microsoft.VisualBasic
Imports System
Imports System.Data
Imports System.Drawing
Imports System.Timers
Imports System.Runtime.InteropServices
Imports System.Collections.Generic
Imports ESRI.ArcGIS.ADF.CATIDs
Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.Display
Imports ESRI.ArcGIS.esriSystem
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.SystemUI
Imports ESRI.ArcGIS.ADF

  ''' <summary>
  ''' This layer implements a DynamicLayer based on the DynamicLayerBase base-class
  ''' </summary>
  <Guid("27FB44EB-0426-415f-BFA3-D1581056C0C4"), ComVisible(True), ClassInterface(ClassInterfaceType.None), ProgId("MyDynamicLayerClass")> _
  Public NotInheritable Class MyDynamicLayerClass : Inherits ESRI.ArcGIS.ADF.BaseClasses.BaseDynamicLayer
	
	#Region "class members"
	Private m_point As IPoint = Nothing
	Private m_bDynamicGlyphsCreated As Boolean = False
	Private m_bOnce As Boolean = True
	Private m_markerGlyphs As IDynamicGlyph() = Nothing
	Private m_textGlyph As IDynamicGlyph = Nothing
	Private m_updateTimer As Timer = Nothing
	Private m_extentMaxX As Double = 1000.0
	Private m_extentMaxY As Double = 1000.0
	Private m_extentMinX As Double = 0.0
	Private m_extentMinY As Double = 0.0
	Private m_dynamicGlyphFactory As IDynamicGlyphFactory2 = Nothing
	Private m_dynamicSymbolProps As IDynamicSymbolProperties = Nothing
	Private m_dynamicCompoundMarker As IDynamicCompoundMarker = Nothing
	Private m_table As DataTable = Nothing
	Private Const m_nNumOfItems As Integer = 100
	#End Region

	#Region "class constructor"
	Public Sub New()
		MyBase.New()
			m_table = New DataTable ("rows")
			m_table.Columns.Add("OID", GetType(Integer)) '0
	  m_table.Columns.Add("X", GetType(Double)) '1
	  m_table.Columns.Add("Y", GetType(Double)) '2
	  m_table.Columns.Add("STEPX", GetType(Double)) '3
	  m_table.Columns.Add("STEPY", GetType(Double)) '4
	  m_table.Columns.Add("HEADING", GetType(Double)) '5
	  m_table.Columns.Add("TYPE", GetType(Integer)) '6

	  'set the ID column to be AutoIncremented
	  m_table.Columns(0).AutoIncrement = True

	  m_point = New PointClass()

	  'set an array to store the glyphs used to symbolize the tracked object
	  m_markerGlyphs = New IDynamicGlyph(2){}

	  'set the update timer for the layer
	  m_updateTimer = New Timer(50)
	  m_updateTimer.Enabled = False
	  AddHandler m_updateTimer.Elapsed, AddressOf OnLayerUpdateEvent

	End Sub
	#End Region

#Region "overridden methods"
	''' <summary>
	''' The dynamic layer draw method
	''' </summary>
	''' <param name="DynamicDrawPhase">the current drawphase of the dynamic drawing</param>
	''' <param name="Display">the ActiveView's display</param>
	''' <param name="DynamicDisplay">the ActiveView's dynamic display</param>
	Public Overrides Sub DrawDynamicLayer(ByVal DynamicDrawPhase As ESRI.ArcGIS.Display.esriDynamicDrawPhase, ByVal Display As ESRI.ArcGIS.Display.IDisplay, ByVal DynamicDisplay As ESRI.ArcGIS.Display.IDynamicDisplay)
	  Try
			'make sure that the display is valid as well as that the layer is visible
			If Nothing Is DynamicDisplay OrElse Nothing Is Display OrElse (Not Me.m_visible) Then
				Return
			End If

			'make sure that the current drawphase is immediate. In this sample there is no use of the
			'compiled drawPhase. Use the esriDDPCompiled drawPhase in order to draw semi-static items (items
			'which have update rate lower than the display update rate).
			If DynamicDrawPhase <> esriDynamicDrawPhase.esriDDPImmediate Then
				Return
			End If

			If m_bOnce Then
				'cast the DynamicDisplay into DynamicGlyphFactory
				m_dynamicGlyphFactory = TryCast(DynamicDisplay.DynamicGlyphFactory, IDynamicGlyphFactory2)
				'cast the DynamicDisplay into DynamicSymbolProperties
				m_dynamicSymbolProps = TryCast(DynamicDisplay, IDynamicSymbolProperties)
				'cast the compound marker symbol
				m_dynamicCompoundMarker = TryCast(DynamicDisplay, IDynamicCompoundMarker)

				IntializeLayerData (Display.DisplayTransformation)
				GetLayerExtent()

				m_bOnce = False
			End If

			'get the display fitted bounds
			m_extentMaxX = Display.DisplayTransformation.FittedBounds.XMax
			m_extentMaxY = Display.DisplayTransformation.FittedBounds.YMax
			m_extentMinX = Display.DisplayTransformation.FittedBounds.XMin
			m_extentMinY = Display.DisplayTransformation.FittedBounds.YMin

			'create the dynamic symbols for the layer
			If (Not m_bDynamicGlyphsCreated) Then
				Me.CreateDynamicSymbols(m_dynamicGlyphFactory)
				m_bDynamicGlyphsCreated = True
			End If

			Dim X, Y, heading As Double
			Dim type As Integer
			'iterate through the layers' items
			For Each r As DataRow In m_table.Rows
				If TypeOf r(1) Is DBNull OrElse TypeOf r(2) Is DBNull Then
					Continue For
				End If

				'get the item's coordinate, heading and type
				X = Convert.ToDouble(r(1))
				Y = Convert.ToDouble(r(2))
				heading = Convert.ToDouble(r(5))
				type = Convert.ToInt32(r(6))

				'assign the items' coordinate to the cached point
				m_point.PutCoords(X, Y)

				'set the symbol's properties
				Select Case type
				Case 0
					'set the heading of the current symbols' text
					m_dynamicSymbolProps.Heading(esriDynamicSymbolType.esriDSymbolText)= 0.0f
					m_dynamicSymbolProps.Heading(esriDynamicSymbolType.esriDSymbolMarker)= 0.0f

					'set the symbol alignment so that it will align with the screen
					m_dynamicSymbolProps.RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker)=esriDynamicSymbolRotationAlignment.esriDSRAScreen

					'set the text alignment so that it will also align with the screen
					m_dynamicSymbolProps.RotationAlignment(esriDynamicSymbolType.esriDSymbolText)= esriDynamicSymbolRotationAlignment.esriDSRAScreen

					'scale the item
					m_dynamicSymbolProps.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 0.8f, 0.8f)
					'set the items' color (blue)
					m_dynamicSymbolProps.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 0.0f, 0.0f, 1.0f, 1.0f) ' Blue
					'assign the item's glyph to the dynamic-symbol
					m_dynamicSymbolProps.DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker)= m_markerGlyphs(0)
					'set the labels text glyph
					m_dynamicSymbolProps.DynamicGlyph(esriDynamicSymbolType.esriDSymbolText)= m_textGlyph
					'set the color of the text
					m_dynamicSymbolProps.SetColor(esriDynamicSymbolType.esriDSymbolText, 1.0f, 1.0f, 0.0f, 1.0f) ' Yellow

                        'draw the item as a compound marker. This means that you do not have to draw the items and their
					'accompanying labels separately, and thus allow you to write less code as well as get better
					'performance.  
					'"TOP",
					'"BOTTOM",
					m_dynamicCompoundMarker.DrawCompoundMarker4 (m_point, "Item " & Convert.ToString(r(0)), heading.ToString("###.##"), m_point.X.ToString("###.#####"), m_point.Y.ToString("###.#####"))
				Case 1
					'set the heading of the current symbol
					m_dynamicSymbolProps.Heading(esriDynamicSymbolType.esriDSymbolMarker)= CSng(heading)

				 'set the symbol alignment so that it will align with towards the symbol heading
					m_dynamicSymbolProps.RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker)= esriDynamicSymbolRotationAlignment.esriDSRANorth

					m_dynamicSymbolProps.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f)
					m_dynamicSymbolProps.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 0.0f, 1.0f, 0.6f, 1.0f) ' GREEN
					m_dynamicSymbolProps.DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker)= m_markerGlyphs(1)

					'draw the current location
					DynamicDisplay.DrawMarker(m_point)
				Case 2
					'set the heading of the current symbol
					m_dynamicSymbolProps.Heading(esriDynamicSymbolType.esriDSymbolMarker)= CSng(heading)

					'set the symbol alignment so that it will align with towards the symbol heading
					m_dynamicSymbolProps.RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker)= esriDynamicSymbolRotationAlignment.esriDSRANorth

					m_dynamicSymbolProps.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 1.1f, 1.1f)
					m_dynamicSymbolProps.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f, 1.0f, 1.0f) ' WHITE
					m_dynamicSymbolProps.DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker)= m_markerGlyphs(2)

					'draw the current location
					DynamicDisplay.DrawMarker(m_point)
				End Select
			Next r

			' by setting immediate flag to false, we signal the dynamic display that the layer is current.
			MyBase.m_bIsImmediateDirty = False

	  Catch ex As Exception
			System.Diagnostics.Trace.WriteLine(ex.Message)
	  End Try
	End Sub

	''' <summary>
	''' Returns the UID (ProgID or CLSID)
	''' </summary>
	Public Overrides ReadOnly Property ID() As UID
	  Get
			m_uid.Value = "MyDynamicLayer.MyDynamicLayerClass"
			Return m_uid
	  End Get
	End Property
	#End Region

	#Region "public methods"
	Public Sub Connect()
	  m_updateTimer.Enabled = True
	End Sub

	Public Sub Disconnect()
	  m_updateTimer.Enabled = False
	End Sub

		Public Function NewItem() As DataRow
			If m_table Is Nothing Then
				Return Nothing
			Else
				Return m_table.NewRow ()
			End If
		End Function

		Public Sub AddItem(ByVal row As DataRow)
			If row Is Nothing Then
				Return
			Else
				m_table.Rows.Add (row)
			End If
		End Sub
	#End Region

	#Region "private utility methods"
	''' <summary>
	''' create the layer's glyphs used to set the symbol of the dynamic-layer items
	''' </summary>
	''' <param name="pDynamicGlyphFactory"></param>
	Private Sub CreateDynamicSymbols(ByVal pDynamicGlyphFactory As IDynamicGlyphFactory2)
	  Try
			'set the background color
			Dim backgroundColor As IColor 
            backgroundColor = ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.FromArgb(255, 255, 255))

			' Create Character Marker Symbols glyph
			' --------------------------------------
			Dim characterMarkerSymbol As ICharacterMarkerSymbol = New CharacterMarkerSymbolClass()
			characterMarkerSymbol.Color = TryCast(backgroundColor, IColor)
            characterMarkerSymbol.Font = ESRI.ArcGIS.ADF.Connection.Local.Converter.ToStdFont(New Font("ESRI Environmental & Icons", 32))
			characterMarkerSymbol.Size = 40
			characterMarkerSymbol.Angle = 0
			characterMarkerSymbol.CharacterIndex = 36

			'create the glyph from the marker symbol
			m_markerGlyphs(0) = pDynamicGlyphFactory.CreateDynamicGlyph(TryCast(characterMarkerSymbol, ISymbol))

			characterMarkerSymbol.Size = 32
			characterMarkerSymbol.CharacterIndex = 224

			'create the glyph from the marker symbol
			m_markerGlyphs(1) = pDynamicGlyphFactory.CreateDynamicGlyph(TryCast(characterMarkerSymbol, ISymbol))

			' Create the glyph from embedded bitmap
			' -----------------------------------
		  ' Sets the transparency color
		  Dim transparenyColor As IColor
            transparenyColor = ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.FromArgb(255, 255, 255))
		
			Dim bitmap as new Bitmap(me.GetType(), "B2.bmp")
		  m_markerGlyphs(2) = pDynamicGlyphFactory.CreateDynamicGlyphFromBitmap(esriDynamicGlyphType.esriDGlyphMarker,bitmap.GetHbitmap.ToInt32(),False,transparenyColor)

			' Create a glyph for the labels text, use the first 'internal' text glyph 
			' ------------------------------------------------------------------------
			m_textGlyph = pDynamicGlyphFactory.DynamicGlyph(1, esriDynamicGlyphType.esriDGlyphText, 1)

	  Catch ex As Exception
			System.Diagnostics.Trace.WriteLine(ex.Message)
	  End Try
	End Sub

	''' <summary>
	''' timer elapsed event handler, used to update the layer
	''' </summary>
	''' <param name="sender"></param>
	''' <param name="e"></param>
	''' <remarks>This layer has synthetic data, and therefore need the timer in order
	''' to update the layers' items.</remarks>
	Private Sub OnLayerUpdateEvent(ByVal sender As Object, ByVal e As ElapsedEventArgs)
	  Try
			Dim X, Y, stepX, stepY, heading As Double

		'iterate through the layers' records
			For Each r As DataRow In m_table.Rows
				If TypeOf r(1) Is DBNull OrElse TypeOf r(2) Is DBNull Then
					Continue For
				End If

				'get the current item location and the item's steps
				X = Convert.ToDouble(r(1))
				Y = Convert.ToDouble(r(2))
				stepX = Convert.ToDouble(r(3))
				stepY = Convert.ToDouble(r(4))

				'increment the item's location
				X += stepX
				Y += stepY

				'test that the item's location is within the fitted bounds
				If X > m_extentMaxX Then
					stepX = -Math.Abs(stepX)
				End If
				If X < m_extentMinX Then
					stepX = Math.Abs(stepX)
				End If
				If Y > m_extentMaxY Then
					stepY = -Math.Abs(stepY)
				End If
				If Y < m_extentMinY Then
					stepY = Math.Abs(stepY)
				End If
				'calculate the item's heading
				heading = (360.0 + 90.0 - Math.Atan2(stepY, stepX) * 180 / Math.PI) Mod 360.0

				'update the item's record
				r(1) = X
				r(2) = Y
				r(3) = stepX
				r(4) = stepY
				r(5) = heading
				SyncLock m_table
				r.AcceptChanges()
				End SyncLock

					'set the dirty flag to true in order to let the DynamicDisplay that the layer needs redraw.
					MyBase.m_bIsImmediateDirty = True
			Next r
	  Catch ex As Exception
			System.Diagnostics.Trace.WriteLine(ex.Message)
	  End Try
	End Sub

	''' <summary>
	''' Calculates the layer extent
	''' </summary>
	Private Sub GetLayerExtent()
	  If Nothing Is m_table Then
			Return
	  End If

	  Dim env As IEnvelope = New EnvelopeClass()
		env.SpatialReference = MyBase.m_spatialRef
	  Dim point As IPoint = New PointClass()
		For Each r As DataRow In m_table.Rows
			If TypeOf r(1) Is DBNull OrElse TypeOf r(2) Is DBNull Then
				Continue For
			End If

			point.Y = Convert.ToDouble(r(3))
			point.X = Convert.ToDouble(r(4))

			env.Union(point.Envelope)
		Next r

	  MyBase.m_extent = env
	End Sub

		''' <summary>
		''' Initialize the synthetic data of the layer
		''' </summary>
		Private Sub IntializeLayerData(ByVal displayTransformation As IDisplayTransformation)
			Try

				'get the map's fitted bounds
				Dim extent As IEnvelope = displayTransformation.FittedBounds

				'calculate the step for which will be used to increment the items
				Dim XStep As Double = extent.Width / 5000.0
				Dim YStep As Double = extent.Height / 5000.0

				Dim rnd As Random = New Random ()
				Dim stepX, stepY As Double

				'generate the items
				For i As Integer = 0 To m_nNumOfItems - 1
					'calculate the step for each item
					stepX = XStep * rnd.NextDouble ()
					stepY = YStep * rnd.NextDouble ()

					'create new record
					Dim r As DataRow = NewItem ()
					'set the item's coordinate
					r(1) = extent.XMin + rnd.NextDouble () * (extent.XMax - extent.XMin)
					r(2) = extent.YMin + rnd.NextDouble () * (extent.YMax - extent.YMin)
					'set the item's steps
					r(3) = stepX
					r(4) = stepY
					'calculate the heading
					r(5) = (360.0 + 90.0 - Math.Atan2 (stepY, stepX) * 180 / Math.PI) Mod 360.0

					'add a type ID in order to define the symbol for the item
					Select Case i Mod 3
						Case 0
							r(6) = 0
						Case 1
							r(6) = 1
						Case 2
							r(6) = 2
					End Select

					'add the new item record to the table
					AddItem (r)
				Next i
			Catch ex As Exception
				System.Diagnostics.Trace.WriteLine (ex.Message)
			End Try
		End Sub
	#End Region

  End Class