【ASP.NET Core MVC】ASP.NET Core MVCであみだくじを作ってみる 4 (cssの作成)

# 概要
前回：[【ASP.NET Core MVC】ASP.NET Core MVCであみだくじを作ってみる 3 (モデルの追加)](https://qiita.com/plusM/items/6a1aac0c1b39533bca95)
今回は、cssを使ってビューのレイアウトを作成していきます。
コントローラー間でのモデルデータの受け渡しも考えてみます。

# コントローラー間でモデルを受け渡す
[ASP.NET Core でのセッションとアプリの状態](https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1)をざっと読み、[TempData](https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1#tempdata)を使うことにしました。
HomeControllerのIndexアクションに、モデルデータをTempDataにセットする処理を追加します。

```cs:HomeController.cs
using Newtonsoft.Json;

        /// <summary>
        /// HomeビューでPOSTした時のアクション
        /// </summary>
        /// <param name="name"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        public IActionResult Index(string name, KujiModel model)
        {
            // 入力エラーがある時は何もしない
            if (!ModelState.IsValid)
                return View(model);

            // [結果を設定する]ボタンを押下した場合
            if (name == "set")
            {
                // くじの数が減った場合は、結果入力欄を減らす
                while (model.NumberOfKuji < model.Result.Count)
                {
                    model.Result.RemoveAt(model.Result.Count - 1);
                }

                // くじの数が増えた場合は、結果入力欄を増やす
                while (model.NumberOfKuji > model.Result.Count)
                {
                    model.Result.Add(new ResultModel());
                }

                return View(model);
            }

            TempData["kuji"] = JsonConvert.SerializeObject(model);

            // くじコントローラーを呼ぶ
            return RedirectToAction("Index", "Kuji");
        }
```
TempData["kuji"] = model としたくなるところですが、オブジェクトはセットできないようです。
このため、JsonConvert.SerializeObjectでjson文字列に変換してからセットしました。

KujiController側で、TempData["kuji"]を受け取ります。

```cs:KujiController.cs
using Amidakuji.Models;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace Amidakuji.Controllers
{
    public class KujiController : Controller
    {
        [HttpGet]
        public IActionResult Index()
        {
            ModelState.Clear();

            // 受け取ったTempDataからデータを取得する
            var json = TempData["kuji"]?.ToString();
            if (json != null)
            {
                var model = JsonConvert.DeserializeObject<KujiModel>(json);
                return View(model);
            }

            return View(new KujiModel());
        }
    }
}
```
JsonConvert.DeserializeObject<KujiModel>で、json文字列をKujiModel型にデシリアライズしています。
デシリアライズ後のモデルをビューに渡すことで、Homeビューで入力したデータをKujiビューでも使えるようになります。

# cssファイルを追加する
JavaScript、CSS、画像等の静的ファイルは、Webルート(既定ではwwwroot)ディレクトリに格納されています。
ASP.NET Core MVCでは、デフォルトのcssフレームワークとして[Bootstrap](https://getbootstrap.com/docs/4.3/getting-started/introduction/)が導入されていますが、
今回はオリジナルのcssを作ります。

ソリューションエクスプローラーの「wwwroot\css」を選択します。
右クリックでメニューを表示し、追加⇒新しい項目を選択します。
スタイルシートを選択します。ここでは、名前を「kuji.css」としました。
![css.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520699/8485db37-766e-b5c7-14a1-68e2facf635b.png)
Home\Index.cshtmlやKuji\Index.cshtmlの\<style>をcssに移動します。
各cshtmlの\<style>～\<\style>は削除しておきます。

```css:kuji.css
/*あみだくじデータの設定領域*/
.kuji-items {
    display: grid; /*グリッドレイアウトにする*/
    grid-auto-columns: 100px; /*1列あたりの幅 100px*/
    grid-auto-rows: 36px; /*1行あたりの高さ 30px*/
    background-color: #99CCCC; /*背景色*/
    margin: 0px auto; /*中央寄せ*/
    padding: 10px;
}

/*あみだくじデータの設定領域の入力項目*/
.kuji-items > input {
    height: 30px; /*高さ*/
}

/*くじを作るボタン*/
.create_btn {
    font-weight: bold;  /*太文字*/
    padding: 0.25em 0.5em;  /*文字余白*/
    text-decoration: none;  /*文字装飾なし*/
    color: #FFF;    /*文字色*/
    background: #008080; /*背景色*/
    margin-left: 10px; /*左側余白*/
    height: 30px; /*高さ*/
}

/*くじコンテナ*/
.kuji-container {
    height: 100px; /*高さ*/
    background-color: #CBFFD3; /*背景色*/
}
```
「Views\Shared\\_Layout.cshtml」の\<head>内にcssへのリンクを追加します。

```html:_Layout.cshtml
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Amidakuji</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
    <!-- kuji.cssを追加 -->
    <link rel="stylesheet" href="~/css/kuji.css" />
</head>
```

# cssにUIを定義する
試行錯誤の結果、あみだ部分は[グリッドレイアウト](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Grid_Layout)を利用することにしました。
kuji.cssに以下を追加します。内容はコードにコメントで書きました。

```css:kuji.css
・・・省略・・・

/*タイトル*/
.kuji-container > h1 {
    color: #000000; /*文字色*/
    background: #E6FFE9; /*背景色*/
    border-bottom: solid 3px #0000FF; /*下線*/
}

/*説明文*/
.kuji-container > p {
    color: #000000; /*文字色*/
    border-bottom: solid 3px #0000FF; /*下線*/
}

/*あみだくじの領域*/
.kuji-line {
    display: grid; /*グリッドレイアウトにする*/
    grid-auto-columns: minmax(10px, auto); /*1列あたりの幅 10px以上*/
    grid-auto-rows: minmax(25px, auto); /*1行あたりの高さ 25px以上*/
    background-color: #99CCCC; /*背景色*/
    position: relative; /*後述の「あみだを隠すためのマスク領域」の基準点とするためにrelativeを指定*/
}

/*あたり・はずれ等の結果*/
.kuji-line > div {
    margin: 0px auto; /*中央寄せ*/
}

/*縦線*/
.vertical-line {
    background-color: #000000; /*背景色*/
    width: 1px; /*幅*/
    height: auto; /*高さ*/
    margin: 0px auto; /*中央寄せ*/
}

/*横線*/
.horizontal-line {
    background-color: #000000; /*背景色*/
    width: 100%; /*幅*/
    height: 1px; /*高さ*/
    margin-left: 50%; /*左マージン 縦線を中央寄せしているため、幅50%分をずらして縦線の位置から始める*/
}

/*くじを辿る時の縦線*/
.vertical-line.follow {
    background-color: #FF0000; /*背景色*/
    width: 3px; /*幅*/
}

/*くじを辿る時の横線*/
.horizontal-line.follow {
    background-color: #FF0000; /*背景色*/
    height: 3px; /*幅*/
}

/*選択肢ボタン*/
.select-btn {
    color: #0000FF; /*文字色*/
    width: 25px; /*幅*/
    height: 25px; /*高さ*/
    border-radius: 50%; /*角丸 50%で円になる*/
    border: solid 2px #0000FF; /*枠線*/
    text-align: center; /*文字の中央寄せ*/
    margin: 0px auto; /*中央寄せ*/
}

/*選択肢ボタンのマウスオーバー時*/
.select-btn:hover {
    background-color: #6495ED; /*背景色*/
}

/*選択したボタン*/
.select-btn.selected {
    color: #FF0000; /*文字色*/
    border: solid 2px #FF0000; /*枠線*/
    background-color: #FFDDFF; /*背景色*/
}

/*あみだを隠すためのマスク領域*/
.mask {
    position: absolute; /*「あみだくじの領域」を基準点とするためにabsoluteを指定*/
    width: 90%; /*幅 「あみだくじの領域」の90%の幅*/
    height: 76%; /*高さ 「あみだくじの領域」の76%の高さ*/
    top: 12%; /*上位置 「あみだくじの領域」の上から12%の位置*/
    left: 5%; /*左位置 「あみだくじの領域」の左から5%の位置*/
    text-align: center; /*文字の中央寄せ*/
    border: solid 2px #808080; /*枠線*/
    background-color: #E6FFE9; /*背景色*/
    background-image: radial-gradient(#808080 10%, transparent 20%), radial-gradient(#808080 10%, transparent 20%); /*背景画像*/
    background-size: 10px 10px; /*背景画像のサイズ(幅 高さ)*/
}
```
「あみだを隠すためのマスク領域」についての説明を補足します。
背景画像に[円形グラデーション](https://developer.mozilla.org/ja/docs/Web/CSS/radial-gradient)を指定し、background-sizeで幅、高さを10pxに揃えることで円を描いています。
この円が、マスク領域のサイズ分繰り返されます。

# 定義したcssを使ってビューをあみだくじっぽくする
Kuji\Index.cshtmlを以下のように変更します。
くじの数は5本固定で、マスク領域を隠したり表示したりする機能はありません。
このあたりは、次回Razorの学習で改善していくつもりです。

```html:Kuji\Index.cshtml
@model Amidakuji.Models.KujiModel

@{
    ViewData["Title"] = "あみだくじ";
}

<div class="kuji-container">
    <h1>@Model.Title</h1>
    <p>くじの数は、@Model.NumberOfKuji 本です。</p>

    <!--あみだくじの領域(グリッドレイアウト)-->
    <div class="kuji-line">
        <!--選択肢ボタン-->
        <a href="#" class="select-btn" style="grid-row:2;grid-column:1">1</a>
        <a href="#" class="select-btn" style="grid-row:2;grid-column:2">2</a>
        <a href="#" class="select-btn" style="grid-row:2;grid-column:3">3</a>
        <a href="#" class="select-btn" style="grid-row:2;grid-column:4">4</a>
        <a href="#" class="select-btn" style="grid-row:2;grid-column:5">5</a>

        <!--縦線-->
        <div class="vertical-line" style="grid-row:3 / span 15;grid-column:1"></div>
        <div class="vertical-line" style="grid-row:3 / span 15;grid-column:2"></div>
        <div class="vertical-line" style="grid-row:3 / span 15;grid-column:3"></div>
        <div class="vertical-line" style="grid-row:3 / span 15;grid-column:4"></div>
        <div class="vertical-line" style="grid-row:3 / span 15;grid-column:5"></div>

        <!--横線-->
        <p class="horizontal-line" style="grid-row:4;grid-column:1"></p>
        <p class="horizontal-line" style="grid-row:10;grid-column:1"></p>
        <p class="horizontal-line" style="grid-row:13;grid-column:1"></p>
        <p class="horizontal-line" style="grid-row:8;grid-column:2"></p>
        <p class="horizontal-line" style="grid-row:12;grid-column:2"></p>
        <p class="horizontal-line" style="grid-row:14;grid-column:2"></p>
        <p class="horizontal-line" style="grid-row:6;grid-column:3"></p>
        <p class="horizontal-line" style="grid-row:13;grid-column:3"></p>
        <p class="horizontal-line" style="grid-row:5;grid-column:4"></p>
        <p class="horizontal-line" style="grid-row:9;grid-column:4"></p>
        <p class="horizontal-line" style="grid-row:15;grid-column:4"></p>

        <!--結果-->
        <!--横幅を揃えるため、PadRightで5文字分に揃える-->
        <div style="grid-row:18;grid-column:1">@Model.Result[0].Item.PadRight(5, '　')</div>
        <div style="grid-row:18;grid-column:2">@Model.Result[1].Item.PadRight(5, '　')</div>
        <div style="grid-row:18;grid-column:3">@Model.Result[2].Item.PadRight(5, '　')</div>
        <div style="grid-row:18;grid-column:4">@Model.Result[3].Item.PadRight(5, '　')</div>
        <div style="grid-row:18;grid-column:5">@Model.Result[4].Item.PadRight(5, '　')</div>

        <!--1行分を余白とする-->
        <div style="grid-row:19;grid-column:1 / span 5"></div>

        <!--あみだを隠すためのマスク領域-->
        <div class="mask"></div>
    </div>
</div>
```
「grid-row:2;grid-column:1」という書き方をしている箇所があります。
これは、グリッドレイアウトの中で、2行目の1列目、という意味です。
「grid-column:1 / span 5」等となっている箇所は、1列目から始めて5列分の領域という意味です。
Excelのセル結合を思い浮かべると分かりやすいかもしれません。

# 実行してみる
マスク領域あり
![デザイン①.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520699/f069ccee-0c40-1c47-ad16-01b886cc420c.png)
マスク領域なし
![デザイン②.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520699/ac696ca4-a380-1eb7-16b3-49775563db51.png)


# まとめ
今回は、cssを使ってビューのレイアウトを作成しました。
次回は、Razorを使ってマスク領域を隠したり表示したりする機能を作ります。
次回分はこちら⇒[ASP.NET Core MVCであみだくじを作ってみる 5 (Razorを使う)](https://qiita.com/plusM/items/add1ab06bfb5f9677fea)