トップ回答者
Timer イベントで while ループから抜けたい

質問
-
はじめまして。E-650と申します。今回初めて投稿させて頂きます。
過去にDelphiを使った事はあるのですが(初心者どまり)、C#に変えてみる事にしました。
タイマーがスタートして最初のイベントで while ループを抜けるようにしたいのですが、
試しても試してもうまく行きません。どうかよろしくお願い致します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;using System.Linq;
using System.Text;
using System.Windows.Forms;namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
bool exitFlag = false;public Form1()
{
InitializeComponent();
timer1.Enabled = false;
}private void button1_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
timer1.Interval = 10000;
timer1.Start();
int i = 0;while (true)
{
textBox1.Text = i.ToString();
textBox1.Update();
i++;if (exitFlag)
break;
}
}private void timer1_Tick(object sender, EventArgs e)
{
exitFlag = true;
timer1.Enabled = false;
timer1.Stop();
}
}
}
回答
-
2つのTimerのTickイベントと、button1のClickイベントの設定を確認してみて下さい。
completeTimerのTick に「completeTimer_Tick」、
cycleTimerのTick に「cycleTimer_Tick」、
button1のClick に「button1_Click」をそれぞれ設定する必要があります。
(補足)イベントが上記のように設定されていて、かつTimerのEnabledを2つともtrueに設定しておくと、ボタンを押さなくても番号表示が始まります。
すべての返信
-
早速のご回答ありがとうございます。
少し変えてみました。でも、これも駄目な様ですね。自分で考えてみます。ヒントありがとうございました。
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
bool exitFlag = false;public Form1()
{
InitializeComponent();
timer1.Enabled = false;
}private void button1_Click(object sender, EventArgs e)
{
Loop();
}private void timer1_Tick(object sender, EventArgs e)
{
exitFlag = true;
timer1.Enabled = false;
timer1.Stop();
}public void Loop()
{
timer1.Enabled = true;
timer1.Interval = 10000;
timer1.Start();
int i = 0;while (! exitFlag)
{
textBox1.Text = i.ToString();
textBox1.Update();
i++;
}
MessageBox.Show("抜けました");
}
}
} -
そもそも何をしたいのでしょうか?
コードを読む限り、「10秒間にいくつカウントアップできるかの確認」のように見えますが。
もし本当にそんなことをしたらCPU使用率が100%になって何もできないプログラムにしかなりません。そのため一般的なGUIは逆のアプローチで、どのタイミングで更新(この場合のカウントアップ)をしたいのかという実装をします。
なので、例えば、10msごとに更新するのであればそのようにタイマーを組みます。となると当初の目的の答えとしては、「10秒 / 更新タイミング」回カウントアップできる、となるため、そもそもこのコードが無意味になってしまいます。
-
もう少し具体的に書いてみました。ビンゴのソフトの一部です。
アルゴリズムの勉強をもっともっとした方がいいと思いました。
基礎からやるべきだと痛感しました。
開始時間を記録して、終了時間(暫定的に15秒間)との差で while ループを抜けられるように考えます。
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
bool[] ball = new bool[76];public Form1()
{
InitializeComponent();for (int i = 1; i < 76; i++)
{
ball[i] = true;
}
}private void button1_Click(object sender, EventArgs e)
{
byte num;
int seed = Environment.TickCount;
Random rnd = new Random(seed);// (暫定的に)15秒という長さのwavファイルを再生する
//
// 音の再生の処理をここに書く。音が鳴っている間も数字を表示して行くwhile (true) // 15秒間はこれを繰り返す
{
num = (byte)rnd.Next(75 + 1);
if (ball[num])
{
ball[num] = false;
textBox1.Text = num.ToString();
System.Threading.Thread.Sleep(70);
textBox1.Update();
}}
// 15秒経ったらここに来るようにしたい
}
}
}- 編集済み E-650 2010年6月13日 12:22 舌足らずな文章を補足
-
Delphi には
名前から察するに、.NET Framework の Application.DoEvents と同等のことをしているのでしょうか。
Application.ProcessMessage();
というのがあって、これで Windows に制御を移す事が出来る様です。
ただし、Application.DoEvents の利用は避けるべきです。
安易な利用は不具合を招くだけです。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。 -
Delphi には
名前から察するに、.NET Framework の Application.DoEvents と同等のことをしているのでしょうか。
Application.ProcessMessage();
というのがあって、これで Windows に制御を移す事が出来る様です。
ただし、Application.DoEvents の利用は避けるべきです。
安易な利用は不具合を招くだけです。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
遅くなりすみません。では、どのようにするのがいいのでしょうか? ヒントだけでも頂けたらと思います。
どうかよろしくお願い致します。
-
まず、15 秒間全力で数字を選び続ける必要はありません。
画面に反映されるのに時間がかかるし、そもそも人の目に数ミリ秒での変化は捉えられません。Timer あたりで、100ms とか、500ms とか、求めるスピードを満たしつつ、やり過ぎない間隔(Interval)で変更していけば良いでしょう。
15 秒経過後の最初の Tick イベントでタイマーを止めれば、15 秒間は数値をランダムで選択しつつ、無限ループにならない構造になるでしょう。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。 -
E-650さま、こんにちは。
試しにコードを書いてみたら、こんな風になりました。
ご参考になれば幸いです。
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { completeTimer.Start(); cycleTimer.Start(); } private void completeTimer_Tick(object sender, EventArgs e) { completeTimer.Stop(); cycleTimer.Stop(); Button1ClickComplete(); } private void Button1ClickComplete() { MessageBox.Show("完了しました"); } NumberSelector selector = new NumberSelector(); private void cycleTimer_Tick(object sender, EventArgs e) { int number = selector.SelectNumber(); textBox1.Text = number.ToString(); } private class NumberSelector { List<int> numbers = Enumerable.Range(1, 75).ToList(); Random random = new Random(); int selection; public int SelectNumber() { selection = numbers[random.Next(numbers.Count)]; return selection; } } }
このコードでは、Timerを2つ使っています。
数字を更新するTimer(cycleTimer)と、 そのTimerを止めるTimer(completeTimer)です。
Intervalをそれぞれ適当な値に設定してお試し下さい。
whileループは使わずにすみました。
-
はじめまして。ご丁寧にありがとうございます。
早速試してみました。
cycleTimer を 500
completeTimer を 10000
にしてみました。
ボタンを押しても表示が出ず、10000ms 待っても
MessageBox.Show("完了しました");
が出ませんでした。2つのタイマーの Enabled は true にしています。
タイマーの Interval をいろいろ変えてみましたが今の所、うまく行っていません。
せっかくご丁寧に対応して下さってありがたいと思い、返信させて頂きました。
リストのコピーミスとかはないと思います。こちらでも何か自分のミスがあるかもしれないと思い、
考えています。リスト中にまだ知らないC#の機能がありますので、それも自分で詳しく勉強したい
と思います。
-
2つのTimerのTickイベントと、button1のClickイベントの設定を確認してみて下さい。
completeTimerのTick に「completeTimer_Tick」、
cycleTimerのTick に「cycleTimer_Tick」、
button1のClick に「button1_Click」をそれぞれ設定する必要があります。
(補足)イベントが上記のように設定されていて、かつTimerのEnabledを2つともtrueに設定しておくと、ボタンを押さなくても番号表示が始まります。