🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

イベントハンドラ

マウスのクリックなどの特定の事象(イベント)が発生した時に実行される処理のことをイベントハンドラと呼びます。

Q&A

解決済

4回答

5884閲覧

timerでフラグを立てて次のイベントに割り込みをかけたい(C#)

y.u

総合スコア8

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

イベントハンドラ

マウスのクリックなどの特定の事象(イベント)が発生した時に実行される処理のことをイベントハンドラと呼びます。

0グッド

0クリップ

投稿2019/12/06 09:13

ボタンを用意(今回は3つ)して、ボタン1を押すと10秒後にtimerでフラグをたて
フラグを立てた後どのボタンを押しても

MessageBox.Show("10秒立ったので処理を終了します");
this.Close();

を次に行われるイベントに割り込みをかけたいのですがフラグを立てた後割り込みイベントをどのように立てたらよいのでしょうか?

C#

1using System; 2using System.Windows.Forms; 3 4namespace WindowsFormsApp 5{ 6 public partial class Form1 : Form 7 { 8 bool b = false; 9 10 public Form1() 11 { 12 InitializeComponent(); 13 } 14 15 private void Button1_Click(object sender, EventArgs e) 16 { 17 MessageBox.Show("ボタン1"); 18 timer1.Interval = 10000; 19 timer1.Start(); 20 } 21 22 private void Button2_Click(object sender, EventArgs e) 23 { 24 MessageBox.Show("ボタン2"); 25 } 26 27 private void Button3_Click(object sender, EventArgs e) 28 { 29 MessageBox.Show("ボタン3"); 30 } 31 32 private void Timer1_Tick(object sender, EventArgs e) 33 { 34 b = true; 35 timer1.Stop(); 36 } 37 38 39 } 40}

使用環境
visualstudio2019 
WindowsForms
.NET framework4.7.2

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

イベント削除の覚書として。

C#

1using System; 2using System.Windows.Forms; 3 4namespace WindowsFormsApp1 5{ 6 public partial class Form1 : Form 7 { 8 public Form1() 9 { 10 InitializeComponent(); 11 12 var class1 = Class1.Instance; 13 14 // シングルトンなのでどのフォームでもボタンを追加できる。 15 class1.Buttons.AddRange(new[] { 16 button1, 17 button2, 18 button3, 19 }); 20 21 // タイマーをミリ秒で設定 22 class1.Timer.Interval = 10 * 1000; 23 24 // タイマー終了後にボタンをクリックした時の動作 25 class1.Click += (sender, e) => 26 { 27 MessageBox.Show("10 秒経ったので元のイベントを破棄しました。"); 28 }; 29 } 30 31 private void button1_Click(object sender, EventArgs e) 32 { 33 Class1.Instance.Timer.Start(); 34 } 35 36 private void button2_Click(object sender, EventArgs e) 37 { 38 MessageBox.Show(nameof(button2)); 39 } 40 41 private void button3_Click(object sender, EventArgs e) 42 { 43 MessageBox.Show(nameof(button3)); 44 } 45 } 46}

C#

1using System; 2using System.Collections.Generic; 3using System.ComponentModel; 4using System.Reflection; 5using System.Windows.Forms; 6 7namespace WindowsFormsApp1 8{ 9 public class Class1 10 { 11 private Class1() 12 { 13 Timer.Tick += (s, e) => 14 { 15 var getEvents = typeof(Control) 16 .GetMethod("get_Events", BindingFlags.Instance | BindingFlags.NonPublic); 17 var eventClick = typeof(Control) 18 .GetField("EventClick", BindingFlags.Static | BindingFlags.NonPublic); 19 foreach (var button in Buttons) 20 { 21 RemoveOldClickEvents(button, getEvents, eventClick); 22 button.Click += Click; 23 } 24 Timer.Stop(); 25 }; 26 } 27 28 private void RemoveOldClickEvents(Button button, MethodInfo get_Events, FieldInfo eventId) 29 { 30 var events = (EventHandlerList)get_Events.Invoke(button, Array.Empty<object>()); 31 var handlers = events[eventId.GetValue(button)]?.GetInvocationList(); 32 if (handlers == null) return; 33 foreach (EventHandler handler in handlers) 34 { 35 button.Click -= handler; 36 } 37 } 38 39 public static Class1 Instance { get; } 40 = new Class1(); 41 42 public List<Button> Buttons { get; } 43 = new List<Button>(); 44 45 public Timer Timer { get; } 46 = new Timer(); 47 48 public event EventHandler Click; 49 } 50}

投稿2019/12/06 14:05

Zuishin

総合スコア28669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ちょっと今スマホなのでテストする環境がないのですが加筆・修正しました。

C#

1if(b){ 2 MessageBox.Show("10秒立ったので処理を終了します"); 3 this.Close(); 4}

を何度も書くのはいくら何でもナンセンスなので
Timerとflagと上記処理をまとめて1つのクラスにして、イベントで呼び出される各メソッドがそのインスタンスのCloseチェックメソッドを呼び出す形にすれば綺麗になると思います。

C#

1using System; 2using System.Collections.Generic; 3using System.ComponentModel; 4using System.Data; 5using System.Drawing; 6using System.Linq; 7using System.Text; 8using System.Threading.Tasks; 9using System.Windows.Forms; 10 11namespace WindowsFormsApp 12{ 13 public partial class Form1 : Form 14 { 15 CloseTimer closeTimer; 16 public Form1() 17 { 18 InitializeComponent(); 19 20 closeTimer = new CloseTimer(this, 10000, "10秒立ったので処理を終了します"); 21 } 22 23 private void Button1_Click(object sender, EventArgs e) 24 { 25 closeTimer.Start(); 26 MessageBox.Show("ボタン1"); 27 } 28 29 private void Button2_Click(object sender, EventArgs e) 30 { 31 if (closeTimer.CloseIfCan()) 32 return; 33 MessageBox.Show("ボタン2"); 34 } 35 36 private void button3_Click(object sender, EventArgs e) 37 { 38 if (closeTimer.CloseIfCan()) 39 return; 40 MessageBox.Show("ボタン3"); 41 } 42 } 43 44 public class CloseTimer : IDisposable 45 { 46 bool canClose; 47 Timer timer; 48 string message; 49 Form target; 50 51 public CloseTimer(Form target, int delay, string message) 52 { 53 this.target = target; 54 55 timer = new Timer(); 56 timer.Interval = delay; 57 timer.Tick += (object sender, EventArgs e) => 58 { 59 timer.Stop(); 60 canClose = true; 61 }; 62 63 this.message = message; 64 } 65 66 public void Start() 67 { 68 if (canClose) 69 return; 70 timer.Start(); 71 } 72 73 public bool CloseIfCan() 74 { 75 if (canClose) 76 { 77 MessageBox.Show(message); 78 target.Close(); 79 return true; 80 } 81 return false; 82 } 83 84 public void Dispose() 85 { 86 timer.Dispose(); 87 } 88 } 89}

これで、遅延時間を変えたときに全部のボタンクリックイベントを修正しなくてもよくなりました。
例外チェックなど細かな部分は省いているので必要に応じて修正してください。
実際に書くときは別のファイルに書いた方が良いかと思います。

今回Timerを使ってますが、周期的に動作させるわけではないのでTask.Delayでも良いかもしれませんね。

投稿2019/12/06 10:06

編集2019/12/06 11:34
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

割り込みイベントをどのように立てたらよいのでしょうか?

Button2_ClickButton3_Clickの中でbの状態による分岐を作りfalseなら従来通りの処理を、trueなら

C#

1MessageBox.Show("10秒立ったので処理を終了します"); 2this.Close();

を呼ぶよう作れば良いように思います。

C#

1 private void Button2_Click(object sender, EventArgs e) 2 { 3 if(b){ 4 MessageBox.Show("10秒立ったので処理を終了します"); 5 this.Close(); 6 }else{ 7 MessageBox.Show("ボタン2"); 8 } 9 }

また、もしTimerが有効な時のみ従来の処理をしたい場合は、booleanを使う代わりにEnabledプロパティを使い分岐を作ることができます。その場合はタイマーを開始する前と停止した後の振る舞いが工夫をしないと同じになるので気を付けてください。

投稿2019/12/06 09:17

編集2019/12/06 09:29
BluOxy

総合スコア2663

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y.u

2019/12/06 09:30

ちなみにこれ大量のイベントやフォームがあった場合も全部に bの分岐を作る必要があるのでしょうか? 別フォームやイベントがたくさんある中でタイマーでフラグが経った場合はどうなるのでしょうか? 一応メインフォーム以外を全部閉じるという処理 //特定フォーム以外(メイン以外のすべてを閉じる) foreach (Form frm in Application.OpenForms) { if (frm.Name != "Main") { frm.Close(); } } は用意できたのですが
BluOxy

2019/12/06 09:35

> ちなみにこれ大量のイベントやフォームがあった場合も全部にbの分岐を作る必要があるのでしょうか? 複数種類のイベントやフォームがあり、それ等でbを見る必要がでてくればその個数分の分岐を作る必要があります。 > 別フォームやイベントがたくさんある中でタイマーでフラグが経った場合はどうなるのでしょうか? 「タイマーでフラグが経った」というのは何を示しているのでしょうか。 bのことなのか、私が紹介したEnabledプロパティの話なのかが分かりません。
BluOxy

2019/12/06 09:47 編集

いずれにしろ、そのフォーム(Form1)で持っているタイマーの状態を、別のフォームやイベントが参照していれば何かしらの影響はあると思いますが、実際にどう作用するかはそのアプリケーションの中身によります。
BluOxy

2019/12/06 10:23 編集

一見Button2_ClickとButton3_Clickを見比べると同じ処理に見えますが、それ等2つのボタンの仕様が同じかどうかは明記されていないので、「まとめて」とはあえて突っ込みませんでした。 両方共、同じ処理ならまとめても問題ありませんし、それぞれのボタンの役割が分かれているのなら、イベントの定義も分かれているままで良いと思います。 複数bを定義する必要があるかどうかは、まずはボタンが同じ役割を持つかどうかの精査をしてからです。
guest

0

b の分岐をたくさん作るのが嫌なら、ボタンクリックのハンドラ関数を全部まとめて一つにしてしまえばよろしい。
これならbの分岐は一つで済みます。で、クリック処理はボタン名で判断させればよろしい

投稿2019/12/06 09:50

y_waiwai

総合スコア88040

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問