bdx
bdx

Reputation: 3516

C# Singleton form not opening correctly from Timer, opening correctly from button press

I have a singletone form that can be opened from a ribbon button or that will check every minute whether it should be open after passing a few conditional checks.

When opening the form from the ribbon button, it works correctly every time.

When opening on the timer, the form does not get rendered correctly, any place a control should be is just displayed as a white rectangle. Screenshots below.

ThisAddIn.cs

using Timer = System.Timers.Timer;

public partial class ThisAddIn
{
    private Timer ticker;

    private void ThisAddIn_Startup(object sender, System.EventArgs e) {
        ticker = new Timer(5 * 60 * 1000);
        ticker.AutoReset = true;
        ticker.Elapsed += new System.Timers.ElapsedEventHandler(checkForOverdue);
        ticker.Start();
    }

    private void checkForOverdue(object sender, System.Timers.ElapsedEventArgs e)
    {
        bool overdue = false;
        foreach (Reminder reminder in reminders) 
        {
            DateTime now = DateTime.Now;
            if (reminder.time <= now)
            {
                overdue = true;
                break;
            }
        }
        if (overdue)
        {
            RemindersList form = RemindersList.CreateInstance();
            if (form != null)
            {
                form.Show();
            }
        }
    }
}

Ribbon.cs

public partial class Ribbon
{
    private void reminderListButton_Click(object sender, RibbonControlEventArgs e)
    {
        RemindersList form = RemindersList.CreateInstance();
        if (form != null)
        {
            form.Show();
        }
    }
}

RemindersList.cs

public partial class RemindersList : Form
{
    private static RemindersList _singleton;

    private RemindersList()
    {
        InitializeComponent();
        this.FormClosed += new FormClosedEventHandler(f_formClosed);
    }

    private static void f_formClosed(object sender, FormClosedEventArgs e)
    {
        _singleton = null;
    }

    public static RemindersList CreateInstance(List<Reminder> rs)
    {
        if (_singleton == null)
        {
            _singleton = new RemindersList(rs);
            _singleton.Activate();
            // Flash in taskbar if not active window
            FlashWindow.Flash(_singleton);
            return _singleton;
        }
        else
        {
            return null;
        }
    }
}

Working form - Opened from ribbon button

Broken form - Opened from Timer

EDIT - SOLUTION

Per sa_ddam213's answer, I changed out the System.Timers.Timer for a Windows.Forms.Timer and it's now working just how I wanted.

Code changes:

ThisAddIn.cs

using Timer = System.Windows.Forms.Timer;
public partial class ThisAddIn {
    private void ThisAddIn_Startup(object sender, System.EventArgs e) {
        ticker = new Timer();
        ticker.Interval = 5 * 60 * 1000;
        ticker.Tick += new EventHandler(checkForOverdue);
        ticker.Start();
    }

    // Also needed to change the checkForOverdue prototype as follows:
   private void checkForOverdue(object sender, EventArgs e)
}

Upvotes: 0

Views: 172

Answers (2)

sa_ddam213
sa_ddam213

Reputation: 43636

You can't touch UI controls/elements with any other thread than the UI thread, in your case the System.Timer is running on another thread and the window will never open

Try switching to a Windows.Forms.Timer

Or invoke the call back to the UI thread.

 private void checkForOverdue(object sender, System.Timers.ElapsedEventArgs e)
 {
    base.Invoke(new Action(() => 
    {
         /// all your code here
    }));
 }

Upvotes: 1

MirekE
MirekE

Reputation: 11555

I suspect that the timer event handler is not launched on the UI thread, which could cause all sorts of problems. I would check that first and ensure that the UI stuff is actually done on the UI thread.

Upvotes: 0

Related Questions