マスタ系の4つの画面は、同系の構成と操作手順となっています。そこで、以下では大分類データを操作する画面を例題として説明します。
画面と操作方法の詳細な説明は こちら を参照してください。画面を以下に再掲します。(画像をクリックすると、拡大表示します)
大分類画面を構成するクラスを示します。必要とするクラスは少数です。ただし、 Web フォームクラスは、 TransactionPage → BasePage の
順にクラス階層を持ちます。その説明は、 こちら を参照してください。
Major クラスは同様に FieldworkModel を親クラスに持ちます。説明は、 こちら を参照してください。
データベースに関連した FieldworkDB と MajorTable のクラスも親クラスを持ちます。その説明は、 こちら を参照してください。
MajorPage が他のマスタ系の画面と異なるのは、下図のアイコンを表示・操作する部分でしょう。(画像をクリックすると、拡大表示します)
その部分の html を示します。
MajorPage.aspx の一部分
1 <div class="col-md-6">
2 <asp:GridView ID="IconList" runat="server" AutoGenerateColumns="False" ShowHeader="False"
3 <GridLines="None" ViewStateMode="Disabled" CssClass="table table-bordered p100">
4 <Columns>
5 <asp:ImageField DataImageUrlField="0" DataAlternateTextField="index0" ItemStyle-CssClass="IconCell text-center"></asp:ImageField>
6 <asp:ImageField DataImageUrlField="1" DataAlternateTextField="index1" ItemStyle-CssClass="IconCell text-center"></asp:ImageField>
7 <asp:ImageField DataImageUrlField="2" DataAlternateTextField="index2" ItemStyle-CssClass="IconCell text-center"></asp:ImageField>
8 <asp:ImageField DataImageUrlField="3" DataAlternateTextField="index3" ItemStyle-CssClass="IconCell text-center"></asp:ImageField>
9 <asp:ImageField DataImageUrlField="4" DataAlternateTextField="index4" ItemStyle-CssClass="IconCell text-center"></asp:ImageField>
10 <asp:ImageField DataImageUrlField="5" DataAlternateTextField="index5" ItemStyle-CssClass="IconCell text-center"></asp:ImageField>
11 <asp:ImageField DataImageUrlField="6" DataAlternateTextField="index6" ItemStyle-CssClass="IconCell text-center"></asp:ImageField>
12 <asp:ImageField DataImageUrlField="7" DataAlternateTextField="index7" ItemStyle-CssClass="IconCell text-center"></asp:ImageField>
13 <asp:ImageField DataImageUrlField="8" DataAlternateTextField="index8" ItemStyle-CssClass="IconCell text-center"></asp:ImageField>
14 <asp:ImageField DataImageUrlField="9" DataAlternateTextField="index9" ItemStyle-CssClass="IconCell text-center"></asp:ImageField>
15 </Columns>
16 </asp:GridView>
17 </div>
2行目:全体は asp:GridView を使用します
5~14行目:1行には10個のセルを表示します。それぞれのセルには asp:ImageField を使用してアイコンのイメージを表示します
アイコンの一覧を表示するためのデータは、MajorPage.aspx.cs で動的に作成しています。
MajorPage.aspx.cs の一部分
1 private bool DisplayIcon()
2 {
3 DataTable table = new DataTable();
4 table.Columns.Add("0", typeof(String));
5 table.Columns.Add("1", typeof(String));
6 table.Columns.Add("2", typeof(String));
7 table.Columns.Add("3", typeof(String));
8 table.Columns.Add("4", typeof(String));
9 table.Columns.Add("5", typeof(String));
10 table.Columns.Add("6", typeof(String));
11 table.Columns.Add("7", typeof(String));
12 table.Columns.Add("8", typeof(String));
13 table.Columns.Add("9", typeof(String));
14 table.Columns.Add("index0", typeof(String));
15 table.Columns.Add("index1", typeof(String));
15 table.Columns.Add("index2", typeof(String));
17 table.Columns.Add("index3", typeof(String));
18 table.Columns.Add("index4", typeof(String));
19 table.Columns.Add("index5", typeof(String));
20 table.Columns.Add("index6", typeof(String));
21 table.Columns.Add("index7", typeof(String));
22 table.Columns.Add("index8", typeof(String));
23 table.Columns.Add("index9", typeof(String));
24 DataRow row;
25 String url;
26 int index = 0, cnt = Major.Markers.Length;
27 for (int i = 0, times = Major.Markers.Length / 10; i < times; ++i)
28 {
29 row = table.NewRow();
30 for (int j = 0; j < 10; ++j)
31 {
32 url = ResolveUrl("~/resource/img/" + Major.ToMarkerName("" + index));
33 row["" + j] = url;
34 row["index" + j] = index;
35 ++index;
36 }
37 table.Rows.Add(row);
38 }
39 IconList.DataSource = table;
40 IconList.DataBind();
41 return true;
42 }
4~13行目:イメージ用 URL を格納するフィールド名を設定する
14~23行目:添え字の番号を格納するフィールド名を設定する
33行目: URL の値を設定する
34行目:添え字の値を設定する
39~40行目:テーブルにデータをバインドする
アイコンが表示されたのセルをクリックしたときに、選択色に変更する処理は jQuery で行っています。
MajorPage.aspx の一部分
1 <script>
2 $(function () {
3 $(".IconCell").click(function () {
4 $(".IconCell").removeClass("success");
5 $(this).addClass("success");
6 $("#HiddenIcon").val($(this).children("img")[0].getAttribute("alt"));
7 });
8 });
9 </script>
略
10 <asp:TextBox ID="HiddenIcon" runat="server" value="0" type="hidden" />
3行目: class 属性に IconCell を持つセルがクリックされたときに呼び出される
4行目: bootstrap の定義色 success をすべてのセルから外す
5行目: bootstrap の定義色 success をクリックされたセルにのみ設定する
6行目:クリックされたアイコンの添え字番号を hidden 項目に記録する
10行目:クリックされたアイコンの添え字番号を記録するための項目
大分類の一覧表示の行をクリックすると、表示が選択色に変わります。これは、標準の方法で実現しています。 jQuery では行っていません。
MajorPage.aspx の一部分
1 <asp:GridView ID="MajorView" runat="server" AutoGenerateColumns="False" CssClass="table table-condensed table-bordered p100"
2 GridLines="None" ViewStateMode="Disabled" OnRowDataBound="MajorView_RowDataBound">
3 <SelectedRowStyle CssClass="success" />
4 <Columns>
5 <asp:BoundField DataField="ID" HeaderText="ID">
6 <HeaderStyle CssClass="p5 text-center" />
7 </asp:BoundField>
8 <asp:BoundField DataField="Name" HeaderText="名称">
9 <HeaderStyle CssClass="p20 text-center" />
10 </asp:BoundField>
11 <asp:ImageField DataImageUrlField="IconUrl" HeaderText="アイコン" ItemStyle-CssClass="text-center">
12 <HeaderStyle CssClass="p5 text-center" />
13 </asp:ImageField>
14 <asp:BoundField DataField="Comment" HeaderText="コメント">
15 <HeaderStyle CssClass="p20 text-center" />
16 </asp:BoundField>
17 <asp:BoundField DataField="Cdate" HeaderText="登録日" DataFormatString="{0:d}">
18 <HeaderStyle CssClass="p10 text-center" />
19 </asp:BoundField>
20 <asp:BoundField DataField="Mdate" HeaderText="変更日" DataFormatString="{0:d}">
21 <HeaderStyle CssClass="p10 text-center" />
22 </asp:BoundField>
23 <asp:TemplateField HeaderStyle-CssClass="hidden" ItemStyle-CssClass="hidden">
24 <ItemTemplate>
25 <asp:LinkButton ID="SelectButton" runat="server" CommandName="Select" Text="" Width="0%" Visible="True" />
26 </ItemTemplate>
27 </asp:TemplateField>
28 </Columns>
29 </asp:GridView>
MajorPage.aspx.cs の一部分
30 protected void MajorView_RowDataBound(object sender, GridViewRowEventArgs e)
31 {
32 if (e.Row.RowType == DataControlRowType.DataRow)
33 {
34 LinkButton btn = (LinkButton)(e.Row.FindControl("SelectButton"));
35 e.Row.Attributes["onClick"] = ClientScript.GetPostBackClientHyperlink(btn, "");
36 }
37 }
2行目:レコードがバインドされたときのハンドラ OnRowDataBound を設定しています
3行目:レコードが選択されたときの選択色(bootstrap の定義値 success)を指定しています
24~26行目:幅ゼロの隠し項目(ID="SelectButton の部分)を行末に追加しています
30~37行目:レコードがクリックされたときに、サーバにポストバックさせています
Web Form におけるトランザクションに関しては、 こちら を参照してください。
ここでは、大分類画面における新規の追加の例を示します。実は、トランザクションを使用していません。
MajorPage.aspx.cs の一部分
1 protected void AddButton_Click(object sender, EventArgs e)
2 {
3 FieldworkDB database = new FieldworkDB();
4 MajorTable majorTable = new MajorTable(database);
5 Major one = new Major();
6 one.Id = InputId.Text;
7 one.Name = InputName.Text;
8 one.Comment = InputComment.Text;
9 one.MarkerId = HiddenIcon.Text;
10 if (majorTable.Append(one))
11 {
12 Response.BufferOutput = true;
13 Response.Redirect("MajorPage.aspx");
14 }
15 else
16 {
17 DisplayErrors(majorTable.Result);
18 }
19 }
1行目:「追加」ボタンがクリックされたときのハンドラの定義
3~4行目:データベース関連の準備
5~9行目:追加するモデルクラスの値を画面コントロールから入手する
10行目:1件のデータを追加する
12~13行目:追加成功後に、初期状態に遷移する
17行目:追加失敗時に、エラーを表示する
大分類の追加は、必ず1件1 SQL 文となります。そのためトランザクションの必要がないため、このような実装になっています。
これを、実際のプログラムとは異なりますが、トランザクションを使用した実装を以下に示します。
略
1 private ActionMethod realAction;
略
2 protected void AddButton_Click(object sender, EventArgs e)
3 {
4 FieldworkDB database = new FieldworkDB();
5 MajorTable majorTable = new MajorTable(database);
6 Major one = new Major();
7 one.Id = InputId.Text;
8 one.Name = InputName.Text;
9 one.Comment = InputComment.Text;
10 one.MarkerId = HiddenIcon.Text;
11 bool flag = true;
12 realAction = (() =>
13 {
14 if (! majorTable.Append(one))
15 {
16 flag = false;
17 throw new Exception(majorTable.Result);
18 }
19 });
20 Execute();
21 if (flag)
22 {
23 Response.BufferOutput = true;
24 Response.Redirect("MajorPage.aspx");
25 }
26 else
27 {
28 DisplayErrors(majorTable.Result);
29 }
30 }
31 public override void DoAction()
32 {
33 realAction();
34 }
1行目:実際の処理を格納するための変数
4~5行目:データベース関連の準備
6~10行目:追加するモデルクラスの値を画面コントロールから入手する
11行目:トランザクションの結果を格納するための変数
12~19行目:トランザクション本体の準備
20行目:トランザクションの開始
23~24行目:トランザクション成功時に、初期画面に遷移する
28行目:トランザクション失敗時に、エラーを表示
31~34行目:トランザクション本体の呼び出し
状態遷移図を示します。詳細は、 こちら を参照してください。
コントローラからビューに値を渡すには、コントローラがモデルを生成するときに必要な値をモデルに設定し、そのモデルをビューに渡すのが一般的です。
しかし、場合によってはビューに直接値を受け渡したい場合があります。そのような用途に使用できる次のようなオブジェクトがあります。
それぞれの特徴を考慮して使い分けてください。多くの場合は、一番寿命が短い ViewData で十分なことが多いと思います。大分類操作の画面でも、 ViewData を使用しています。
大分類の初期画面と読出後の画面には、まったく同じ内容の大分類一覧リストが必要になります。そこで、部分ビューとして、 _MajorView を 利用することにしました。部分ビューの名前は「_」で始まるのが慣習のようです。
このビューのモデルは、 MajorAndMajors です。初期画面や読出後の画面と同じモデルを使用します。初期画面や読出後の画面の一部として 組み込んで使用しますので、同じモデル(もしくは、派生したモデル)である必要があります。
1 @model FieldworkForMVC.Models.MajorAndMajors
2 <table class="table table-condensed table-bordered p100">
3 <tr>
4 <th class="p5 text-center">
5 @Html.DisplayNameFor(model => model.Major.Id)
6 </th>
7 <th class="p25 text-center">
8 @Html.DisplayNameFor(model => model.Major.Name)
9 </th>
10 <th class="p10 text-center">
11 アイコン
12 </th>
13 <th class="p30 text-center">
14 @Html.DisplayNameFor(model => model.Major.Comment)
15 </th>
16 <th class="p15 text-center">
17 @Html.DisplayNameFor(model => model.Major.CreateDate)
18 </th>
19 <th class="p15 text-center">
20 @Html.DisplayNameFor(model => model.Major.ModifyDate)
21 </th>
22 </tr>
23 @foreach (var item in Model.Majors)
24 {
25 <tr class="mainRow">
26 <td class="majorId">
27 @Html.DisplayFor(modelItem => item.Id)
28 </td>
29 <td>
30 @Html.DisplayFor(modelItem => item.Name)
31 </td>
32 <td class="text-center">
33 <img src="@FieldworkForMVC.Controllers.MajorsController.IconFile(item.MarkerId)" alt="" />
34 </td>
35 <td>
36 @Html.DisplayFor(modelItem => item.Comment)
37 </td>
38 <td>
39 @Html.DisplayFor(modelItem => item.CreateDate)
40 </td>
41 <td>
42 @Html.DisplayFor(modelItem => item.ModifyDate)
43 </td>
44 </tr>
45 }
46 </table>
1行目:モデルクラスは MajorAndMajors です
2行目:大分類の一覧を表示する table タグです
3~22行目: table のヘッダー部です
23~45行目: table のボディー部です
33行目:アイコン番号からアイコンのイメージファイル名に変換するために、 static メソッドを呼び出しています。このビューのモデルからはたどり着けない情報であるため static メソッドで実装してあります。やや苦しい実装です
この部分ビューを利用する初期化画面を示します。
Index.cshtml の一部分
1 @Html.Partial("_MajorView")
部分ビューが必要なところに、 @Html.Partial() 記述をするだけです。初期状態の画面(Index.cshtml)と読出後の画面(Selected.cshtml)に、この部分ビューを組み込んでいます。
Web MVC の画面で、アイコンの一覧を表示する部分を示します。(画像をクリックすると、拡大表示します)
一覧を表示する部分は、 cshtml ファイルに直接プログラムコード片を記述することで実現しています。
Index.cshtmp の一部分
1 <table class="table table-condensed table-bordered">
2 @for (int i = 0, cnt = FieldworkForMVC.Models.Major.Markers.Length / 10; i < cnt; ++i)
3 {
4 <tr>
5 @for (int j = 0; j > 10; ++j)
6 {
7 <td class="iconCell text-center">
8 <img src="@FieldworkForMVC.Controllers.MajorsController.IconFile(i * 10 + j)" alt="@((i * 10) + j)" />
9 </td>
10 }
11 </tr>
12 }
13 </table>
14 @Html.Hidden("hiddenIcon", ViewData["hiddenIcon"])
15 <script>
16 $(function () {
17 $(".iconCell").click(function () {
18 $(".iconCell").removeClass("success");
19 $(this).addClass("success");
20 $("#hiddenIcon").val($(this).children("img")[0].getAttribute("alt"));
21 });
22 });
23 </script>
1行目:全体を table タグで実現します
2行目:必要な行数分繰り返します
5行目:1行にセルは10個存在するため、その数だけ繰り返します
14行目:選択されたアイコンの添え字番号を格納する hidden 項目です。 ViewData を使用して初期値を渡しています
17~21行目:セルがクリックされたときに選択色(success)に変更し、選択されたアイコンの添え字番号を記録する JQuery プログラムです
MajorController の概略を示します。 FieldworkController クラスから派生しています。この親クラスについては こちら を参照してください。
1 public class MajorsController : FieldworkController
2 {
3 private static string Path = "";
4 private MajorTable majorTable;
5 private ActionMethod realAction;
6 public ActionResult Index() { 略 }
7 [HttpPost]
8 public ActionResult Index(string button, FormCollection collection) { 略 }
9 public ActionResult Selected() { 略 }
10 [HttpPost]
11 public ActionResult Selected(string button, FormCollection collection) { 略 }
12 public override void DoAction()
13 {
14 realAction();
15 }
16 public static string IconFile(int index) { 略 }
17 public static string IconFile(string id) { 略 }
18 }
4行目:大分類テーブルクラスです。データベースクラスの FieldworkDB は親クラス FieldworkController で定義しています
5行目:データベースの本体となるラムダ式を格納します。大分類の追加・更新・削除によって異なるラムダ式となります
6行目:「初期画面」に入ってくるときに呼び出されるメソッドです
7~8行目:「初期画面」から出ていくときに呼び出されるメソッドです
9行目:「読出後」に入ってくるときに呼び出されるメソッドです
10~11行目:「読出後」から出ていくときに呼び出されるメソッドです
12~15行目:トランザクション中で呼び出される処理の本体です
16行目:アイコンの添え字番号からアイコンイメージのファイル名を返すメソッドです
17行目:アイコンの添え字文字列からアイコンイメージのファイル名を返すメソッドです
中心となるメソッドは、上記の6行目・7~8行目;9行目;10~11行目の4メソッドです。それぞれ、状態遷移図に対応したメソッドになります。 状態遷移図については こちら を参照してください。
Web Form から MVC に移ってきて、最初に戸惑うのはモデルはどうやって構築するのか?だと思います。Web Form の場合、 ブラウザからの要求に含まれるビューステイトから、フレームワークによって自動的に画面要素は構築されます。アプリケーションに制御が 渡ってきたときには、ブラウザから渡された要素の値は、自由に参照できる状態になっているわけです。
ところが、 Web MVC の場合には自前でその変換処理を用意する必要があります。この作業は面倒に感じるかもしれません。 その例を以下に示します。ここで示すので、「初期画面」から出ていくときに呼び出されたメソッドの場合になります。
MajorController の一部分
1 [HttpPost]
2 public ActionResult Index(string button, FormCollection collection)
3 {
略
4 Major major = new Major();
5 major.Id = collection.GetValue("inputId").AttemptedValue;
6 major.Name = collection.GetValue("inputName").AttemptedValue;
7 major.Comment = collection.GetValue("inputComment").AttemptedValue;
8 major.MarkerId = collection.GetValue("hiddenIcon").AttemptedValue;
略
9 }
Index.cshtml の一部分
略
10 @Html.TextBox("inputId", Model.Major.Id, new { @class = "form-control", type = "text", maxlength = "2" })
11 @Html.TextBox("inputName", Model.Major.Name, new { @class = "form-control", type = "text", maxlength = "20" })
12 @Html.Hidden("hiddenIcon", ViewData["hiddenIcon"])
13 @Html.TextBox("inputComment", Model.Major.Comment, new { @class = "form-control", type = "text", maxlength = "128" })
略
1行目:POST で起動されるメソッドを指定しています。 [HttpPost] の指定は必須です
2行目: FormCollection 型の引数に画面要素の値が key-value の形式で格納されています
4~8行目:画面要素からモデル Major を自前で構築しています
10行目:大分類 ID 用の項目です
11行目:大分類名用の項目です
12行目:選択されたアイコンの番号を格納する hidden 項目です。格納は jQuery で行います。初期値の受け渡しは、 ViewData を介して行います
13行目:大分類のコメント用項目です
Index() メソッドの引数は2行目の形式で固定ではありません。フレームワークが適宜正しい引数付きで呼び出してくれます。
FormCollection のキーとなるのは、html の id 属性値です。また、この集合に格納される項目は Razor 表記のもの(@Html のもの)だけです。標準の html のタグは格納されません。
ブラウザのどの項目が原因となってポストバックが起こったかを知る必要があります。いろいろの方法がありますが、このプログラムでは以下の方法に統一しています。 さきほどと同じく、「初期画面」から出ていくときに呼び出されたメソッドを例にとります。
MajorController の一部分
1 [HttpPost]
2 public ActionResult Index(string button, FormCollection collection)
3 {
略
4 if (button == null)
5 return RedirectToAction("Index");
略
6 if (button.Equals("読 出"))
7 {
8 }
9 else if (button.Equals("追 加"))
10 {
11 }
12 }
Index.cshtml の一部分
略
13 <input id="createButton" type="submit" name="button" value="追 加" />
14 <input id="selectedButton" type="submit" name="button" value="読 出" />
4行目:引数の button が null の検査。実際にはこの場合は存在せず、予防的プログラミングとなっています
6~8行目:「読出」ボタンがクリックされてポストバックが発生した場合です。14行目と合わせて確認してください
9~11行目:「追加」ボタンがクリックされてポストバックが発生した場合です。13行目と合わせて確認してください
13行目:html の追加ボタンのタグです。 name 属性に、 Index メソッドの引数名と同じ値(button)を指定します
14行目:html の読出ボタンのタグです。 name 属性に、 Index メソッドの引数名と同じ値(button)を指定します
重要なのは、 html のタグの name 属性の値です。 その 値とメソッドの引数の名前を合わせる (この場合は、2行目の string button の部分と name="button" の部分)必要があることです。 比較には、value 属性の値を使用します。
Web MVC におけるトランザクションに関しては、 こちら を参照してください。
ここでは、「初期状態」から追加した場合の処理を示します。
MajorController の一部分
1 [HttpPost]
2 public ActionResult Index(string button, FormCollection collection)
3 {
4 if (button.Equals("追 加"))
5 {
6 Major major = new Major();
7 major.Id = collection.GetValue("inputId").AttemptedValue;
8 major.Name = collection.GetValue("inputName").AttemptedValue;
9 major.Comment = collection.GetValue("inputComment").AttemptedValue;
10 major.MarkerId = collection.GetValue("hiddenIcon").AttemptedValue;
11 bool flag = true;
12 realAction = (() =>
13 {
14 if (database.Execute(majorTable.AppendSql(major)) < 0)
15 {
16 flag = false;
17 throw new Exception(majorTable.Result);
18 }
19 });
20 Execute();
21 if (!flag)
22 {
23 return Index();
24 }
25 }
26 return RedirectToAction("Index");
27 }
28 public override void DoAction()
29 {
30 realAction();
31 }
6~10行目:引数の画面要素から Major モデルを構築します
11行目:トランザクションの結果用フラグ
12~19行:トランザクション本体となるラムダ式を設定します
14行目:新規追加が失敗した場合の検査です
16行目:結果フラグを false に設定します
17行目:トランザクションをロールバックするために例外を投げます
20行目:トランザクションを開始します
21行目:トランザクションが失敗したかの検査です
23行目:初期状態の画面を返します
26行目:処理が成功した場合、POST-REDIRECT-GET により初期状態に遷移します
28~31行目:トランザクションの本体用メソッド定義です。 realAction に定義されているラムダ式を呼び出しています