89883


こちらの内容の続きになります。  
[別スレッドからコントロールにアクセスするとSystem.InvalidOperationExceptionになる](https://ja.stackoverflow.com/questions/89825/%e5%88%a5%e3%82%b9%e3%83%ac%e3%83%83%e3%83%89%e3%81%8b%e3%82%89%e3%82%b3%e3%83%b3%e3%83%88%e3%83%ad%e3%83%bc%e3%83%ab%e3%81%ab%e3%82%a2%e3%82%af%e3%82%bb%e3%82%b9%e3%81%99%e3%82%8b%e3%81%a8system-
invalidoperationexception%e3%81%ab%e3%81%aa%e3%82%8b?noredirect=1#comment101927_89825)

**やりたいこと**  
作成物について  
指定した時刻と現在時刻を比較して同じになった場合、アラーム画面を表示する目覚まし時計のフォームアプリを作成しています。

**問題点**  
指定したアラーム時刻と現在時刻が同じになった場合にアラーム画面を表示するのですが、  
1秒に1つ画面が連続で表示されてしまいます。  
最初の画面が表示されたら、以降は表示されないようにしたいです。  
どのようにすれば解決できますでしょうか？

**実際のコード**

[code]

    using System;
    using System.Timers;
    namespace Alarm_Clock
    {
        public partial class Form1 : Form
        {
    
    
            public static class Global
            {
                public static string datetime_now = "";
                public static string Alarm_set = "";
            }
    
    
    
            public Form1()
            {
                InitializeComponent();
            }
    
          
            private void Form1_Load(object sender, EventArgs e)
            {
    
                System.Timers.Timer timer = new System.Timers.Timer(1000);
    
                timer.Elapsed += new ElapsedEventHandler(timer_Tick);
                
                timer.AutoReset = true;
                timer.Enabled = true;
    
    
    
            }
    
    
    
            private void timer_Tick(object sender, EventArgs e)
            {
                
                DateTime d = DateTime.Now;
                int hh = d.Hour;
                int mm = d.Minute;
    
                string time = "";
                if (hh < 10)
                {
                    time += "0" + hh;
                }
                else
                {
                    time += hh;
                }
                time += ":";
    
                if (mm < 10)
                {
                    time += "0" + mm;
                }
                else
                {
                    time += mm;
                }
    
    
                Global.datetime_now = time.ToString();//String型に変換する
                this.Invoke((Action)(() => time_now.Text = time));
                
    
                if (onbutton.Checked == true)
                {
                    if (Global.datetime_now == Global.Alarm_set)
                    {
                        Form2 form2 = new Form2();
                        form2.ShowDialog();
    
    
                    }
    
                }
    
    
            }
    
            private void button_Click(object sender, EventArgs e)
            {
                Global.Alarm_set = textbox.Text;//アラーム時刻取得
                if (Global.Alarm_set == "")
                {
                    exc.Text = "時刻を入力してください";
                    return;
                }
    
                //入力時刻の時とコロンと分を文字列で取得
                String h_time = Global.Alarm_set.Substring(0, 2);
                String Colon = Global.Alarm_set.Substring(2, 1);
                String m_time = Global.Alarm_set.Substring(3);
    
                //取得した時と分を数値に変換
                int hh_time = int.Parse(h_time);
                int mm_time = int.Parse(m_time);
    
    
                //時と分が入力範囲内であるか。コロンが入力されているか
    
                if ((hh_time >= 0 && hh_time < 24) && (Colon == ":") && (mm_time >= 0 && mm_time < 60))
                {
                    String Alarm = "アラーム時刻：" + Global.Alarm_set;
                    Alarmtime.Text = Alarm;//アラーム時刻表示
                    exc.Text = "";//エラー文非表示
                }
    
                else
                {
                    exc.Text = "正しく時刻を入力してください";
                }
    
            }
      }
    }
    
[/code]


CC BY-SA 4.0
2022-07-08T07:22:49.260
89836
2022-07-11T01:33:59.100
2022-07-08T07:31:05.730
3060
47546
question
c#
アラーム時刻になったらアラーム画面を一回だけ表示したい。


■ System.Timers.Timer について

System.Timers.Timer は精度の高いタイマーですが、取り扱いは難しいものです。  
間隔を 10ミリくらいにすると、Form1 を閉じたとき、

this.Invoke((Action)(() => time_now.Text = time));

の行で、

「 System.ObjectDisposedException: '破棄されたオブジェクトにアクセスできません。」

というエラーが出るはずです。

提示されたコードでは、間隔が１秒なので起きにくくはありますが、発生しないわけではないので、本来は回避するコードが必要です。

今回の処理であれば、精度をさほど必要としないので、System.Windows.Forms.Timer を使用したほうが良いでしょう。

ウインドウが閉じられれば Tick イベントは発生しませんし、Application.DoEvents()
を実行しない限り、フォームを閉じる処理をしている間に Ticks イベントが割り込むことはありません。

■ 本題について

というわけで、System.Windows.Forms.Timer を使った処理に書き換えてみました。  
気になるところは手をいれてコメントをつけていますので参考にしてみてください。

[code]

    using System;
    using System.Globalization;
    using System.Windows.Forms;
    
    namespace Alarm_Clock
    {
        public partial class Form1 : Form
        {
            public static class Global
            {
                public static string datetime_now = string.Empty;
                public static string Alarm_set = string.Empty;
            }
    
            public Form1() {
                InitializeComponent();
            }
    
            // Timer のインスタンスを保持するための変数
            // インスタンスを保持しないと GC が発生したとき
            // Timer オブジェクトが回収されイベントが発生しなくなる
            private Timer timer;
    
            // アラーム画面を表示したかどうかのフラグ
            // true:表示済み false:未表示
            private bool Alermed = true;
    
            private void Form1_Load(object sender, EventArgs e) {
                timer = new Timer();
                timer.Tick += timer_Tick;
                timer.Interval = 1000;
                timer.Enabled = true;
                // 初期表示
                time_now.Text = DateTime.Now.ToString("HH:mm");
            }
    
            private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
                // フォームを閉じるときにタイマーを無効にする
                timer.Enabled = false;
            }
    
            private void timer_Tick(object sender, EventArgs e) {
                // DateTime.ToString で書式が設定できる
                string time = DateTime.Now.ToString("HH:mm");
                // time は string なので ToString() する必要ナシ
                Global.datetime_now = time;
                time_now.Text = time;
                if (onbutton.Checked) {
                    if (Global.datetime_now == Global.Alarm_set) {
                        // 未表示なら表示する
                        if (!Alermed) {
                            // 表示済みにする
                            Alermed = true;
                            // UI スレッドで ShowDialog すると処理が止まってしまうので Show を使用する
                            Form2 form2 = new Form2();
                            form2.Show();
                        }
                    }
                }
            }
    
            private void button_Click(object sender, EventArgs e) {
                // 未チェックのテキストを他から参照できる Global.Alarm_set に入れるべきではない
                string time = textbox.Text;
    
                // 空文字もしくは空白をチェックする
                if (string.IsNullOrWhiteSpace(time)) {
                    exc.Text = "時刻を入力してください";
                    return;
                }
    
                // 時刻のチェック
                if (!DateTime.TryParseExact(time, "HH:mm", null, 
                                    DateTimeStyles.None, out DateTime result)) {
                    exc.Text = "正しく時刻を入力してください";
                    return;
                }
    
                // 予期しない書式で表示されるのを防ぐため、入力値をそのまま使用しない。
                // 得られた時刻を編集して表示する
                time = result.ToString("HH:mm");
                Global.Alarm_set = time;
                Alarmtime.Text = "アラーム時刻：" + time;
                exc.Text = string.Empty;
    
                // 未表示にする
                Alermed = false;
            }
    
        }
    }
    
[/code]


CC BY-SA 4.0
2022-07-10T07:32:29.477
89883
2022-07-10T07:47:31.163
2022-07-10T07:47:31.163
50741
50741
89836
answer


timer_Tickの頭で、timer.Enabled = false;を入れると、タイマーは1度しか起動されないようになります。

起動したくなったタイミングで、timer.Enabled = true;を書きます。

[code]

    public partial class Form1 : Form
    {
        Timer timer = new Timer();
    
    
        private void timer_Tick(object sender, EventArgs e)
        {
            timer.Enabled = false;
        
            ....
    
[/code]


CC BY-SA 4.0
2022-07-11T01:33:59.100
89899
2022-07-11T01:33:59.100
24490
89836
answer