ArcGIS Progress – Showing Progress in ArcGIS Engine Application

arcgis-10.0arcgis-enginearcobjects

I would like to add a progress indicator to our ArcGIS Engine 10 application. Like a progressbar in the statusbar to show when the MapControl is busy getting or drawing the map. I've been trying to find any resources in the documentation on how to do this, but all resources I find is pointing to the MxStatusBar that is only in the ArcGIS Desktop. Does anyone know which events I should be using to create my own progress indicator?

Update: It's a standalone ArcGIS Engine Application. Built in VB.Net using WinForms. We've created the graphical animation for our busy indicator. So that we can start and stop the animation when there is progress. This was created using a dialog running in it's own thread because the MapControl block the redrawing of the form during it's draw phase. We've tried to hook up the busy indicator to AxMapControl1_OnBeforeScreenDraw and AxMapControl1_OnAfterScreenDraw events. But these events are thrown even when the map is repainted,for.ex. when a dialog is dragged over the map.

We've also tried manually using the IMapControlEvents2 and it gives the same result.

We have tried to add the IDisplayEvent but it only get triggered on start up not when we pan and zoom the map. Here is some code I used to test the events with:

'Declarations:

 Private m_ActiveViewEventsAfterDraw As ESRI.ArcGIS.Carto.IActiveViewEvents_AfterDrawEventHandler
    Private m_DisplayEventsDisplayStarted As ESRI.ArcGIS.Display.IDisplayEvents_DisplayStartedEventHandler
    Private m_DisplayEventsDisplayFinished As ESRI.ArcGIS.Display.IDisplayEvents_DisplayFinishedEventHandler
    Private m_MapControlEventsOnAfterScreenDraw As ESRI.ArcGIS.Controls.IMapControlEvents2_OnAfterScreenDrawEventHandler
    Private m_MapControlEventsOnBeforeScreenDraw As ESRI.ArcGIS.Controls.IMapControlEvents2_OnBeforeScreenDrawEventHandler

'Form_Load

Dim mapControl As IMapControl4 = DirectCast(axMapControl1.Object, IMapControl4)
        Dim map As IMap = DirectCast(axMapControl1.ActiveView, IMap)
        m_ActiveViewEventsAfterDraw = New ESRI.ArcGIS.Carto.IActiveViewEvents_AfterDrawEventHandler(AddressOf OnActiveViewEventsAfterDraw)
        AddHandler CType(map, ESRI.ArcGIS.Carto.IActiveViewEvents_Event).AfterDraw, m_ActiveViewEventsAfterDraw
m_DisplayEventsDisplayStarted = New ESRI.ArcGIS.Display.IDisplayEvents_DisplayStartedEventHandler(AddressOf OnDisplayStarted)
        AddHandler CType(axMapControl1.ActiveView.ScreenDisplay, ESRI.ArcGIS.Display.IDisplayEvents_Event).DisplayStarted, m_DisplayEventsDisplayStarted

        m_DisplayEventsDisplayFinished = New ESRI.ArcGIS.Display.IDisplayEvents_DisplayFinishedEventHandler(AddressOf OnDisplayFinished)
        AddHandler CType(axMapControl1.ActiveView.ScreenDisplay, ESRI.ArcGIS.Display.IDisplayEvents_Event).DisplayFinished, m_DisplayEventsDisplayFinished
        m_MapControlEventsOnAfterScreenDraw = New ESRI.ArcGIS.Controls.IMapControlEvents2_OnAfterScreenDrawEventHandler(AddressOf OnAfterScreenDraw)
        AddHandler CType(axMapControl1.Object, ESRI.ArcGIS.Controls.IMapControlEvents2_Event).OnAfterScreenDraw, m_MapControlEventsOnAfterScreenDraw
        m_MapControlEventsOnBeforeScreenDraw = New ESRI.ArcGIS.Controls.IMapControlEvents2_OnBeforeScreenDrawEventHandler(AddressOf OnBeforeScreenDraw)
        AddHandler CType(axMapControl1.Object, ESRI.ArcGIS.Controls.IMapControlEvents2_Event).OnBeforeScreenDraw, m_MapControlEventsOnBeforeScreenDraw


'Methods


Private Sub OnAfterScreenDraw(ByVal hDc As Integer)

   Console.WriteLine("OnAfterScreenDraw")
End Sub


    Private Sub OnBeforeScreenDraw(ByVal hDc As Integer)
        Console.WriteLine("OnBeforeScreenDraw")
    End Sub

    Private Sub OnActiveViewEventsAfterDraw(ByVal Display As ESRI.ArcGIS.Display.IDisplay, ByVal phase As ESRI.ArcGIS.Carto.esriViewDrawPhase)
        Console.WriteLine("AfterDraw")
    End Sub

Private IntDisplayStarted As Integer
    Private Sub OnDisplayStarted(ByVal Display As IDisplay)
        IntDisplayStarted = IntDisplayStarted + 1
        Console.WriteLine("OnDisplayStarted " & IntDisplayStarted)
    End Sub

    Private IntDisplayFinished As Integer
    Private Sub OnDisplayFinished(ByVal Display As IDisplay)
        IntDisplayFinished = IntDisplayFinished + 1
        Console.WriteLine("OnDisplayFinished " & IntDisplayFinished)
    End Sub

Have also tried using the ILayerStatus by implementing the IStepProgressor callback on the MapServerLayer, but it was also a disapointment, it get triggered after the image has been fetched from the server.

Does anyone know if there is an event that we can use that only get triggered when the Map is getting data form its datasource?

Best Answer

The code below works for me. If you load a new map, you'll need to re-wire your events. Maybe try start animating when IDisplayEvents.Start fires (and IViewRefresh.Drawing is true). Then stop animating when IActiveViewEvents.AfterDraw fires with drawPhase == esriViewDrawPhase.esriViewForeground.

private IDisplayEvents_Event m_dispevt;
private IActiveViewEvents_Event m_avEvt;
private int m_lastTick;
private void WireEvents(IMap map)
{
    Debug.Print("wiring");
    m_dispevt = ((IActiveView)map).ScreenDisplay as IDisplayEvents_Event;
    m_dispevt.DisplayStarted += new IDisplayEvents_DisplayStartedEventHandler(MainForm_DisplayStarted);
    m_dispevt.DisplayFinished += new IDisplayEvents_DisplayFinishedEventHandler(MainForm_DisplayFinished);
    m_dispevt.DisplayInvalidated += new IDisplayEvents_DisplayInvalidatedEventHandler(MainForm_DisplayInvalidated);
    m_dispevt.DisplayScrolled += new IDisplayEvents_DisplayScrolledEventHandler(MainForm_DisplayScrolled);
    m_avEvt = (IActiveViewEvents_Event)map;
    m_avEvt.AfterDraw += new IActiveViewEvents_AfterDrawEventHandler(m_avEvt_AfterDraw);
    m_avEvt.AfterItemDraw += new IActiveViewEvents_AfterItemDrawEventHandler(m_avEvt_AfterItemDraw);
}

void m_avEvt_AfterItemDraw(short Index, IDisplay Display, esriDrawPhase phase)
{
    Debug.Print("afteritemdraw {0} {1}", Index, phase);
}

void m_avEvt_AfterDraw(IDisplay Display, esriViewDrawPhase phase)
{
    if(phase == esriViewDrawPhase.esriViewForeground)
        Debug.Print("foreground finished afterdraw {0} {1} msecs", phase, Environment.TickCount - m_lastTick);
    if (phase == esriViewDrawPhase.esriViewBackground)
        m_lastTick = Environment.TickCount;
}

void MainForm_DisplayScrolled(IDisplay Display, int deltaX, int deltaY)
{
    Debug.Print("scrolled");
}

void MainForm_DisplayInvalidated(IDisplay Display, ESRI.ArcGIS.Geometry.IEnvelope rect, bool erase, short cacheID)
{
    Debug.Print("invalidated");
}

void MainForm_DisplayFinished(IDisplay Display)
{
    Debug.Print("finished");
}

void MainForm_DisplayStarted(IDisplay Display)
{
    Debug.Print("started {0}", ((IViewRefresh)axMapControl1.Map).Drawing);
}