目次(ここを押すと開閉します)
- 講座全体の概要
- アプリの仕様をどうするか
- C#のWebアプリケーションで変数のデータを保持する方法 TempData (Cookie・WebStorage・セッション変数)
- Listの先頭または最後の要素を取得する方法
- Listの要素を削除する方法
- 同じ要素を異なる複数のListに入れることも可能
- Index.cshtmlの処理を途中で抜ける(終了させる)には return を使う
- ラジオボタン等の入力項目を入力不可(disabled)にするとデータがサーバーに送信されない 解決策はpointer-events: none
- ラジオボタンを未選択の状態に初期化したい場合 JavaScriptを使用すれば可能
- 次回は回答例を掲載します
講座全体の概要
プログラミング基礎講座の総仕上げ、「ハイ&ロー」というトランプゲームを作成する実践演習です。数回に分けてヒントや実際のプログラム例の提示、解説等を行っていきます。今回は演習で少し難しいポイントについて解説します。講座全体の概要は以下の記事ゼロから始めるプログラミング基礎講座 第1回【講座の概要・目的】をご覧ください。
演習の内容等は前回の記事ゼロから始めるプログラミング基礎講座 第9回【実践演習】PART1にありますので、そちらを先にご覧ください。
アプリの仕様をどうするか
このハイ&ローは2人で対戦するゲームですが、アプリではプレイヤー(自分)とCPU(コンピューター)で対戦する形式にすると作りやすいです。プレイヤーは画面で「ハイ」か「ロー」を選択してコールするようにし、CPUは自動で選択させます。
CPUの選択方法はプログラムで決める必要がありますが、一番単純な選択方法としては「プレイヤーのカードが7以上ならローを選択、6以下ならハイを選択」とすれば良いかと思います。もっと高度に確率を計算することもできますし、ランダムにすることもできますが、アプリを一旦完成させることを優先させましょう。
また、画面のデザイン等はあまり気にせずに、最低限必要な情報だけ表示して進めましょう。見映えは最後に調整すれば十分です。
C#のWebアプリケーションで変数のデータを保持する方法 TempData (Cookie・WebStorage・セッション変数)
Webアプリケーションでは、基本的に変数のデータを保持し続けることはできません。しかし、全くデータを保持できないとなるとアプリケーションは成り立ちません。そこで、一般的にはクライアント側(ブラウザーが動いているPC)にデータを保存できるCookieやWebStorageと、サーバー側に保存できるセッション変数というものを利用して一時的にデータを保存します。
当講座で扱ってきたC#のWebアプリケーションでは、TempDataという仕組みが用意されています。これは内部的にCookieを使用してデータをクライアント側に保存しています。(設定を行えばセッション変数を使用するように変更できます。)
TempDataは一時的なデータの保存のために用いられますので、保存した変数を1回参照すると、その変数のデータは削除されます。しかし、参照しても削除したくない場合もあります。この様な場合は下記のコードを実行すれば可能となります。
TempData.Keep()
なお、Cookieは使用できるデータ容量に限りがありますので、大量データを保存することはできません。本格的なアプリケーションを作成する場合は、データベースサーバーを使用する等、別の手段を用意する必要があります。しかし、今回の演習で作成するアプリケーションではそこまで気にする必要はなく、Cookieで十分動作します。
Listの先頭または最後の要素を取得する方法
カードの束はList<Card>型の変数で表せます。ここから先頭もしくは最後のカードを取得するには以下の方法があります。
private void Sample(List<Card> cards)
{
Card card;
int count = cards.Count; // リストの要素数を取得
card = cards[0]; // リストの先頭を取得
card = cards.First(); // リストの先頭を取得する別の方法
card = cards[count -1]; // リストの最後を取得
card = cards.Last(); // リストの最後を取得する別の方法
}
今回のアプリでは先頭、最後のどちらから取得しても仕様上問題ありません。ただし、インデックス(添字)は先頭が0から始まるので、Listの要素数(Count)と最後のインデックスが一致しないことには注意してください。
また、取得しようとしたインデックスの要素が存在しない場合はエラーになりますので、取得前に必ず要素数を確認する等の対策をしましょう。
Listの要素を削除する方法
実装の方法にもよりますが、Listの要素を削除したい場合もあるかと思います。その場合は以下の様にします。
private void Sample(List<Card> cards)
{
cards.RemoveAt(0); // リストの先頭を削除
int count = cards.Count; // リストの要素数を取得
cards.RemoveAt(count -1); // リストの最後を削除
Card card = cards[0]; // リストから削除する前に取り出しておくと、
cards.RemoveAt(0); // ここでリストからは削除されるが、card変数には先に取り出した要素が残っている。
}
先頭または中間の要素を削除した場合は、それ以降の要素のインデックスがスライドしますので注意してください。要素数(Count)も当然変わります。
また、Listから削除しても、事前に別の変数やListに格納してあれば、そちらは残ります。
同じ要素を異なる複数のListに入れることも可能
下のコードでは「PlayerCards」と「CpuCards」という2つのListを用意して、それぞれに全く同じ要素を追加しています。このコードだけでは意味がありませんが、Listからの削除等はそのListにだけ作用しますので、用途によっては意味があります。
private void DealCards()
{
PlayerCards = new List<Card>();
CpuCards = new List<Card>();
Cards cards = new Cards();
foreach (Card card in cards)
{
PlayerCards.Add(card);
CpuCards.Add(card);
}
}
Index.cshtmlの処理を途中で抜ける(終了させる)には return を使う
条件によって画面の表示処理を途中で終了させたいことがあるかもしれません。このような場合はreturn文を記述すれば可能です。下記のコードを参照してください。
@page "{handler?}"
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1>ハイ&ロー</h1>
@{
if (Model.HasNotStarted)
{
@*ゲーム開始前の表示*@
<form method="post">
<div class="row w-100 mt-3 mb-5 justify-content-center">
<input asp-page-handler="GameStart" class="btn btn-primary col-sm-2" type="submit" value="ゲーム開始" />
</div>
</form>
return;
}
else if (Model.HasEnded)
{
@*ゲーム終了時の表示*@
<div class="container-fluid mb-4">
<span class="col">@Model.ResultMessage</span>
</div>
return;
}
}
<div class="container-fluid mb-4">
@*ゲーム中の表示*@
</div>
<form method="post">
@*ゲーム中の入力項目表示*@
</form>
</div>
これは、ゲーム開始前、ゲーム中、ゲーム終了といった3つの状態によって、画面に表示する内容を変えている例です。
主な状態は「ゲーム中」なので、この状態では記述内容も多くなりますが、それ以外の状態は例外的な処理であり、簡易な表示で済みます。この様な場合、先に例外的な処理を記述しておいて、returnでそれ以降の処理を行わないようにするとすっきりします。
上のコードをよく見ると、最後の行にある閉じタグ
</div>
より前にreturn文が記述されているため、HTMLとして不正になるように思えます。
しかし、ここの部分はC#がうまく解釈して、HTMLには閉じタグが正しく出力されるので問題ありません。
ラジオボタン等の入力項目を入力不可(disabled)にするとデータがサーバーに送信されない 解決策はpointer-events: none
実装の方法にもよりますが、入力項目を条件によって入力不可にしたいことがあると思います。HTMLの項目にdisabled属性を付与すれば入力不可になりますが、実はこれだと、ボタンを押してサーバーに処理要求をした時に、この項目のデータが送られません。
入力は不可にしたいけれど、表示されているデータは送りたいという場合にはdisabledは使えません。代わりに、style属性にpointer-events: noneを指定します。
<div class="form-check form-check-inline justify-content-center mb-2" style="pointer-events: none">
<div class="mr-3">
<input class="form-check-input" asp-for="HighLowChoice" type="radio" value="ハイ" id="radioHigh" required />
<label class="form-check-label" for="radioHigh">ハイ</label>
</div>
<div class="mr-3">
<input class="form-check-input" asp-for="HighLowChoice" type="radio" value="ロー" id="radioLow" />
<label class="form-check-label" for="radioLow">ロー</label>
</div>
</div>
この例ではラジオボタンの親要素のdivにstyleを指定しています。これで、このdivに対するクリック等の入力イベントが発生しないようになります。ここでは無条件に設定していますが、条件分岐と組み合わせれば状況に応じて入力可否を決定することができます。
ラジオボタンを未選択の状態に初期化したい場合 JavaScriptを使用すれば可能
ラジオボタンは通常、選択肢から必ず1つ選択を行うためのコントロールです。その性質から、1度何らかの選択をすると、何も選択していない状態には戻せません。
これをどうしても元に戻したい場合はJavaScriptを使用することになります。ここまではJavaScriptを極力使わないで済むようにしてきましたが、これに関しては他に良い方法がありません。
下の例は初期化した後、任意の選択肢を選ぶ例です。
@{
// ラジオボタンの初期選択を切り換え
<script>
document.getElementById('radioHigh').removeAttribute('checked');
document.getElementById('radioLow').removeAttribute('checked');
</script>
if (Model.HighLowChoice == "ハイ")
{
<script>
document.getElementById('radioHigh').setAttribute('checked', 'checked');
</script>
}
else if (Model.HighLowChoice == "ロー")
{
<script>
document.getElementById('radioLow').setAttribute('checked', 'checked');
</script>
}
}
JavaScriptはブラウザ側で処理されますが、操作する項目が確実に存在するタイミングで処理される必要があります。このため、Index.cshtmlのHTML記述部分の最後に記載するのが良いでしょう。
次回は回答例を掲載します
今回は以上となります。次回は回答例を掲載する予定です。
この記事へのコメントはありません。