ここでは、C#の文法とWindows Formのコントロールについて、簡単に見て行きます。
超基本的なアルゴリズムをC#で記述する方法を学んでいきます。
今回は以下の様な事をプログラムしてみます。
1.ボタンをクリックすると、テキストボックスに虹の色を順に設定します。
また、もう一度ボタンをクリックすると元に戻します。
ここでは、順次処理をC#で記述する方法を学びます。
2.二つのラジオボタンのうち、片方をクリックしてオンにすると、もう片方をオフにします。
判断をC#で記述する方法を学びます。
3-1.3つのラジオボタンのうち一つをクリックしてオンにすると、他のラジオボタンをオフにするとともに、信号機の様に該当するラジオボタンに対応する信号を明るくします。また、テキストボックスに色を表示するとともに背景の色を変えます。
3-2.上記で、テキストボックスに緑・黄・赤と入力すると、該当するラジオボタンをオンにして、他をオフにするとともに、信号機の色をセットします。
分岐の処理をC#でどう表現するのかを学びます。
4.startボタンを押すと、写真を一定間隔で表示します。stopを押すまで、表示を繰り返します。
繰り返し処理をC#で表現する方法を学びます。
Visual Studio 2019を起動して、「Windows Formアプリケーション(.Net Framework)」を選択してプロジェクトを作成します。
超基本的なアルゴリズムをC#で記述する方法を学んでいきます。
今回は以下の様な事をプログラムしてみます。
1.ボタンをクリックすると、テキストボックスに虹の色を順に設定します。
また、もう一度ボタンをクリックすると元に戻します。
ここでは、順次処理をC#で記述する方法を学びます。
2.二つのラジオボタンのうち、片方をクリックしてオンにすると、もう片方をオフにします。
判断をC#で記述する方法を学びます。
3-1.3つのラジオボタンのうち一つをクリックしてオンにすると、他のラジオボタンをオフにするとともに、信号機の様に該当するラジオボタンに対応する信号を明るくします。また、テキストボックスに色を表示するとともに背景の色を変えます。
3-2.上記で、テキストボックスに緑・黄・赤と入力すると、該当するラジオボタンをオンにして、他をオフにするとともに、信号機の色をセットします。
分岐の処理をC#でどう表現するのかを学びます。
4.startボタンを押すと、写真を一定間隔で表示します。stopを押すまで、表示を繰り返します。
繰り返し処理をC#で表現する方法を学びます。
Visual Studio 2019を起動して、「Windows Formアプリケーション(.Net Framework)」を選択してプロジェクトを作成します。
画面のデザイン
FormにWindowsのコモンコントロールを載せて上記の様な状態にしていきます。
載せているコントロールと順番を書いていきます。
まず、左から、
Button 1つ
TextBox 7つ
起きます。
右に移って、まずは
Panel 1つ
を置きます。
このPanelの中に、
RadioButton 2つ
置きます。
左のRadioButtonをクリックして選択状態にしたうえで、右下のプロパティを探して、Textの右側に「左」と記入します。(ここで「と」は入力せず間の左だけ記入してください。)
同じく、右のRadioButtonをクリックして、右下のプロパティを探して、Textの右側に「右」と記入します。(ここも「と」は入力せず間の右だけ記入してください。)
そして、もう一度、左のRadioButtonをクリックして、今度は、Checkedというプロパティを探して、そこの右にFalseと記入されている所をクリックしてTrueにします(右はFalseのままにしておきます。)
更に、
Panel 1つ
RadioButton 3つ
PictureBox 3つ
TextBox 1つ
置きます。
RadioButtonのTextプロパティは順番に緑・黄・赤と記入しておきます。
また、緑のRadioButtonのCheckedプロパティをTrueにしておきます。(他はFalseのまま)
PictureBoxにはここでは、そのままにしておきましょう。
そして、TextBoxのTextプロパティに緑と記入して、BackColorプロパティに0,192,0を設定します。(0,192,0は緑色を示します。)
最後に、PictureBox 1つ
Button 2つ
設置します。
上のButtonのTextは「start」、下は「stop」と記入しておきます。
以上で、信号機の部分が少し違いますが、おおむね上の画像の様になります。
位置や大きさは余りこだわらないで結構です。適用にいろいろと動かしてみてください。
WindowsのメニューからWindowsアクセサリのペイントを探して起動します。
画面上のホームのイメージからサイズ変更を選択します。
そして垂直方向60ピクセル、水平方向60ピクセルとします。
この際、「縦横比を維持する」のチェックは外しておきましょう。
上の状態で、OKを押すと、下記の様な状況になります。なお、これからの作業を考慮してWindowのサイズを少し横に長くしました。
ここで、画面上の図形は楕円を選択しておき、色1をクリックして緑色、色2をクリックして緑色を選択しておきます。
そして、キャンバスの中に円を描きます。
この状態で円の内側をクリックしたまま少しずらして、円の淵の白い点を操作して、数の赤い枠内を参考にして図を60×60の大きさにした上で、キャンバスの位置に図形を合わせます。
ここまで出来たら、ファイルで名前を付けて保存で、green1.pngという名前で、保存します。
同様に、黄と赤の円を作って、yellow1.png、red1.pngという名前で保存しておきます。
同様に、灰色の円を作って、green0.png、yellow0.png、red0.pngという名前で保存します。
そして、右下のプロパティから、Imageを探し、右側のボタンの様なものをクリックします。
ここでは、すでに画像を設定していますが、プロジェクト リソース ファイルの下の「インポート」をクリックして、先ほど保存した画像を順番に取り込みます。
そして、信号機部分の緑に該当するPictureBoxには緑の円(green1.png)を、黄と赤に該当するのPictureBoxには灰色の円(yellow0.png、red0.png)を設定しておきます。
これで、この画面のデザインの最初に示した様な図になります。
デザインのサイゴンに一番下のPictureBoxに表示する画像を10個くらい準備して、適当なフォルダに格納しておきます。
載せているコントロールと順番を書いていきます。
まず、左から、
Button 1つ
TextBox 7つ
起きます。
右に移って、まずは
Panel 1つ
を置きます。
このPanelの中に、
RadioButton 2つ
置きます。
左のRadioButtonをクリックして選択状態にしたうえで、右下のプロパティを探して、Textの右側に「左」と記入します。(ここで「と」は入力せず間の左だけ記入してください。)
同じく、右のRadioButtonをクリックして、右下のプロパティを探して、Textの右側に「右」と記入します。(ここも「と」は入力せず間の右だけ記入してください。)
そして、もう一度、左のRadioButtonをクリックして、今度は、Checkedというプロパティを探して、そこの右にFalseと記入されている所をクリックしてTrueにします(右はFalseのままにしておきます。)
更に、
Panel 1つ
RadioButton 3つ
PictureBox 3つ
TextBox 1つ
置きます。
RadioButtonのTextプロパティは順番に緑・黄・赤と記入しておきます。
また、緑のRadioButtonのCheckedプロパティをTrueにしておきます。(他はFalseのまま)
PictureBoxにはここでは、そのままにしておきましょう。
そして、TextBoxのTextプロパティに緑と記入して、BackColorプロパティに0,192,0を設定します。(0,192,0は緑色を示します。)
最後に、PictureBox 1つ
Button 2つ
設置します。
上のButtonのTextは「start」、下は「stop」と記入しておきます。
以上で、信号機の部分が少し違いますが、おおむね上の画像の様になります。
位置や大きさは余りこだわらないで結構です。適用にいろいろと動かしてみてください。
画像の準備
ここまで出来たら、プログラムで使う画像を準備しましょう。WindowsのメニューからWindowsアクセサリのペイントを探して起動します。
画面上のホームのイメージからサイズ変更を選択します。
そして垂直方向60ピクセル、水平方向60ピクセルとします。
この際、「縦横比を維持する」のチェックは外しておきましょう。
上の状態で、OKを押すと、下記の様な状況になります。なお、これからの作業を考慮してWindowのサイズを少し横に長くしました。
ここで、画面上の図形は楕円を選択しておき、色1をクリックして緑色、色2をクリックして緑色を選択しておきます。
そして、キャンバスの中に円を描きます。
この状態で円の内側をクリックしたまま少しずらして、円の淵の白い点を操作して、数の赤い枠内を参考にして図を60×60の大きさにした上で、キャンバスの位置に図形を合わせます。
ここまで出来たら、ファイルで名前を付けて保存で、green1.pngという名前で、保存します。
同様に、黄と赤の円を作って、yellow1.png、red1.pngという名前で保存しておきます。
同様に、灰色の円を作って、green0.png、yellow0.png、red0.pngという名前で保存します。
PictureBoxの初期設定
二つ目のPanelの中の左の画像をクリックして選択します。そして、右下のプロパティから、Imageを探し、右側のボタンの様なものをクリックします。
ここでは、すでに画像を設定していますが、プロジェクト リソース ファイルの下の「インポート」をクリックして、先ほど保存した画像を順番に取り込みます。
そして、信号機部分の緑に該当するPictureBoxには緑の円(green1.png)を、黄と赤に該当するのPictureBoxには灰色の円(yellow0.png、red0.png)を設定しておきます。
これで、この画面のデザインの最初に示した様な図になります。
画像の準備 - その2
デザインのサイゴンに一番下のPictureBoxに表示する画像を10個くらい準備して、適当なフォルダに格納しておきます。
テキストボックスに虹の色を順に設定
それでは、順番にプログラムを書いていきましょう。
まずは、左のボタンを押したら、テキストボックスに虹色を設定します。
button1をダブルクリックします。
虹色を示す色をここでは下記とします。
赤、橙、黄、緑、青、藍、紫
テキストボックスの背景色を設定するプロパティは、BackColorプロパティでColor構造体で定義された色を格納します。
少し感覚が違うかもしれませんが、Red、Orange、Yellow、Green、Blue、Indigo、Purpleを設定しましょう。
以上を順番に並べて記述すれば、
textBox1.Text = "赤";
textBox1.BackColor = Color.Red;
textBox2.Text = "橙";
textBox2.BackColor = Color.Orange;
textBox3.Text = "黄";
textBox3.BackColor = Color.Yellow;
textBox4.Text = "緑";
textBox4.BackColor = Color.Green;
textBox5.Text = "青";
textBox5.BackColor = Color.Blue;
textBox6.Text = "藍";
textBox6.BackColor = Color.Indigo;
textBox7.Text = "紫";
textBox7.BackColor = Color.Purple;
となるので、これをカーソル位置に記入します。
全て保存して、開始ボタンを押してみましょう。プログラムが起動します。
ここでbutton1をクリックしてみます。
無事、虹色になりました。
そこで、再度button1をクリックしてみます。
残念ながらと言うか、当然ながら、何も起こりません。
それはそうですね。プログラム組んでませんからね。
では、一度、虹色にしてから、再度ボタンをクリックされたら、元に戻すにはどうすれば良いでしょう。
上記の処理を記述した、
private void button1_Click(object sender, EventArgs e)
は、button1がクリックされた時に行う処理を記述します。これをC#ではメソッドと呼んでいます。
何回クリックされようが、button1がクリックされたら、このbutton1_Clickの中に記述したプログラムが実行されます。
なので、このメソッドの中で、自分は虹色に設定するのか元に戻すのかを「判断」しなければいけません。
さて、それでは、最初に呼ばれた時と、次に呼ばれた時はどう区別すればよいでしょう。
そのためには、最初にボタンをクリックされた時と、二度目にクリックされた時の違いを見つけます。
実は色々なものが考えられます。
一つは、textBox1.Textは最初は""(長さ0の文字列)で、一度button1がクリックされると"赤"になります。なので、""だったら初めてクリックされること、""でなくなっていたら、すでに1度クリックされていることが分かります。
textBox1.Textが""かどうかは、ifw使うことで、判断できます。
if 条件式
ステートメント1;
else
ステートメント2;
これ全体で一つのステートメントを表し、ifステートメントもしくはif文と呼びます。
条件式はTrueかFalseの値を持ちます。条件が成立すれば値としてTrueを、成立していなければFalseとなります。
つまりbool型の値を持ちます。
条件式がTrueならステートメント1を実行します。条件式がFalseならステートメント2を実行します。
条件式は判断を行うための式で条件を()で囲んで記述します。
またステートメントは{}で囲むことで、複数のステートメントを記述できます。
そこで、ifステートメントは下記の様な形で記述されます。
if (条件)
{
ステートメント1−1;
ステートメント1−2;
・・・
ステートメント1−3;
ステートメント1−4;
}
else
{
ステートメント2ー1;
ステートメント2ー2;
・・・
ステートメント2−3;;
ステートメント2−4;
}
なお、それぞれのステートメントの前に空白を置くのはインデントと呼び、プログラムを読みやすくしているだけで、C#自身は空白を無視します。
同様にそれぞれのステートメントを改行で区切るのも読みやすくするためであって、無くても読みにくくなるだけでエラーではありません。
それぞれのステートメントの後ろにはステートメントの終了を示す;(セミコロン)を書きます。
なお}の後ろにセミコロンは要りません。
textBox1.Textが""かどうかとは
(textBox1.Text == "")
という条件式で判断します。
==は等しいことを比較することを表します。
textBox1.Textが""と等しければ式の値として True になります。
等しくなければ False になります。
つまり、上のプログラムは、次の様に書き換えることが出来ます。
if (textBox1.Text == "")
{
textBox1.Text = "赤";
textBox1.BackColor = Color.Red;
textBox2.Text = "橙";
textBox2.BackColor = Color.Orange;
textBox3.Text = "黄";
textBox3.BackColor = Color.Yellow;
textBox4.Text = "緑";
textBox4.BackColor = Color.Green;
textBox5.Text = "青";
textBox5.BackColor = Color.Blue;
textBox6.Text = "藍";
textBox6.BackColor = Color.Indigo;
textBox7.Text = "紫";
textBox7.BackColor = Color.Purple;
}
else
{
textBox1.Text = "";
textBox1.BackColor = Color.White;
textBox2.Text = "";
textBox2.BackColor = Color.White;
textBox3.Text = "";
textBox3.BackColor = Color.White;
textBox4.Text = "";
textBox4.BackColor = Color.White;
textBox5.Text = "";
textBox5.BackColor = Color.White;
textBox6.Text = "";
textBox6.BackColor = Color.White;
textBox7.Text = "";
textBox7.BackColor = Color.White;
}
そうすると、3度目のクリックを行うと、今度はtextBox1.Textが””になっているので、また虹色になることも分かります。
実際に上の様に修正して、保存した後に開始してみましょう。
如何ですか?
上手く動きましたか?
しかし、これはたまたま、textBox1.Textを元に戻していたから、使える手法です。
【練習問題】
textBox1.Textの内容で判断する以外の判断方法を考えて、プログラムを組んでみてください。
ヒント1)何回クリックされたかという情報を持っても判断可能です。0を含めて偶数回なら虹色にする。奇数回なら元に戻せば良い訳です。
ヒント2)偶数回クリックされたかどうかという情報を持っても判断可能です。
まずは、左のボタンを押したら、テキストボックスに虹色を設定します。
button1をダブルクリックします。
虹色を示す色をここでは下記とします。
赤、橙、黄、緑、青、藍、紫
テキストボックスの背景色を設定するプロパティは、BackColorプロパティでColor構造体で定義された色を格納します。
少し感覚が違うかもしれませんが、Red、Orange、Yellow、Green、Blue、Indigo、Purpleを設定しましょう。
以上を順番に並べて記述すれば、
textBox1.Text = "赤";
textBox1.BackColor = Color.Red;
textBox2.Text = "橙";
textBox2.BackColor = Color.Orange;
textBox3.Text = "黄";
textBox3.BackColor = Color.Yellow;
textBox4.Text = "緑";
textBox4.BackColor = Color.Green;
textBox5.Text = "青";
textBox5.BackColor = Color.Blue;
textBox6.Text = "藍";
textBox6.BackColor = Color.Indigo;
textBox7.Text = "紫";
textBox7.BackColor = Color.Purple;
となるので、これをカーソル位置に記入します。
全て保存して、開始ボタンを押してみましょう。プログラムが起動します。
ここでbutton1をクリックしてみます。
無事、虹色になりました。
そこで、再度button1をクリックしてみます。
残念ながらと言うか、当然ながら、何も起こりません。
それはそうですね。プログラム組んでませんからね。
では、一度、虹色にしてから、再度ボタンをクリックされたら、元に戻すにはどうすれば良いでしょう。
上記の処理を記述した、
private void button1_Click(object sender, EventArgs e)
は、button1がクリックされた時に行う処理を記述します。これをC#ではメソッドと呼んでいます。
何回クリックされようが、button1がクリックされたら、このbutton1_Clickの中に記述したプログラムが実行されます。
なので、このメソッドの中で、自分は虹色に設定するのか元に戻すのかを「判断」しなければいけません。
さて、それでは、最初に呼ばれた時と、次に呼ばれた時はどう区別すればよいでしょう。
そのためには、最初にボタンをクリックされた時と、二度目にクリックされた時の違いを見つけます。
実は色々なものが考えられます。
一つは、textBox1.Textは最初は""(長さ0の文字列)で、一度button1がクリックされると"赤"になります。なので、""だったら初めてクリックされること、""でなくなっていたら、すでに1度クリックされていることが分かります。
textBox1.Textが""かどうかは、ifw使うことで、判断できます。
if 条件式
ステートメント1;
else
ステートメント2;
これ全体で一つのステートメントを表し、ifステートメントもしくはif文と呼びます。
条件式はTrueかFalseの値を持ちます。条件が成立すれば値としてTrueを、成立していなければFalseとなります。
つまりbool型の値を持ちます。
条件式がTrueならステートメント1を実行します。条件式がFalseならステートメント2を実行します。
条件式は判断を行うための式で条件を()で囲んで記述します。
またステートメントは{}で囲むことで、複数のステートメントを記述できます。
そこで、ifステートメントは下記の様な形で記述されます。
if (条件)
{
ステートメント1−1;
ステートメント1−2;
・・・
ステートメント1−3;
ステートメント1−4;
}
else
{
ステートメント2ー1;
ステートメント2ー2;
・・・
ステートメント2−3;;
ステートメント2−4;
}
なお、それぞれのステートメントの前に空白を置くのはインデントと呼び、プログラムを読みやすくしているだけで、C#自身は空白を無視します。
同様にそれぞれのステートメントを改行で区切るのも読みやすくするためであって、無くても読みにくくなるだけでエラーではありません。
それぞれのステートメントの後ろにはステートメントの終了を示す;(セミコロン)を書きます。
なお}の後ろにセミコロンは要りません。
textBox1.Textが""かどうかとは
(textBox1.Text == "")
という条件式で判断します。
==は等しいことを比較することを表します。
textBox1.Textが""と等しければ式の値として True になります。
等しくなければ False になります。
つまり、上のプログラムは、次の様に書き換えることが出来ます。
if (textBox1.Text == "")
{
textBox1.Text = "赤";
textBox1.BackColor = Color.Red;
textBox2.Text = "橙";
textBox2.BackColor = Color.Orange;
textBox3.Text = "黄";
textBox3.BackColor = Color.Yellow;
textBox4.Text = "緑";
textBox4.BackColor = Color.Green;
textBox5.Text = "青";
textBox5.BackColor = Color.Blue;
textBox6.Text = "藍";
textBox6.BackColor = Color.Indigo;
textBox7.Text = "紫";
textBox7.BackColor = Color.Purple;
}
else
{
textBox1.Text = "";
textBox1.BackColor = Color.White;
textBox2.Text = "";
textBox2.BackColor = Color.White;
textBox3.Text = "";
textBox3.BackColor = Color.White;
textBox4.Text = "";
textBox4.BackColor = Color.White;
textBox5.Text = "";
textBox5.BackColor = Color.White;
textBox6.Text = "";
textBox6.BackColor = Color.White;
textBox7.Text = "";
textBox7.BackColor = Color.White;
}
そうすると、3度目のクリックを行うと、今度はtextBox1.Textが””になっているので、また虹色になることも分かります。
実際に上の様に修正して、保存した後に開始してみましょう。
如何ですか?
上手く動きましたか?
しかし、これはたまたま、textBox1.Textを元に戻していたから、使える手法です。
【練習問題】
textBox1.Textの内容で判断する以外の判断方法を考えて、プログラムを組んでみてください。
ヒント1)何回クリックされたかという情報を持っても判断可能です。0を含めて偶数回なら虹色にする。奇数回なら元に戻せば良い訳です。
ヒント2)偶数回クリックされたかどうかという情報を持っても判断可能です。
ラジオボタンの片方をオンにしたらもう片方をオフにする
ラジオボタンはクリックされていると黒くなり、クリックされていない状況では白くなることで、選択状態が分かる仕組みです。
まず、ここで行いたいのは、右上の左右のラジオボタンの処理です。
左のラジオボタンをクリックしたら、左のラジオボタンを黒くして、右のラジオボタンを白くします。
右のラジオボタンをクリックしたら、右のラジオボタンを黒くして、左のラジオボタンを黒くします。
となるようにしたいわけです。
次に、緑・黄・赤の3つのラジオボタンです。
これは、
緑のラジオボタンをクリックしたら、緑のラジオボタンを黒くして、黄と赤のラジオボタンを白くします。
黄のラジオボタンをクリックしたら、黄のラジオボタンを黒くして、緑と赤のラジオボタンを白くします。
赤のラジオボタンをクリックしたら、赤のラジオボタンを黒くして、緑と黄のラジオボタンを白くします。
となるようにしたい。
少しネタ晴らしになってしまいますが、ここで、また、開始ボタンを押してみてください。
そこで、左や右、緑・黄・赤のラジオボタンをクリックしてみてください。
どうです。
上手く動いてますよね。
そうなんです。これ、Panelに置いているのが一つのポイントです。
同じPanelに置かれたラジオボタンは一つのグループを片付くります。そして同じグループに属するラジオボタンは、最後にクリックされた一つだけがオンになるようにWindowsが制御しています。
なので、特にプログラムしなくても、上に書いたことは実現で来てしまいました。
しかし、あくまでもラジオボタンだけの話ですから、信号機やテキストボックスは変化しません。
まず、ここで行いたいのは、右上の左右のラジオボタンの処理です。
左のラジオボタンをクリックしたら、左のラジオボタンを黒くして、右のラジオボタンを白くします。
右のラジオボタンをクリックしたら、右のラジオボタンを黒くして、左のラジオボタンを黒くします。
となるようにしたいわけです。
次に、緑・黄・赤の3つのラジオボタンです。
これは、
緑のラジオボタンをクリックしたら、緑のラジオボタンを黒くして、黄と赤のラジオボタンを白くします。
黄のラジオボタンをクリックしたら、黄のラジオボタンを黒くして、緑と赤のラジオボタンを白くします。
赤のラジオボタンをクリックしたら、赤のラジオボタンを黒くして、緑と黄のラジオボタンを白くします。
となるようにしたい。
少しネタ晴らしになってしまいますが、ここで、また、開始ボタンを押してみてください。
そこで、左や右、緑・黄・赤のラジオボタンをクリックしてみてください。
どうです。
上手く動いてますよね。
そうなんです。これ、Panelに置いているのが一つのポイントです。
同じPanelに置かれたラジオボタンは一つのグループを片付くります。そして同じグループに属するラジオボタンは、最後にクリックされた一つだけがオンになるようにWindowsが制御しています。
なので、特にプログラムしなくても、上に書いたことは実現で来てしまいました。
しかし、あくまでもラジオボタンだけの話ですから、信号機やテキストボックスは変化しません。
ラジオボタンに合わせて信号の色を変える
ラジオボタンがチェックされているかどうかは、chekedプロパティを確認します。
ラジオボタンが選択されている状態なら、CheckedがTrueになり、そうでなければFalseです。
なので、例えば、radioButton3が選択されているかどうかで判断する記述は、
if (radioButton1.Checked)
{
チェックされている場合の処理
}
else
{
チェックされていない場合の処理
}
と書けます。
なので、
緑のラジオボタンが選択されていたら、緑の円の画像を表示する。
黄のラジオボタンが選択されていたら、黄の円の画像を表示する。
赤のラジオボタンが選択されていたら、赤の円の画像を表示する。
処理をプログラムにすれば良いでしょう。
???
果たしてそうでしょうか。
これだと、一度緑がついたら緑の円が表示されっぱなしになってしまいます。黄も赤も同様です。
なので、少し修正しましょう。
緑のラジオボタンが選択されていたら、緑の円の画像を表示する。選択されていなければ緑の場所に灰色の円を表示する。
黄のラジオボタンが選択されていたら、黄の円の画像を表示する。選択されていなければ黄の場所に灰色の円を表示する。
赤のラジオボタンが選択されていたら、赤の円の画像を表示する。選択されていなければ赤の場所に灰色の円を表示する。
これで良いでしょうか。
では、プログラムを書きましょう。
緑のラジオボタンをダブルクリックしましょう。(これで緑のラジオボタンをクリックした時の処理になるはずですよね。)
プログラムを書く所に画面が切り替わります。
じゃあ、緑のボタンが押された場合の処理を書きましょう。
あれ、上に書いた処理は、緑のボタンが押されたかどうかではないですよね。選択されているかどうかです。
緑のボタンを押したんだから、条件判断は要らないんじゃないですか?
あれ、クリックされた時っていうなら、クリックされない状況ってどうするの?
う〜ん、なんかこんがらがってきました。
はい、少し待って落ち着いてください。
実は、この部分って、クリックされたら場合の処理を書く場所ではないのです。
メソッドの名前をよく見てください。
radioButton3_CheckedChanged
選択状態が変化した場合の処理を書くばしょになります。変化した場合と言うのは、選択されていない状態から選択された状態に変わった時や、選択されている状態から選択されていない状態になった場合です。
なので、選択された状態になった場合と選択されなくなった状態をここに書けば良いのです。なので、上に書いた様に「緑のラジオボタンが選択されていたら、緑の円の画像を表示する。選択されていなければ緑の場所に灰色の円を表示する。」処理を書きます。
円の画像はリソースにインポートしました。そこから取り込むときは、Properties.Resourceから当該のデータを指定します。
if (radioButton3.Checked)
pictureBox1.Image = Properties.Resources.green1;
else
pictureBox1.Image = Properties.Resources.green0;
上に書いた様な記述になります。黄も赤も同様です。
黄のラジオボタンでダブルクリックした先で、黄色の処理を、赤のラジオボタンをダブルクリックした先で、赤の処理を書きます。
private void radioButton3_CheckedChanged(object sender, EventArgs e)
{
if (radioButton3.Checked)
pictureBox1.Image = Properties.Resources.green1;
else
pictureBox1.Image = Properties.Resources.green0;
}
private void radioButton4_CheckedChanged(object sender, EventArgs e)
{
if (radioButton4.Checked)
pictureBox2.Image = Properties.Resources.yellow1;
else
pictureBox2.Image = Properties.Resources.yellow0;
}
private void radioButton5_CheckedChanged(object sender, EventArgs e)
{
if (radioButton5.Checked)
pictureBox3.Image = Properties.Resources.red1;
else
pictureBox3.Image = Properties.Resources.red0;
}
こんな形にかけます。
ここで、radioButton4の処理を書く際はradioButton3の処理をコピペすれば早く作業が出来ます。(radioButton5も同じです。)
ただし、コピペした場合は、ちゃんとradioButton3をradioButton4、radioButton5に修正することと、pictureBox1をpictureBox2、pictureBox3に変更すること、更にgreenをyellow、redに修正することを忘れないでください。
一つでも忘れるときちんと動きません。
さ、また、全て保存して開始を押してみましょう。
キチンと色が変わりましたか?
ただ、テキストボックスは変わりませんよね。
それでは、テキストボックスの処理を入れましょう。
緑のボタンが押されたら、テキストボックスに"緑"と表示して背景を緑にしましょう。
private void radioButton3_CheckedChanged(object sender, EventArgs e)
{
if (radioButton3.Checked)
{
pictureBox1.Image = Properties.Resources.green1;
textBox8.Text = "緑";
textBox8.BackColor = Color.Green;
}
else
pictureBox1.Image = Properties.Resources.green0;
}
こんな感じです。elseの方は書かなくて良いのかって思いますよね。elseは他のラジオボタンが押されている時ですから、それぞれの色の処理で行いますので、抜けはありません。
逆に、ここに書いてしまうと、同じ条件が発生した場合の処理を2か所に書くことになるので、将来的にバグを生み出す恐れがあります。(例えば何か修正が入った時に片一方しか直さない可能性があります。)
さあ、またすべて保存して開始ボタンをクリックして試してみましょう。
こんな感じになればOKです。
ラジオボタンが選択されている状態なら、CheckedがTrueになり、そうでなければFalseです。
なので、例えば、radioButton3が選択されているかどうかで判断する記述は、
if (radioButton1.Checked)
{
チェックされている場合の処理
}
else
{
チェックされていない場合の処理
}
と書けます。
なので、
緑のラジオボタンが選択されていたら、緑の円の画像を表示する。
黄のラジオボタンが選択されていたら、黄の円の画像を表示する。
赤のラジオボタンが選択されていたら、赤の円の画像を表示する。
処理をプログラムにすれば良いでしょう。
???
果たしてそうでしょうか。
これだと、一度緑がついたら緑の円が表示されっぱなしになってしまいます。黄も赤も同様です。
なので、少し修正しましょう。
緑のラジオボタンが選択されていたら、緑の円の画像を表示する。選択されていなければ緑の場所に灰色の円を表示する。
黄のラジオボタンが選択されていたら、黄の円の画像を表示する。選択されていなければ黄の場所に灰色の円を表示する。
赤のラジオボタンが選択されていたら、赤の円の画像を表示する。選択されていなければ赤の場所に灰色の円を表示する。
これで良いでしょうか。
では、プログラムを書きましょう。
緑のラジオボタンをダブルクリックしましょう。(これで緑のラジオボタンをクリックした時の処理になるはずですよね。)
プログラムを書く所に画面が切り替わります。
じゃあ、緑のボタンが押された場合の処理を書きましょう。
あれ、上に書いた処理は、緑のボタンが押されたかどうかではないですよね。選択されているかどうかです。
緑のボタンを押したんだから、条件判断は要らないんじゃないですか?
あれ、クリックされた時っていうなら、クリックされない状況ってどうするの?
う〜ん、なんかこんがらがってきました。
はい、少し待って落ち着いてください。
実は、この部分って、クリックされたら場合の処理を書く場所ではないのです。
メソッドの名前をよく見てください。
radioButton3_CheckedChanged
選択状態が変化した場合の処理を書くばしょになります。変化した場合と言うのは、選択されていない状態から選択された状態に変わった時や、選択されている状態から選択されていない状態になった場合です。
なので、選択された状態になった場合と選択されなくなった状態をここに書けば良いのです。なので、上に書いた様に「緑のラジオボタンが選択されていたら、緑の円の画像を表示する。選択されていなければ緑の場所に灰色の円を表示する。」処理を書きます。
円の画像はリソースにインポートしました。そこから取り込むときは、Properties.Resourceから当該のデータを指定します。
if (radioButton3.Checked)
pictureBox1.Image = Properties.Resources.green1;
else
pictureBox1.Image = Properties.Resources.green0;
上に書いた様な記述になります。黄も赤も同様です。
黄のラジオボタンでダブルクリックした先で、黄色の処理を、赤のラジオボタンをダブルクリックした先で、赤の処理を書きます。
private void radioButton3_CheckedChanged(object sender, EventArgs e)
{
if (radioButton3.Checked)
pictureBox1.Image = Properties.Resources.green1;
else
pictureBox1.Image = Properties.Resources.green0;
}
private void radioButton4_CheckedChanged(object sender, EventArgs e)
{
if (radioButton4.Checked)
pictureBox2.Image = Properties.Resources.yellow1;
else
pictureBox2.Image = Properties.Resources.yellow0;
}
private void radioButton5_CheckedChanged(object sender, EventArgs e)
{
if (radioButton5.Checked)
pictureBox3.Image = Properties.Resources.red1;
else
pictureBox3.Image = Properties.Resources.red0;
}
こんな形にかけます。
ここで、radioButton4の処理を書く際はradioButton3の処理をコピペすれば早く作業が出来ます。(radioButton5も同じです。)
ただし、コピペした場合は、ちゃんとradioButton3をradioButton4、radioButton5に修正することと、pictureBox1をpictureBox2、pictureBox3に変更すること、更にgreenをyellow、redに修正することを忘れないでください。
一つでも忘れるときちんと動きません。
さ、また、全て保存して開始を押してみましょう。
キチンと色が変わりましたか?
ただ、テキストボックスは変わりませんよね。
それでは、テキストボックスの処理を入れましょう。
緑のボタンが押されたら、テキストボックスに"緑"と表示して背景を緑にしましょう。
private void radioButton3_CheckedChanged(object sender, EventArgs e)
{
if (radioButton3.Checked)
{
pictureBox1.Image = Properties.Resources.green1;
textBox8.Text = "緑";
textBox8.BackColor = Color.Green;
}
else
pictureBox1.Image = Properties.Resources.green0;
}
こんな感じです。elseの方は書かなくて良いのかって思いますよね。elseは他のラジオボタンが押されている時ですから、それぞれの色の処理で行いますので、抜けはありません。
逆に、ここに書いてしまうと、同じ条件が発生した場合の処理を2か所に書くことになるので、将来的にバグを生み出す恐れがあります。(例えば何か修正が入った時に片一方しか直さない可能性があります。)
さあ、またすべて保存して開始ボタンをクリックして試してみましょう。
こんな感じになればOKです。
テキストボックスの入力に合わせてラジオボタンと信号機の色をセットする
さて、ここまでで、ラジオボタンに合わせて信号機やテキストボックスの色を設定できる様になりました。
ここでは、テキストボックスに入力したら、ラジオボタンと信号機の色を設定しましょう。
テキストボックスの内容はTextプロパティで判断できます。
そこで、テキストボックスの内容が"緑"だったら、緑のラジオボタンをセットして、緑の信号に緑の円を表示します。
同様にテキストボックスの内容が"黄"だったら、黄のラジオボタンをセットして、黄の信号に黄の円を表示し、
テキストボックスの内容が"赤"だったら、赤のラジオボタンをセットして、赤の信号に赤の円を表示します。
これで良いですか?
では、テキストボックスをダブルクリックします。
上の処理をC#で記述すると、下記の様になります。
private void textBox8_TextChanged(object sender, EventArgs e)
{
switch (textBox8.Text)
{
case "緑":
{
radioButton3.Checked = true;
break;
}
case "黄":
{
radioButton4.Checked = true;
break;
}
case "赤":
{
radioButton5.Checked = true;
break;
}
default:
break;
}
}
textBox8.Textの値で処理を選択するので、Switchステートメント(Switch文)を使います。
Switch文は下記の様に記述します。
Switch (変数)
case 値1:
{
ステートメント1;
break;
}
case 値2:
{
ステートメント2;
break;
}
case 値3:
{
ステートメント3;
break;
}
default:
{
ステートメント4;
break;
}
ステートメントは複数書けます。また、break;が無いと次のcase:のステートメントが実行されます。
とここまでの説明を読んでいて、あれ、って思いました?
信号機の処理は書かなくて良いの?って思いますよね。上でわざわざこれで良いですかって書いている位なので、何かあるかなと思った方は凄いです。
あくまでもこれはWindowsの仕様だからです。
というのは、textBox8.Textに緑と入力すると、この処理が呼ばれて、radioButton3.chekedにTrueがセットされます。
もともと緑が選択されていれば、何も起こりませんが、仮に、黄や赤だった場合は、radioButton3が変化します。そのため、radioButton3_CheckedChangedが自動的に呼び出されるのです。
そう、このメソッドは、クリックされたら動くのではありませんでした。選択の状態が変化したら呼び出されるんでした。そして、radioButton3が変化するとグループ内のradioButton4、radioButton5が変化するので、radioButton4_CheckedChanged、radioButton4_CheckedChanged5が自動的に呼び出されるのです。
ある意味、Windowsのプログラミングの面白いところです。
青と書いても、何も起こりません。
ここでは、テキストボックスに入力したら、ラジオボタンと信号機の色を設定しましょう。
テキストボックスの内容はTextプロパティで判断できます。
そこで、テキストボックスの内容が"緑"だったら、緑のラジオボタンをセットして、緑の信号に緑の円を表示します。
同様にテキストボックスの内容が"黄"だったら、黄のラジオボタンをセットして、黄の信号に黄の円を表示し、
テキストボックスの内容が"赤"だったら、赤のラジオボタンをセットして、赤の信号に赤の円を表示します。
これで良いですか?
では、テキストボックスをダブルクリックします。
上の処理をC#で記述すると、下記の様になります。
private void textBox8_TextChanged(object sender, EventArgs e)
{
switch (textBox8.Text)
{
case "緑":
{
radioButton3.Checked = true;
break;
}
case "黄":
{
radioButton4.Checked = true;
break;
}
case "赤":
{
radioButton5.Checked = true;
break;
}
default:
break;
}
}
textBox8.Textの値で処理を選択するので、Switchステートメント(Switch文)を使います。
Switch文は下記の様に記述します。
Switch (変数)
case 値1:
{
ステートメント1;
break;
}
case 値2:
{
ステートメント2;
break;
}
case 値3:
{
ステートメント3;
break;
}
default:
{
ステートメント4;
break;
}
ステートメントは複数書けます。また、break;が無いと次のcase:のステートメントが実行されます。
とここまでの説明を読んでいて、あれ、って思いました?
信号機の処理は書かなくて良いの?って思いますよね。上でわざわざこれで良いですかって書いている位なので、何かあるかなと思った方は凄いです。
あくまでもこれはWindowsの仕様だからです。
というのは、textBox8.Textに緑と入力すると、この処理が呼ばれて、radioButton3.chekedにTrueがセットされます。
もともと緑が選択されていれば、何も起こりませんが、仮に、黄や赤だった場合は、radioButton3が変化します。そのため、radioButton3_CheckedChangedが自動的に呼び出されるのです。
そう、このメソッドは、クリックされたら動くのではありませんでした。選択の状態が変化したら呼び出されるんでした。そして、radioButton3が変化するとグループ内のradioButton4、radioButton5が変化するので、radioButton4_CheckedChanged、radioButton4_CheckedChanged5が自動的に呼び出されるのです。
ある意味、Windowsのプログラミングの面白いところです。
青と書いても、何も起こりません。
stopを押すまで画像の表示を繰り返す
さて、文法編の最後です。
まあ、文法編と書いておきながら、あまりきちんとC#の文法書いてませんが。
さて、ここでは、startボタンが押されたらあらかじめ用意しておいた画像をstopボタンが押されるまで繰り返して表示する処理について学んでいきます。
ここは、実は少し難しい飛躍があります。
まずは、単純に処理を考えてみます。
予め、画像は用意しておきます。(どうすれば良いかは後で考えます。)
startボタンが押されたら呼ばれる処理
---------ここから----------
stopボタンが押されたら、終了する
次に画像を表示する
最初に戻る
----------ここまで----------
という処理を行えばよさそうです。
さて、stopボタンが押されたら、ストップボタンが押された時に呼ばれる処理が呼ばれます。
stopボタンを押されたら呼び出される処理
----------ここから----------
startボタンを押された処理を止める
----------ここまで----------
???????????????????
色々な疑問がわきます。
まず、
1.startボタンを押された場合に行う処理で、stopボタンが押されたかどうか分かるのでしょうか?
2.画像を次々に表示する方法は?
3.次々に表示されて、どんどん画像が変わってしまうから、人間の目で追えるの?
4.stopボタンを押されたら行う処理の中で、どうやってstartボタンの処理を止めることが出来るの?
先に4.の答えを書いておくと、直接別のメソッドに対して止まれと言うことは出来ません。
また、3.について書くと、人間の目では負えません。
更に言うと、画像を表示してすぐにまた画像を表示してと言うことを繰り返すので、キーボードやマウスの入力も受け付けずにWindowsのシステムが止まってしまったかのような状態になります。stopボタンを押してもうんともすんとも言いません。いわゆる無限ループに陥ってしまった状態になります。
そこで、画像を表示したら1秒間待つ事にしましょう。
startボタンが押されたら呼ばれる処理
---------ここから----------
stopボタンが押されたら、終了する
次に画像を表示する
5秒間待つ
最初に戻る
----------ここまで----------
当然待っている間は、Windowsの他の処理が行われる様にしたいですね。
そこで、待っている処理として、次の処理を使います。
Task.Delay(待つ時間をミリ秒で指定);
1秒は1000ミリ秒です。
Task.Delay(5000);
と書きます。そして、更に、ここで、一つおまじないを掛けます。
await Task.Delay(5000);
と書きます。こうすることで、待っている間、Windowsが他の処理を進めてくれると覚えてください。
そこで、1.と4.の質問に戻りましょう。
ここで、また、一つ考え方を覚えてください。
stopが押されたか押されていないかと言うのは、二つの状態を区別できれば良いことが分かりますか?
stopが押されていない状態
そして
stopが押された状態
です。
つまり、bool型の変数例えば名前をon_stopを用意して、
stopが押されていない状態の場合は on_stopがFalse
そして
stopが押された状態の場合は、on_stopがTrueになっていれば良いですよね。
なので、
startボタンが押されたら呼ばれる処理
---------ここから----------
on_stop = false;
戻る場所:
on_stopなら終了する
次に画像を表示する
await Task.Delay(5000);
戻る場所に戻る
----------ここまで----------
stopボタンが押されたら呼ばれる処理
---------ここから----------
on_stop = True;
----------ここまで----------
こうすることで、startボタンが押された後は、stopボタンが押されるまで、画像表示しては1秒待つことを繰り返すことになります。
それでは、次の画像を表示するにはどうすれば良いでしょう?
上の画面のデザインのところで、画像は10個程度用意しておくことにしていました。
そこで、10個分の画像を用意しておき、そこに格納する方法が考えられます。
画像に関しては、いろいろと覚えなければいけないことが多いので、Windows FormではPictureBoxという画像表示用のコンポーネントを提供してくれています。
そして、PictureBoxにはImageLocationというプロパティがあり、画像が保管されている場所を指定してあげれば、画像を表示してくれます。
なので、10個分の画像ファイルの場所の情報(パスと言い、普通の文字列(String型)で表せます。)を持っておけばよいことが分かります。
文字列を使う場合は、
string a;
string a = "文字列1";
の様に宣言します。これで、aは文字列を格納することが出来る変数だと宣言したことになります。
10個分の文字列を指定する場合は、どうすれば良いでしょうか。
同じ型のものを複数宣言する場合は、配列と言うものを使います。
10個の文字列の配列の宣言は下記の様に記述します。
string[] str_ImageLoc = new string[10];
そこで、今、画像1〜10の名前がa1.jpg、a2.jpg、a3.jpg...a9.jpg,a10.jpgで、C:\work\imagesというフォルダに格納されているものとします。
すると、この様に書くことで、画像の場所を準備出来ます。
string Image_folder = @"C:\work\images\";
string[] ImageLoc = new string[] { "a1.jpg", "a2.jpg", "a3.jpg", "a4.jpg", "a5.jpg", "a6.jpg", "a7.jpg", "a8.jpg", "a9.jpg", "a10.jpg" };
使うときは、Image_folder + ImageLoc[0]と書くことで、C:\work\images\a1.jpgが表現できます。
10個の配列の場合、最初の変数は[0]、10個目の変数は[9]と指定することで、読み書きできます。
そこで、何番目の画像を表示するのかという情報(nとしておきます)を持っていれば、nに次の様に情報を持たせれば、10個の画像が順番に表示されます。
0 1 2 3 4 5 6 7 8 9 10ではなくて0 1 2 3 4 5 6 7 8 9 10ではなくて0 1 2 3 4 5
次の画像は、nに1を加えてあげれば次を示せます。ただし、10になったら0にするという処理を行えば可能です。
ただ、どうしましょう。もっと簡単に1を加えて数を10で割って、余りをnに格納すれば良いことが分かりますか?
nに1を加えたものを10で割った余りをnに格納するということをC#ではn = (n+1)%10;と書きます。%で割り算した結果の余りを表します。
nは0から9を格納すればよいので、int(整数)の方でよさそうです。そこでまずは、どこかでnを宣言して0で初期化します。
つまり、startボタンが押されたら呼ばれる処理は
---------ここから----------
on_stop = false;
int n;
n = 0;
戻る場所:
on_stopなら終了する
pictureBox4.ImageLocation = Image_folder + ImageLoc[n];
await Task.Delay(5000);
n = (n + 1)%10;
戻る場所に戻る
----------ここまで----------
となります。
さ、最後に繰り返しの部分をC#で書きます。
最初に条件を評価しています。
これはwhileステートメントで書きます。
while (条件)
ステートメント(繰り返しの処理)
と書くことで、条件が成立している間、ステートメントを繰り返します。
そこで、on_stopなら終了するを、on_stopがFalseの間、下記を繰り返すと読み替えてください。
そうすると、繰り返しの部分は、次のように表現できます。
while (!on_stop)
{
pictureBox4.ImageLocation = Image_folder + ImageLoc[n];
await Task.Delay(5000);
n = (n + 1)%10;
}
それでは、実際にプログラムを書いてみます。
まず、フォームのデザイン画面状のstartボタンをダブルクリックして、startボタンが押されたら呼ばれる処理を書きます。
次に、stopボタンをダブルクリックして、stopボタンが押されたら呼ばれる処理を書きます。
さ、それでは、すべて保存して、開始ボタンを押して実行しましょう
イヤイヤ、ちょっと待ってください。
画面の下を見てください。1エラーと表示されていませんか。(もっと多いかもしれませんが。)
await演算子は、非同期メソッド内でのみ使用できます。このメソッドに'async'修飾子を指定し、戻り値の方を'Task'に変更することを検討してください。
と書かれています。
何が書かれているか、全くちんぷんかんぷんですよね。
ここでは、詳細の説明は省きます。
エラーの説明の行を観ると、132になっています。そこで、132行目をマウスでクリックします。すると、下の様に、132行目の左側に何やら表示されています。
その左側の図の上にマウスを移動してみると、少し図形が変化して、下向きの矢印のようなものが表示されるので、そこをクリックしてください。
すると、
メソッドを非同期にします
メソッドを非同期にする(voidを維持)
と表示されるので、下の「メソッドを非同期にする(voidを維持)」をマウスでクリックしてください。
すると左側の図形が消えて、エラーも0になります。
そこで、すべて保存して、開始を押してみましょう。
プログラムが起動したら、startボタンを押してみてください。画像が表示されたでしょうか。
また、stopを押したら、表示が止まりましたか?
あれ、画像が良く分からないですって?
PictureBox4の大きさが小さいので、大きな画像を表示すると一部分しか表示されません。
全部表示させたいですよね。
そこで、もう一つのおまじないとして、画像を表示する前に次の分を加えてみてください。
pictureBox4.SizeMode = PictureBoxSizeMode.StretchImage;
また、stopもstop下かどうかわかりにくいので、次の分を入れてみましょう。
pictureBox4.ImageLocation = "";
さあ、今度はどうですか?無事画像が表示されましたか?
また、この画像を表示している間に、button1や緑・黄・赤のラジオボタンをクリックしてみてください。
画像表示とは独立して、表示が変わりますでしょう。この画像表示の処理と他の処理が独立して動作する状態を非同期に動作していると表現します。
そうです、先ほど出ていたエラーに関連する話です。
awaitは非同期に動作しているメソッドでしか使用できないというエラーでした。
そして、startボタンを押したときの処理の部分をもう一度、見てみてください。
private async void button2_Click(object sender, EventArgs e)
asyncという言葉が書かれています。
これは、button2_Clickという処理(メソッド)が非同期で動作するメソッドであることを示す言葉です。先ほどのエラー訂正で、「メソッドを非同期にする(voidを維持)」を選択したことで、Visual Studio 2019が自動的に修正してくれたのです。
今のtころ理屈は分からなくて結構ですが、Windowsにはもともと複数のスレッドを同時に並行して動作させることが出来ます。
C#はプログラマーがあまり意識せずに、自動で並行して動作するスレッド処理を記述することが可能になっています。
今回はその機能を使用しました。
まあ、文法編と書いておきながら、あまりきちんとC#の文法書いてませんが。
さて、ここでは、startボタンが押されたらあらかじめ用意しておいた画像をstopボタンが押されるまで繰り返して表示する処理について学んでいきます。
ここは、実は少し難しい飛躍があります。
ボタンを押された時の処理
まずは、単純に処理を考えてみます。
予め、画像は用意しておきます。(どうすれば良いかは後で考えます。)
startボタンが押されたら呼ばれる処理
---------ここから----------
stopボタンが押されたら、終了する
次に画像を表示する
最初に戻る
----------ここまで----------
という処理を行えばよさそうです。
さて、stopボタンが押されたら、ストップボタンが押された時に呼ばれる処理が呼ばれます。
stopボタンを押されたら呼び出される処理
----------ここから----------
startボタンを押された処理を止める
----------ここまで----------
???????????????????
色々な疑問がわきます。
まず、
1.startボタンを押された場合に行う処理で、stopボタンが押されたかどうか分かるのでしょうか?
2.画像を次々に表示する方法は?
3.次々に表示されて、どんどん画像が変わってしまうから、人間の目で追えるの?
4.stopボタンを押されたら行う処理の中で、どうやってstartボタンの処理を止めることが出来るの?
先に4.の答えを書いておくと、直接別のメソッドに対して止まれと言うことは出来ません。
また、3.について書くと、人間の目では負えません。
更に言うと、画像を表示してすぐにまた画像を表示してと言うことを繰り返すので、キーボードやマウスの入力も受け付けずにWindowsのシステムが止まってしまったかのような状態になります。stopボタンを押してもうんともすんとも言いません。いわゆる無限ループに陥ってしまった状態になります。
そこで、画像を表示したら1秒間待つ事にしましょう。
startボタンが押されたら呼ばれる処理
---------ここから----------
stopボタンが押されたら、終了する
次に画像を表示する
5秒間待つ
最初に戻る
----------ここまで----------
当然待っている間は、Windowsの他の処理が行われる様にしたいですね。
そこで、待っている処理として、次の処理を使います。
Task.Delay(待つ時間をミリ秒で指定);
1秒は1000ミリ秒です。
Task.Delay(5000);
と書きます。そして、更に、ここで、一つおまじないを掛けます。
await Task.Delay(5000);
と書きます。こうすることで、待っている間、Windowsが他の処理を進めてくれると覚えてください。
そこで、1.と4.の質問に戻りましょう。
ここで、また、一つ考え方を覚えてください。
stopが押されたか押されていないかと言うのは、二つの状態を区別できれば良いことが分かりますか?
stopが押されていない状態
そして
stopが押された状態
です。
つまり、bool型の変数例えば名前をon_stopを用意して、
stopが押されていない状態の場合は on_stopがFalse
そして
stopが押された状態の場合は、on_stopがTrueになっていれば良いですよね。
なので、
startボタンが押されたら呼ばれる処理
---------ここから----------
on_stop = false;
戻る場所:
on_stopなら終了する
次に画像を表示する
await Task.Delay(5000);
戻る場所に戻る
----------ここまで----------
stopボタンが押されたら呼ばれる処理
---------ここから----------
on_stop = True;
----------ここまで----------
こうすることで、startボタンが押された後は、stopボタンが押されるまで、画像表示しては1秒待つことを繰り返すことになります。
それでは、次の画像を表示するにはどうすれば良いでしょう?
画像の処理
画像の処理については、いろいろな方法があります。上の画面のデザインのところで、画像は10個程度用意しておくことにしていました。
そこで、10個分の画像を用意しておき、そこに格納する方法が考えられます。
画像に関しては、いろいろと覚えなければいけないことが多いので、Windows FormではPictureBoxという画像表示用のコンポーネントを提供してくれています。
そして、PictureBoxにはImageLocationというプロパティがあり、画像が保管されている場所を指定してあげれば、画像を表示してくれます。
なので、10個分の画像ファイルの場所の情報(パスと言い、普通の文字列(String型)で表せます。)を持っておけばよいことが分かります。
文字列を使う場合は、
string a;
string a = "文字列1";
の様に宣言します。これで、aは文字列を格納することが出来る変数だと宣言したことになります。
10個分の文字列を指定する場合は、どうすれば良いでしょうか。
同じ型のものを複数宣言する場合は、配列と言うものを使います。
10個の文字列の配列の宣言は下記の様に記述します。
string[] str_ImageLoc = new string[10];
【配列の宣言の例】
整数型(int)5個からなる配列の宣言
int[] int_array = new int[5];
一般的にある型の配列は、
型名[] 配列の名前 = new 型名[配列の個数];
と宣言します。
整数型(int)5個からなる配列の宣言
int[] int_array = new int[5];
一般的にある型の配列は、
型名[] 配列の名前 = new 型名[配列の個数];
と宣言します。
そこで、今、画像1〜10の名前がa1.jpg、a2.jpg、a3.jpg...a9.jpg,a10.jpgで、C:\work\imagesというフォルダに格納されているものとします。
すると、この様に書くことで、画像の場所を準備出来ます。
string Image_folder = @"C:\work\images\";
string[] ImageLoc = new string[] { "a1.jpg", "a2.jpg", "a3.jpg", "a4.jpg", "a5.jpg", "a6.jpg", "a7.jpg", "a8.jpg", "a9.jpg", "a10.jpg" };
使うときは、Image_folder + ImageLoc[0]と書くことで、C:\work\images\a1.jpgが表現できます。
10個の配列の場合、最初の変数は[0]、10個目の変数は[9]と指定することで、読み書きできます。
そこで、何番目の画像を表示するのかという情報(nとしておきます)を持っていれば、nに次の様に情報を持たせれば、10個の画像が順番に表示されます。
0 1 2 3 4 5 6 7 8 9 10ではなくて0 1 2 3 4 5 6 7 8 9 10ではなくて0 1 2 3 4 5
次の画像は、nに1を加えてあげれば次を示せます。ただし、10になったら0にするという処理を行えば可能です。
ただ、どうしましょう。もっと簡単に1を加えて数を10で割って、余りをnに格納すれば良いことが分かりますか?
nに1を加えたものを10で割った余りをnに格納するということをC#ではn = (n+1)%10;と書きます。%で割り算した結果の余りを表します。
nは0から9を格納すればよいので、int(整数)の方でよさそうです。そこでまずは、どこかでnを宣言して0で初期化します。
つまり、startボタンが押されたら呼ばれる処理は
---------ここから----------
on_stop = false;
int n;
n = 0;
戻る場所:
on_stopなら終了する
pictureBox4.ImageLocation = Image_folder + ImageLoc[n];
await Task.Delay(5000);
n = (n + 1)%10;
戻る場所に戻る
----------ここまで----------
となります。
さ、最後に繰り返しの部分をC#で書きます。
最初に条件を評価しています。
これはwhileステートメントで書きます。
while (条件)
ステートメント(繰り返しの処理)
と書くことで、条件が成立している間、ステートメントを繰り返します。
そこで、on_stopなら終了するを、on_stopがFalseの間、下記を繰り返すと読み替えてください。
そうすると、繰り返しの部分は、次のように表現できます。
while (!on_stop)
{
pictureBox4.ImageLocation = Image_folder + ImageLoc[n];
await Task.Delay(5000);
n = (n + 1)%10;
}
それでは、実際にプログラムを書いてみます。
まず、フォームのデザイン画面状のstartボタンをダブルクリックして、startボタンが押されたら呼ばれる処理を書きます。
次に、stopボタンをダブルクリックして、stopボタンが押されたら呼ばれる処理を書きます。
さ、それでは、すべて保存して、開始ボタンを押して実行しましょう
イヤイヤ、ちょっと待ってください。
画面の下を見てください。1エラーと表示されていませんか。(もっと多いかもしれませんが。)
await演算子は、非同期メソッド内でのみ使用できます。このメソッドに'async'修飾子を指定し、戻り値の方を'Task'に変更することを検討してください。
と書かれています。
何が書かれているか、全くちんぷんかんぷんですよね。
ここでは、詳細の説明は省きます。
エラーの説明の行を観ると、132になっています。そこで、132行目をマウスでクリックします。すると、下の様に、132行目の左側に何やら表示されています。
その左側の図の上にマウスを移動してみると、少し図形が変化して、下向きの矢印のようなものが表示されるので、そこをクリックしてください。
すると、
メソッドを非同期にします
メソッドを非同期にする(voidを維持)
と表示されるので、下の「メソッドを非同期にする(voidを維持)」をマウスでクリックしてください。
すると左側の図形が消えて、エラーも0になります。
そこで、すべて保存して、開始を押してみましょう。
プログラムが起動したら、startボタンを押してみてください。画像が表示されたでしょうか。
また、stopを押したら、表示が止まりましたか?
あれ、画像が良く分からないですって?
PictureBox4の大きさが小さいので、大きな画像を表示すると一部分しか表示されません。
全部表示させたいですよね。
そこで、もう一つのおまじないとして、画像を表示する前に次の分を加えてみてください。
pictureBox4.SizeMode = PictureBoxSizeMode.StretchImage;
また、stopもstop下かどうかわかりにくいので、次の分を入れてみましょう。
pictureBox4.ImageLocation = "";
さあ、今度はどうですか?無事画像が表示されましたか?
また、この画像を表示している間に、button1や緑・黄・赤のラジオボタンをクリックしてみてください。
画像表示とは独立して、表示が変わりますでしょう。この画像表示の処理と他の処理が独立して動作する状態を非同期に動作していると表現します。
そうです、先ほど出ていたエラーに関連する話です。
awaitは非同期に動作しているメソッドでしか使用できないというエラーでした。
そして、startボタンを押したときの処理の部分をもう一度、見てみてください。
private async void button2_Click(object sender, EventArgs e)
asyncという言葉が書かれています。
これは、button2_Clickという処理(メソッド)が非同期で動作するメソッドであることを示す言葉です。先ほどのエラー訂正で、「メソッドを非同期にする(voidを維持)」を選択したことで、Visual Studio 2019が自動的に修正してくれたのです。
今のtころ理屈は分からなくて結構ですが、Windowsにはもともと複数のスレッドを同時に並行して動作させることが出来ます。
C#はプログラマーがあまり意識せずに、自動で並行して動作するスレッド処理を記述することが可能になっています。
今回はその機能を使用しました。
参考プログラム例
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace csharp
{
public partial class Form1 : Form
{
bool on_stop;
string Image_folder = @"c:\work\images\";
string[] ImageLoc = new string[] { "a1.jpg", "a2.jpg", "a3.jpg", "a4.jpg", "a5.jpg", "a6.jpg", "a7.jpg", "a8.jpg", "a9.jpg", "a10.jpg" };
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{
textBox1.Text = "赤";
textBox1.BackColor = Color.Red;
textBox2.Text = "橙";
textBox2.BackColor = Color.Orange;
textBox3.Text = "黄";
textBox3.BackColor = Color.Yellow;
textBox4.Text = "緑";
textBox4.BackColor = Color.Green;
textBox5.Text = "青";
textBox5.BackColor = Color.Blue;
textBox6.Text = "藍";
textBox6.BackColor = Color.Indigo;
textBox7.Text = "紫";
textBox7.BackColor = Color.Purple;
}
else
{
textBox1.Text = "";
textBox1.BackColor = Color.White;
textBox2.Text = "";
textBox2.BackColor = Color.White;
textBox3.Text = "";
textBox3.BackColor = Color.White;
textBox4.Text = "";
textBox4.BackColor = Color.White;
textBox5.Text = "";
textBox5.BackColor = Color.White;
textBox6.Text = "";
textBox6.BackColor = Color.White;
textBox7.Text = "";
textBox7.BackColor = Color.White;
}
}
private void radioButton3_CheckedChanged(object sender, EventArgs e)
{
if (radioButton3.Checked)
{
pictureBox1.Image = Properties.Resources.green1;
textBox8.Text = "緑";
textBox8.BackColor = Color.Green;
}
else
pictureBox1.Image = Properties.Resources.green0;
}
private void radioButton4_CheckedChanged(object sender, EventArgs e)
{
if (radioButton4.Checked)
{
pictureBox2.Image = Properties.Resources.yellow1;
textBox8.Text = "黄";
textBox8.BackColor = Color.Yellow;
}
else
pictureBox2.Image = Properties.Resources.yellow0;
}
private void radioButton5_CheckedChanged(object sender, EventArgs e)
{
if (radioButton5.Checked)
{
pictureBox3.Image = Properties.Resources.red1;
textBox8.Text = "赤";
textBox8.BackColor = Color.Red;
}
else
pictureBox3.Image = Properties.Resources.red0;
}
private void textBox8_TextChanged(object sender, EventArgs e)
{
switch (textBox8.Text)
{
case "緑":
{
radioButton3.Checked = true;
break;
}
case "黄":
{
radioButton4.Checked = true;
break;
}
case "赤":
{
radioButton5.Checked = true;
break;
}
default:
break;
}
}
private async void button2_Click(object sender, EventArgs e)
{
on_stop = false;
int n;
n = 0;
while(!on_stop)
{
pictureBox4.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox4.ImageLocation = Image_folder + ImageLoc[n];
await Task.Delay(5000);
n = (n + 1) % 10;
}
}
private void button3_Click(object sender, EventArgs e)
{
on_stop = true;
pictureBox4.ImageLocation = "";
}
}
}
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace csharp
{
public partial class Form1 : Form
{
bool on_stop;
string Image_folder = @"c:\work\images\";
string[] ImageLoc = new string[] { "a1.jpg", "a2.jpg", "a3.jpg", "a4.jpg", "a5.jpg", "a6.jpg", "a7.jpg", "a8.jpg", "a9.jpg", "a10.jpg" };
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{
textBox1.Text = "赤";
textBox1.BackColor = Color.Red;
textBox2.Text = "橙";
textBox2.BackColor = Color.Orange;
textBox3.Text = "黄";
textBox3.BackColor = Color.Yellow;
textBox4.Text = "緑";
textBox4.BackColor = Color.Green;
textBox5.Text = "青";
textBox5.BackColor = Color.Blue;
textBox6.Text = "藍";
textBox6.BackColor = Color.Indigo;
textBox7.Text = "紫";
textBox7.BackColor = Color.Purple;
}
else
{
textBox1.Text = "";
textBox1.BackColor = Color.White;
textBox2.Text = "";
textBox2.BackColor = Color.White;
textBox3.Text = "";
textBox3.BackColor = Color.White;
textBox4.Text = "";
textBox4.BackColor = Color.White;
textBox5.Text = "";
textBox5.BackColor = Color.White;
textBox6.Text = "";
textBox6.BackColor = Color.White;
textBox7.Text = "";
textBox7.BackColor = Color.White;
}
}
private void radioButton3_CheckedChanged(object sender, EventArgs e)
{
if (radioButton3.Checked)
{
pictureBox1.Image = Properties.Resources.green1;
textBox8.Text = "緑";
textBox8.BackColor = Color.Green;
}
else
pictureBox1.Image = Properties.Resources.green0;
}
private void radioButton4_CheckedChanged(object sender, EventArgs e)
{
if (radioButton4.Checked)
{
pictureBox2.Image = Properties.Resources.yellow1;
textBox8.Text = "黄";
textBox8.BackColor = Color.Yellow;
}
else
pictureBox2.Image = Properties.Resources.yellow0;
}
private void radioButton5_CheckedChanged(object sender, EventArgs e)
{
if (radioButton5.Checked)
{
pictureBox3.Image = Properties.Resources.red1;
textBox8.Text = "赤";
textBox8.BackColor = Color.Red;
}
else
pictureBox3.Image = Properties.Resources.red0;
}
private void textBox8_TextChanged(object sender, EventArgs e)
{
switch (textBox8.Text)
{
case "緑":
{
radioButton3.Checked = true;
break;
}
case "黄":
{
radioButton4.Checked = true;
break;
}
case "赤":
{
radioButton5.Checked = true;
break;
}
default:
break;
}
}
private async void button2_Click(object sender, EventArgs e)
{
on_stop = false;
int n;
n = 0;
while(!on_stop)
{
pictureBox4.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox4.ImageLocation = Image_folder + ImageLoc[n];
await Task.Delay(5000);
n = (n + 1) % 10;
}
}
private void button3_Click(object sender, EventArgs e)
{
on_stop = true;
pictureBox4.ImageLocation = "";
}
}
}