Tim Bailey
Tim Bailey

Reputation: 571

Single instance form but not singleton

I cannot understand how this is possible. Please help!!

I have an app with a trayicon. I want a form to be show when the user double clicks the trayicon. I have a problem where it is possible to get 2 or more forms showing by quickly triple or quadruple clicking the trayicon. The reason I don't want a singleton is that I want the form to be released each time it is closed to save memory, maybe this is not a good idea?

I have a field called m_form1. I have a method called ShowForm1; I call the method ShowForm1 on the double-click of the TrayIcon.

        private Form1 m_form1;
        private void ShowForm1()
        {
            if (m_form1 == null)
            {
                Trace.WriteLine("*CREATE*" + Thread.CurrentThread.ManagedThreadId.ToString());
                m_form1 = new Form1();
                m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
                m_form1.Show();
            }
            m_form1.BringToFront();
            m_form1.Activate();
        }

So when Form1 takes a while to construct, then it is possible to create 2 because m_form1 is still null when the second call arrives. Locking does not seem to work as it is the same thread both calls (I'm guessing the UI thread) ie the trace writes out *CREATE*1 twice (below).

[3560] *CREATE*1 
[3560] *CREATE*1 

Changing the code to include a lock statement does not help me.

    private Form1 m_form1;
    private object m_Locker = new object();
    private void ShowForm1()
    {
        lock (m_Locker)
        {
            if (m_form1 == null)
            {
                Trace.WriteLine("****CREATE****" + Thread.CurrentThread.ManagedThreadId.ToString());
                m_form1 = new Form1();
                m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
                m_form1.Show();
            }
        }
        m_form1.BringToFront();
        m_form1.Activate();
    }

How should I handle this situation?

Thanks guys

Tim.

Upvotes: 1

Views: 1644

Answers (3)

TheUberOverLord
TheUberOverLord

Reputation:

Please see this, it handles all mouse event combinations for NotifyIcon as well as Form1.

More here: http://code.msdn.microsoft.com/TheNotifyIconExample

Upvotes: 0

Sunny Milenov
Sunny Milenov

Reputation: 22320

Use Interlocked.Increment to change the nr of the tries. If it is 1, open the form, otherwise, don't. And use Interlocked.Decrement after the test or on form's close.

private int openedForms = 0;
private Form1 m_form1;
private void ShowForm1()
{

    if (Interlocked.Increment(ref openedForms) = 1)
    {
       m_form1 = new Form1();
       m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
       m_form1.Show();
    }
    else
    {
       Interlocked.Decrement(ref openedForms);
    }
    if (m_form1 != null)
    {
       m_form1.BringToFront();
       m_form1.Activate();
    }
}

private void m_form1_FormClosed(object Sender, EventArgs args)
{
   Interlocked.Decrement(ref openedForms);
}

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1503290

Have an additional boolean variable, "m_formUnderConstruction" which you test for before constructing the form, and which you set as soon as you've decided to construct it.

The re-entrancy makes all of this a little icky, unfortunately. I've removed the lock, as if this ever gets called from a different thread then you've got the nasty situation of trying to show a form from a different thread to the one it was constructed on.

private Form1 m_form1;
private bool m_underConstruction = false;

private void ShowForm1()
{
    if (m_underConstruction)
    {
        // We're about to show it anyway
        return;
    }
    m_underConstruction = true;
    try
    {
        if (m_form1 == null)
        {
            m_form1 = new Form1();
            m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
            m_form1.Show();
        }
    }
    finally
    {
        m_underConstruction = false;
    }
    m_form1.BringToFront();
    m_form1.Activate();
}

Upvotes: 5

Related Questions