Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

I would like to add a progress indicator to our ArcGIS Engine 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?

share|improve this question
1  
+1 I think this problem can be more generally stated: "Any workaround for ArcEngine UI thread blocking?" Put a mapcontrol and a textbox (multiline= true) on a form. Load some layers that take a really long time to draw. Run the app. While the map is drawing, try to edit text in the text box. Haven't tried it with 10.0, but was big problem in 9.3. Workaround was to put the mapcontrol in a separate process and position its window on top of a form that has the textbox. For simple animation there's probably a way (after all, arcmap can do it). I'd like to see a more general solution though. –  Kirk Kuykendall Aug 19 '10 at 16:05
2  
That's the way we've solved it, but we did it the other way around, we created the progress indicator in a different thread and put it on top of a panel in the form, making the impression that it's in the form. I agree it would be nice with a more general solution. The problem for me right now is that I can't find an event that tells me when the map is busy. Our architecture is based on having a server layer as a background layer and that layer is slow. Making it important o show the user that it's working. Do you know if it somehow is possible to find out when a layer is getting data? –  MathiasWestin Aug 19 '10 at 17:49
add comment

3 Answers

up vote 1 down vote accepted

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);
}
share|improve this answer
    
It worked better when we wired the events after loading the Map. But it's a bit hard to get a single start event, when zooming anv panning the DisplayStarted with IViewRefresh.Drawing=true is thrown five times before I get a Finish event. –  MathiasWestin Aug 23 '10 at 14:23
    
Thanks for sharing the code it helped me to solve the problem. –  MathiasWestin Aug 26 '10 at 14:31
add comment

If your ArcEngine code runs inside a ArcGIS Desktop application, then the MxStatusBar (or the IStatusBar interface) seems appropriate. In that case, refer to IStatusBar.ProgressBar. (ArcEngine also offers progress dialogs — IProgressDialog2 — but that's a full dialog window and apparently not what you're after.)

If your code runs outside of any ArcGIS application, ie. it has its own GUI, you will most likely have to check out the corresponding functionality with your platform's GUI framework (Windows Forms or WPF for .NET; MFC, wxWidgets for C++, Swing / AWT / whatever for Java, etc.).

For Windows Forms GUIs, take a look at the System.Windows.Forms.StatusBar class.

share|improve this answer
    
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. Do you know a better event? –  MathiasWestin Aug 18 '10 at 11:11
    
Did you try IDisplayEvents? –  Kirk Kuykendall Aug 18 '10 at 14:21
    
Kirk, I tried using the IDisplayEvents but as far as I can see it only get triggered the first time the Map gets visible. –  MathiasWestin Aug 19 '10 at 13:46
add comment

Ended up with this code:

 'The event handler
    Private m_ActiveViewEventsAfterDraw As ESRI.ArcGIS.Carto.IActiveViewEvents_AfterDrawEventHandler

    'Is needed to stop "COM object that has been separated from its underlying RCW cannot be used" from occuring
    Private m_ActiveViewEvents As ESRI.ArcGIS.Carto.IActiveViewEvents_Event

    Private m_lastTick As Integer
    Private Sub OnActiveViewEventsAfterDraw(ByVal Display As ESRI.ArcGIS.Display.IDisplay, ByVal phase As ESRI.ArcGIS.Carto.esriViewDrawPhase)

        'Occurs after the layer is drawn
        If phase = esriViewDrawPhase.esriViewGeography Then
            Console.WriteLine("geography finished afterdraw {0} {1} msecs", phase, Environment.TickCount - m_lastTick)
        End If

        'Occurs before the layer is requested
        If phase = esriViewDrawPhase.esriViewBackground Then
            m_lastTick = Environment.TickCount
            Console.WriteLine("background finished")
        End If
    End Sub

    'wire the event handler
    Private Sub WireEventHandler()

        m_ActiveViewEvents = CType(CType(axMapControl1.Object, IMapControl2).ActiveView.FocusMap, ESRI.ArcGIS.Carto.IActiveViewEvents_Event)
        m_ActiveViewEventsAfterDraw = New ESRI.ArcGIS.Carto.IActiveViewEvents_AfterDrawEventHandler(AddressOf OnActiveViewEventsAfterDraw)
        AddHandler m_ActiveViewEvents.AfterDraw, m_ActiveViewEventsAfterDraw

    End Sub

To start the progress indicator on esriViewDrawPhase.esriViewBackground and stop it on the esriViewDrawPhase.esriViewGeography seems to do the trick. Reference for Draw order and a COM problem I got when testing the event handlers "COM object that has been separated from its underlying RCW cannot be used"

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.