1

I'm creating an app that creates notifications at user defined times.

Basically I use two WinForms:

  • Number 1 is for getting the data for notification and creating a new instance of NotificationObject class that contains notification data such as message, title, due time.
  • Form number 2 is for tracking the notifications.

When the program starts, Form number 2 opens and there is a button where I can open Form number 1. When form number 1 closes it saves the notification data to NotificationObject class. Form number 2 gets NotificationObject that is created upon closing of Form number 1 and adds it to ObservableCollection. When something is added to ObservableCollection, it creates a UserControl with constructor parameters DueTime, Title and Message. Then inside the UserControl I use the System.Threading.Timer and NotifyIcon to create Notifications.

Even though I've set up the Callback method to create NotifyIcon Balloon, it doesn't show any.

Form number 1

public partial class NotificationCreator : Form
{
    public NotificationObject ntfObj;
    
    public NotificationCreator()
    {
        InitializeComponent();
    }
    
    private void createNotification_Click(object sender, EventArgs e)
    {
        if (String.IsNullOrEmpty(timeInput.Text))
        {
            MessageBox.Show(
                "Please set the time.",
                "Time is not set",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error
            );
    
            return;
        }
    
        if (String.IsNullOrEmpty(titleInput.Text))
        {
            MessageBox.Show(
                "Please set the title.",
                "Title is not set",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error
            );
    
            return;
        }
    
        if (String.IsNullOrEmpty(messageInput.Text))
        {
            MessageBox.Show(
                "Please set the message.",
                "Message is not set",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error
            );
    
            return;
        }
    
        DateTime time = DateTime.Parse(timeInput.Text);
    
        DateTime dt = new DateTime(dateInput.Value.Year,
            dateInput.Value.Month,
            dateInput.Value.Day,
            time.Hour,
            time.Minute,
            0
        );
    
        ntfObj = new NotificationObject(dt, titleInput.Text, messageInput.Text);
    
        Close();
        Dispose();
    }
    
    private void timeInput_TextChanged(object sender, EventArgs e)
    {
        if (timeInput.Text.Length == 5)
        {
            try
            {
                DateTime dummy = DateTime.Parse(timeInput.Text);
    
                if (dummy.Hour <= DateTime.Now.Hour && dummy.Minute <= DateTime.Now.Minute && dummy.Hour != 00 && dummy.Minute != 00)
                {
                    MessageBox.Show("Please set the time for future",
                        "Invalid time",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
    
                    return;
                }
    
                return;
            }
            catch
            {
                MessageBox.Show("Please set a valid time",
                    "Invalid time",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                return;
            }
        }
    }
    
    private void dateInput_ValueChanged(object sender, EventArgs e)
    {
        if (dateInput.Value < DateTime.Now.AddDays(-1))
        {
            MessageBox.Show("Please set the date for future.",
                "Invalid date",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error);
    
                dateInput.Value = DateTime.Now;
                    
                return;
            }
        }
    }
}

Form number 2

public partial class TrackingPanel : Form
{
    ObservableCollection<NotificationObject> ntfList = new ObservableCollection<NotificationObject>();

    public TrackingPanel()
    {
        InitializeComponent();

        ntfList.CollectionChanged += NtfList_CollectionChanged;
    }

    private void NtfList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            NotificationObject ntfObj = e.NewItems[0] as NotificationObject;

            Notification ntf = new Notification(ntfObj.DueTime, ntfObj.ntfTitle, ntfObj.ntfMessage);

            ntfPanel.Controls.Add(ntf);
        }
    }

    private void TrackingPanel_Load(object sender, EventArgs e)
    {}

    private void TrackingPanel_Resize(object sender, EventArgs e)
    {
        if (WindowState == FormWindowState.Minimized)
        {}
    }

    private void createNotification_Click(object sender, EventArgs e)
    {
        NotificationCreator Tool = new NotificationCreator();
        Tool.ShowDialog();

        ntfList.Add(Tool.ntfObj);
    }
}

NotificationObject

/// <summary>
/// An object that represents the Notification and contains Notification information.
/// </summary>
public class NotificationObject
{
    /// <summary>
    /// Notification due time.
    /// </summary>
    public DateTime DueTime { get; set; }
    /// <summary>
    /// Title of Notification.
    /// </summary>
    public string ntfTitle { get; set; }
    /// <summary>
    /// Notification Message.
    /// </summary>
    public string ntfMessage{ get; set; }

    public NotificationObject(DateTime dueTime, string title, string message)
    {
        DueTime = dueTime;
        ntfTitle = title;
        ntfMessage = message;
    }
}

UserControl

 public partial class Notification : UserControl
    {
        private readonly System.Threading.Timer timer;

        public Notification(DateTime dt, string Title, string Message)
        {
            InitializeComponent();

            this.Title.Text = Title;
            this.Message.Text = Message;

            ntfIco.Visible = false;

            timer = new System.Threading.Timer(NotificationOccurs, null, dt - DateTime.Now, TimeSpan.FromSeconds(20));
        }

        private void NotificationOccurs(object state)
        {
            ntfIco.Visible = true;

            ntfIco.ShowBalloonTip(3500, Title.Text, Message.Text, ToolTipIcon.Info);

            ntfIco.Visible = false;
        }

        private void Notification_Load(object sender, EventArgs e)
        {
            ntfPic.Image = Properties.Resources.Search_Png;
        }
    }

EDIT

I used System.Windows.Forms.Timer in UserControl and still didn't worked.

public partial class Notification : UserControl
    {
        public Notification(DateTime dt, string Title, string Message)
        {
            InitializeComponent();

            this.Title.Text = Title;
            this.Message.Text = Message;

            ntfIco.Visible = false;

            Timer.Interval = (int)(dt - DateTime.Now).TotalMilliseconds;

            Timer.Start();
            
        }

        private void Notification_Load(object sender, EventArgs e)
        {
            ntfPic.Image = Properties.Resources.Search_Png;
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            ntfIco.Visible = true;

            ntfIco.ShowBalloonTip(3500, Title.Text, Message.Text, ToolTipIcon.Info);

            Timer.Interval = 10 * 1000;

            ntfIco.Visible = false;
        }
    }
Orlyyn
  • 2,296
  • 2
  • 20
  • 32
  • 3
    Use a `System.Windows.Forms.Timer` , the `System.Threading.Timer`'s callback won't automatically execute on the UI Thread. – Fildor Sep 18 '20 at 11:14
  • Else you need to synchronize with the UI thread when modifying controls. Perhaps the problem comes from that. –  Sep 18 '20 at 11:15
  • @OlivierRogier That's correct, but why bother if there's a timer explicitly designed for this? – Fildor Sep 18 '20 at 11:16
  • @OlivierRogier how can i do that? –  Sep 18 '20 at 11:16
  • 3
    Use the winforms timer that is UI compatible. –  Sep 18 '20 at 11:16
  • 1
    A form has an instance. When you use 'new ' it create the instance. Then you display the form either using Show or ShowDialog. ShbowDialog the focus stays on the form until closed. The Show method the focus can move while the form is open. Closing a form will dispose the instance of the form and any timers will also be disposed. You can exit a form and not dispose by capturing the close event. See my two form project for sample code : https://stackoverflow.com/questions/34975508/reach-control-from-another-page-asp-net – jdweng Sep 18 '20 at 11:21
  • @OlivierRogier can i use it in `UserControl`? –  Sep 18 '20 at 11:29
  • Yes, just replace `System.Threading.Timer timer` by `System.Windows.Forms.Timer timer` and use the `Tick` event and the `Interval` property with `Enable` or `Start` and `Stop`. And all should be fine with the UI if your algorithm is correct but I have not checked that, I had no time to read the code and try toi understand what you bwant to do at this time, sorry. –  Sep 18 '20 at 11:47
  • @OlivierRogier did those but still doesn't work. Checked algorithm again, things are fine there but don't know why it is not working. It enters the Timer_Tick event but NotifyIcon.ShowBaloonTip() doesn't work. –  Sep 18 '20 at 11:50
  • I just see that you set it visible false after popup it: don't do that and let it fade itself... Why do you change the Interval ? In fact, can you resume in one sentence what you want to do by mixing a notify ballon and a timer like that ? –  Sep 18 '20 at 11:51
  • Is that can help you: [How to Show NotifyIcon in Windows Forms Application Using C#](https://www.c-sharpcorner.com/UploadFile/deepak.sharma00/how-to-show-notifyicon-in-windows-forms-application-using-C-Sharp/) & [ShowBalloonTip Not Working](https://stackoverflow.com/questions/42444541/showballoontip-not-working) & [NotifyIcon.ShowBalloonTip Method](https://learn.microsoft.com/dotnet/api/system.windows.forms.notifyicon.showballoontip) –  Sep 18 '20 at 11:56
  • @OlivierRogier i want to create an interval with given date and when we reach that date want to create a notification with NotifyIcon. I change the interval cause once the notification pops up i want it to pop up at different interval. –  Sep 18 '20 at 11:58
  • @OlivierRogier still doesn't work. I'm frustrated. –  Sep 18 '20 at 12:04

1 Answers1

0

From the comments discussion:

  • Be sure to set the Icon property of the notify icon.

  • Keep the WinForms Timer to be fine with the UI thread.

  • Don't set the notify icon visible to false just after the ballon tip popup else it closes.

  • Let the ballon tip fade itself.

  • If you want to hide the notify icon, do that on the BallonTipClosed event.

private void Timer_Tick(object sender, EventArgs e)
{
  ntfIco.Visible = true;
  ntfIco.ShowBalloonTip(3500, Title.Text, Message.Text, ToolTipIcon.Info);
  Timer.Interval = 10 * 1000;
}
public Notification(DateTime dt, string Title, string Message)
{
  InitializeComponent();
  this.Title.Text = Title;
  this.Message.Text = Message;
  ntfIco.Visible = false;

  ntfIco.BalloonTipClosed += (sender, e) => ntfIco.Visible = false;

  Timer.Interval = (int)( dt - DateTime.Now ).TotalMilliseconds;
  Timer.Start();
}
  • 1
    Mr Olivier thanks for taking your time and answering my question. It now works. Thanks a lot. –  Sep 18 '20 at 12:12