0

I am a bit stuck at the moment, I have a c# addin, which as part of one the methods, contains SavefileDialog object. The code also contains a Timer. The steps for the user is:

  • Click on the map
  • Geometry is drawn
  • Map is refreshed
  • The timer waits for 5 seconds to elapse (enough time for the map to be refreshed)
  • A dialog box appears that asks the user a question
  • If they select yes, the SaveFileDialog object appears.

It seems when that just before the SaveFileDialog appears I get the following error

Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it.

Where in my code do I place [STAThread] attribute as I do not have a Main function. The addin is a toolbar and one button.

2 Answers 2

3

Instead of using a Timer (which runs on a separate thread) how about using the inbuilt events: IActiveViewEvents.ViewRefreshed (start refresh) and then IActiveViewEvents.AfterDraw (finished refresh). If you listen for the AfterDraw with the drawphase esriViewDrawPhase.esriViewForeground or esriViewDrawPhase.esriViewAll (you might need to experiment a bit to see what works for you) then the event is triggered after the map is refreshed, regardless of how long it takes.

Have a boolean (globally) that says 'I've called this refresh' or the save dialog will pop up every time the view is refreshed:

// global
bool pIveCalledThisRefresh = false;
// in the form load
private void Form1_Load(object sender, EventArgs e)
{
    IApplication m_application = ArcMap.Application;
    IMxDocument gDoc           = (IMxDocument)m_application.Document;
    IScreenDisplay gScreenDis  = gDoc.ActiveView.ScreenDisplay;
    ((IActiveViewEvents_Event)gDoc.FocusMap).AfterDraw +=
        new IActiveViewEvents_AfterDrawEventHandler(My_AfterDraw);
}
// in the button event
private void Go_Click(object sender, EventArgs e)
{
    pIveCalledThisRefresh = true;
    // do some stuff and refresh the map
    pIveCalledThisRefresh = false; // refresh back to normal
}
// after draw event
void My_AfterDraw(IDisplay Display, esriViewDrawPhase phase)
{
    if (!pIveCalledThisRefresh) return; // skip refreshes unless you've caused them
    if (phase == esriViewDrawPhase.esriViewForeground) // experiment with this, see what phase you want
    {
        if (MyDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            // do your save
        }
    }
}

If you still want to wait after the refreshed is finished get the time as a DateTime object, add some seconds and then compare with DateTime.Compare() with a DoEvents() in the loop:

void My_AfterDraw(IDisplay Display, esriViewDrawPhase phase)
{
    if (!pIveCalledThisRefresh) return; // skip refreshes unless you've caused them
    if (phase == esriViewDrawPhase.esriViewForeground) // experiment with this, see what phase you want
    {
        pSaveTime = DateTime.Now;
        pSaveTime.AddSeconds(5.0); // arbitrary 5 seconds

        do
        {
            // a loop of nothing 
            System.Windows.Forms.Application.DoEvents(); // lets the other processes do their thing
        } while (DateTime.Compare(pSaveTime,DateTime.Now) > 0);

        if (MyDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            // do your save
        }
    }
}

All of this executes on the same thread which will alleviate your Esri object crossing threads, which is not allowed as all Esri objects are not thread safe.

0
3

If you are using System.Timers.Timer or System.Threading.Timer, your code that is triggered by the timer occurs on a worker thread. This thread will not be marked STAThread and so can't use the SaveFileDialog.

You could either use System.Windows.Forms.Timer, which is synchronous and on the main UI thread, or use Control.Invoke to run your code on the main thread (or IDispatcher.Invoke if using WPF instead of WinForms).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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