まえがき
この記事では、独自のコントロールを作成し、それをASP.NET MVC 3.0の新しいプロジェクトの一部として使用する可能性について説明します。 以下に書かれているものはすべて作者の視点であり、広く普及している、または一般に受け入れられているコントロールの作成方法と一致しない場合があるため、批判やコメントを歓迎します。
はじめに
最も可能性が高いのは、以前にWinFormsまたはASP .NET WebFormsを使用してプロジェクトで作業していた多くのユーザーが、ASP .NET MVCプロジェクトのHTMLヘルパーがCheckBoxListなどのコントロールを作成できないことでした。ユーザープロファイル内またはHabrahabrに新しいトピックを追加する際に、データまたは非構造化データを複数選択します。 もちろん、単一のCheckBoxまたはCheckBoxForの使用を禁止する人はいませんが、このようなチェックボックスのグループで作業するのが便利で、メンテナーが簡単に拡張、理解でき、重複から保護されるコードは、将来のプロジェクトで成果を使用する予定のプログラマーにとって最後の質問ではありません。 そして、視覚表示のコントロールにいくつかの便利なオプションを追加できることを考慮すると、作成の必要性がますます明らかになります。
最後にサブジェクトエリアに飛び込む前に、問題のコントロールを作成するアイデアは、 http://mvc.devexpress.comのサイトを表示した後にインスピレーションを受けたことに注意したいと思います。すごい。 この記事が承認され、肯定的なフィードバックが寄せられた場合、コミュニティのハブに関する一連の記事の一部としてサイトで提示されたコントロールのさらなるレビューと実装が可能です。
分析
そもそも、何をしようとしているのかを理解する必要があります。 コントロールは、渡されたデータコレクションを表示できるCheckBoxesのグループによって表示され、フォームをサーバーに送信するときにモデルバインディングの概念を使用し、次の表示設定があります。
- Layoutt-ワイヤーフレーム(divまたはテーブル内);
- 方向-出力方向(垂直/水平);
- RepeatColumns-列の数。
- htmlAttributes-CheckBoxコンテナに適用される属性。
プロジェクト作成
空のASP .NET MVC 3プロジェクトを作成し、 IndexメソッドとビューでDemoControllerコントローラーを追加します。
最小限の準備が完了したら、コントロールに直接移動します。
CheckBoxListを追加する
Controls.csファイルとCheckBoxListの実装をホストする静的 Controlsクラスを使用して、 Coreフォルダーをプロジェクトルートに追加します。
現時点では、 静的な Controlsクラスで、Htmlヘルパーの独自の実装を既に作成できているため、これに必要なコードを追加します。
public static MvcHtmlString CheckBoxList(this HtmlHelper helper) { return new MvcHtmlString("Hello, i'm your CheckBoxList!"); }
そして、 Indexのビュー、 DemoControllerコントローラーで呼び出してみてください。
注:プラグインアセンブリおよびスコープ付きファイルに関する記事のテキストには意図的に記述していません。 私が言及せずにこれを行うことができることを願っています。 指摘する価値のある唯一のことは、行<add namespace = "MVC3Controls_H.Core" />をファイル/Views/web.configに追加することです。MVC3Controls_H.Coreの場合、これはCheckBoxListを使用した静的クラスの場所です。
要素を呼び出すには、 Index.cshtmlに次の行を追加します(新しいビューを追加するときにRazor Engineを指定した場合):
@Html.CheckBoxList()
プロジェクトのルートにあるGlobal.asaxファイルで、デフォルトでDemoコントローラーのIndexメソッドを呼び出す必要があることを示します。
routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Demo", action = "Index", id = UrlParameter.Optional } // Parameter defaults );
すべてのアクションが正しく実行された場合、次の内容のページが表示されます。
素晴らしい。 次に、CheckBoxListの構造の実装に戻ります。
CheckBoxList構造の実装
まず、 Controls.csファイルに表示の設定を追加します。 この場合、これらは列挙型の3つのオブジェクトになります。
public enum Layoutt { Table = 0, Flow = 1 } public enum Direction { Horizontal = 0, Vertical = 1 } public enum RepeatColumns { OneColumn = 1, TwoColumns = 2, ThreeColumns = 3, FourColumns = 4, FiveColumns = 5 }
また、 CheckBoxList_Settingsクラスでは、コントロールへの呼び出しでそれらを渡します。
public class CheckBoxList_Settings { public string cbl_Name = "SelectedCheckBoxListItems"; public Layoutt cbl_Layout = Layoutt.Table; public Direction cbl_Direction = Direction.Horizontal; public RepeatColumns cbl_RepeatColumns = RepeatColumns.FiveColumns; }
デフォルト設定として、ワイヤーフレームテーブル、出力方向、水平を選択し、5列の形式で表示します。 cbl_Nameプロパティについては後で説明します。
CheckBoxListのコンストラクターを変更して、タイプIDictionary <string、int>のコレクションと設定クラスのインスタンスを受け入れます。
public static MvcHtmlString CheckBoxList(this HtmlHelper helper, IDictionary<string, int> items, CheckBoxList_Settings settings) { return new MvcHtmlString("Hello, i'm your CheckBoxList!"); }
ビューでは、要素を呼び出すだけでなく、2つのパラメーターを渡す必要があるという結論に達しました。これには、コントローラーのIndexメソッドにデータコレクションを追加します。
Dictionary<string, int> languages = new Dictionary<string, int> { {"ActionScript", 0}, {"Delphi", 1}, {"GO", 2}, {"Lua", 3}, {"Prolog", 4}, {"Basic", 5}, {"Eiffel", 6}, {"Haskell", 7}, {"Objective-C", 8}, {"Python", 9}, {"C", 10}, {"Erlang", 11}, {"Java", 12}, {"Pascal", 13}, {"Ruby", 14}, {"C++", 15}, {"F#", 16}, {"JavaScript", 17}, {"Perl", 18}, {"Scala", 19}, {"C#", 20}, {"Fortran", 21}, {"Lisp", 22}, {"PHP", 23} };
そして、このデータをモデルとしてビューに提供します。
return View(languages);
ビューのコードでは、そのモデルがデータ型辞書<string、int>であることを示し、CheckBoxListを呼び出します。
@model Dictionary<string, int> @using MVC3Controls_H.Core @{ ViewBag.Title = "Index"; } <h2>Index</h2> @Html.CheckBoxList(Model, new CheckBoxList_Settings { cbl_Name = "SelectedCheckBoxListItems", cbl_Layout = Layoutt.Table, cbl_Direction = Direction.Horizontal, cbl_RepeatColumns = RepeatColumns.FiveColumns })
プロジェクトを開始すると、馴染みのある同じレコードが表示されます。
現在のアプリケーション構造の分析
先に進む前に、アプリケーションの現在の構造を理解し、場合によっては変更する必要があります。 ここで私が考えたいことは次のとおりです。
- 現時点では、Dictionary <string、int>型のモデルをビューに渡していますが、ほとんどの場合、アプリケーションはコントローラーから送信された他のデータを使用します。ページまたはエンティティ(登録、フォーム)のメタタグ(タイトル、説明)注文)-したがって、私たちはコントロールのためだけにモデルを独占するべきではありません。 さらに、ユーザーがどこかで選択した要素の値を保存および受信する必要があります。
- 時間が経つにつれて、プロジェクトでCheckBoxListがますます多くのビューで呼び出され、ある時点で新しいパラメーターを追加するか、既存のパラメーターを変更する必要があるとします。 この場合、コントロールへのすべての呼び出し(すべてのプロジェクトビューで)を変更する必要がありますが、これはあまり便利ではありません。
上記から、必要なものは次のとおりです。
- プレゼンテーション用のモデル(ViewModel)を追加し、他のデータとともに、このモデルの一部としてCheckBoxList'eに表示するコレクションを渡します。
- コントロールの呼び出しをビューから部分ビューに転送します。そのため、コンストラクターまたはパラメーターが変更された場合、プロジェクトで使用されるすべての要素の呼び出しを1か所で変更する必要があります。
- コントロールの別のデータモデルで送信された値をCheckBoxListに転送し、それを部分ビューに転送して、要素呼び出しの変更からビューをできる限り分離します。
注:ただし、アプリケーションコードの記述を開始する前に、これらの質問や他の多くの質問を考慮に入れて、自問する必要があります。 この記事は、初めて似たようなことをしようとする人を対象にしています。その後、一貫して試行錯誤を繰り返して解決策を導いていきます。
ビューモデルの追加
プレゼンテーションモデルは、その中のデータを送信するだけでなく、受信するのにも役立つため、送信されたコレクションに加えて、選択した要素の配列を追加する必要があります。 ViewModelsフォルダーをModelsフォルダーに追加し、次に示すようにViewModel_Index.csファイルをViewModelsに追加します。
ビューモデルのコード:
public class ViewModel_Index { public IDictionary<string, int> Languages { get; set; } public int[] SelectedCheckBoxListItems { get; set; } }
DemoController 'a のインデックスメソッドは次のように変更されます。
public ActionResult Index() { Dictionary<string, int> languages = new Dictionary<string, int> { {"ActionScript", 0}, {"Delphi", 1}, {"GO", 2}, {"Lua", 3}, {"Prolog", 4}, {"Basic", 5}, {"Eiffel", 6}, {"Haskell", 7}, {"Objective-C", 8}, {"Python", 9}, {"C", 10}, {"Erlang", 11}, {"Java", 12}, {"Pascal", 13}, {"Ruby", 14}, {"C++", 15}, {"F#", 16}, {"JavaScript", 17}, {"Perl", 18}, {"Scala", 19}, {"C#", 20}, {"Fortran", 21}, {"Lisp", 22}, {"PHP", 23} }; ViewModel_Index _ViewModel_Index = new ViewModel_Index { Languages = languages }; return View(_ViewModel_Index); }
コントローラーで送信されたモデルを変更したため、関連付けられたビューでもこれを行う必要があります。
@using MVC3Controls_H.Models.ViewModels @using MVC3Controls_H.Core @model ViewModel_Index @{ ViewBag.Title = "Index"; } <h2>Index</h2> @Html.CheckBoxList(Model.Languages, new CheckBoxList_Settings { cbl_Name = "SelectedCheckBoxListItems", cbl_Layout = Layoutt.Table, cbl_Direction = Direction.Horizontal, cbl_RepeatColumns = RepeatColumns.FiveColumns })
したがって、コントロールのデータコレクションに加えて他の何かをページに転送する必要がある場合は、ビューモデル/Models/ViewModels/ViewModels_Index.csを変更(新しいプロパティを追加)し、初期化を追加するだけでこれを実行できます。コントローラメソッドで。
CheckBoxListへの呼び出しをカプセル化する
最初の記事では、他のいくつかの目的のために、拡張メソッドを呼び出すためにすでに同様のスキームを適用しました。 現在のプロジェクトでは、CheckBoxListをPartial Viewに呼び出すために、Partialフォルダーを/ Viewsフォルダーに追加し、 ControlsフォルダーをControlsフォルダーに追加し、新しいPartial CheckBoxList.cshtmlビューをControlsフォルダーに追加します。
アプリケーションの構造は次のとおりです。
CheckBoxListの代わりにPartial Viewを呼び出して、 Indexビューを変更します。
@using MVC3Controls_H.Models.ViewModels @using MVC3Controls_H.Core @model ViewModel_Index @{ ViewBag.Title = "Index"; } <h2>Index</h2> @Html.Partial("~/Views/Partial/Controls/CheckBoxList.cshtml")
CheckBoxListのデータモデルの追加
コントロールの呼び出しをパーシャルビューに転送する予定なので、そこのデータもそこに転送する必要があります。したがって、データモデルを追加する必要があります。 Modelsフォルダーで、新しいCheckBoxListフォルダーを作成し、 CheckBoxList_Model.csクラスを追加します 。
public class CheckBoxList_Model { public IDictionary<string, int> items; public CheckBoxList_Settings settings; }
IndexビューでPartial View呼び出しを変更し、追加されたモデルをデータとして渡します。
@Html.Partial("~/Views/Partial/Controls/CheckBoxList.cshtml", new CheckBoxList_Model { items = Model.Languages, settings = new CheckBoxList_Settings { cbl_Name = "SelectedCheckBoxListItems", cbl_Layout = Layoutt.Table, cbl_Direction = Direction.Horizontal, cbl_RepeatColumns = RepeatColumns.FiveColumns } })
このモデルを/Views/Partial/Controls/CheckBoxList.cshtmlのパーシャルビューのモデルとして定義し、コントロールに値を呼び出して渡します。
@model CheckBoxList_Model @Html.CheckBoxList(Model.items, Model.settings)
これでアプリケーション構造の終わりです。 その主な点を考慮してください。
良いニュースは、ようやくアプリケーションの構造が完成したことです。悪いニュースは、CheckBoxListについてまだ何も書いていないことです。
CheckBoxListの実装
まず、要素を構築するための一般的なアルゴリズムについて説明します。
1. cbl_Layoutパラメーターに基づくフレーミングHTMLタグ(テーブル、div)の選択。
2. cbl_RepeatColumnsパラメーターとコレクション内のレコード数に基づいて、コレクションの繰り返し回数を決定します。
3.現在の反復のシリアル番号に基づいてコレクションからアイテムを選択するための条件の形成:
3.1。 出力が水平で、列数が4の場合、最初の行はコレクションの最初の4つの要素、次の4つのうちの2番目の要素などで構成される必要があります。
[0 1 2 3] - 0 [4 5 6 7] - 1 [8 9 10 11]
3.2。 出力が垂直に実行され、列数が4で要素の合計数が24である場合、最初の行はコレクションの6番目の要素ごと、反復数の2番目+ 6などで構成される必要があります。
[0 6 12 18] - 0 [1 7 13 19] - 1 [2 8 14 20] [3 9 15 21] [4 10 16 22] [5 11 17 23]
3.3。 除算の残りの部分についての反復回数(要素数/列数)を計算するときは忘れないでください!= 0.この場合、コレクション全体を表示するには追加の反復が必要です。
[0 1 2 3] - 0 [4 5 6 7] - 1 [8 9 10 11] [12 13 - -]
4.フレーミングタグ(div、table)に応じて、各反復に適したサンプルの文字列の形成:
4.1。 divの場合、行末のハイフンのみになります。
4.2。 テーブルの場合、これは次のとおりです。
4.2.1。 行の先頭。 ラベル 要素を列挙する過程で。 行末に。
5.フレーミングタグを閉じて、生成された文字列をビューに返します。
記事の主なアイデアはコードとアルゴリズムではないため、機能の一部のみを紹介します。記事の最後にプロジェクトをダウンロードして、CheckBoxマークアップの形成についてもう少し詳しく説明します。
HTMLマークアップCheckBox'aの形成とモデルバインディング
一般に、これはコレクションの要素とコントロールの設定が渡される最も単純なコードです。 おそらく、このコードの主なことは、name属性にCheckBoxList全体の名前が割り当てられることです。 これは、将来、 DemoControllerコントローラーのIndexメソッドでサーバー側でデータを受信するときに、選択された値を形成された配列の形式で取得できるようにするためです-少し後で例を示します。
public static string GenerateHtmlMarkup_CheckBox(KeyValuePair<string, int> item, CheckBoxList_Settings settings) { TagBuilder tagBuilder = new TagBuilder("input"); tagBuilder.MergeAttribute("type", "checkbox"); tagBuilder.MergeAttribute("name", settings.cbl_Name); tagBuilder.MergeAttribute("value", item.Value.ToString()); return tagBuilder.ToString(TagRenderMode.SelfClosing); }
ラベルのHTMLマークアップ
public static string GenerateHtmlMarkup_Label(KeyValuePair<string, int> item) { TagBuilder tagBuilder = new TagBuilder("label"); tagBuilder.SetInnerText(item.Key); return tagBuilder.ToString(TagRenderMode.Normal); }
反復回数の計算:
int iMod = items.Count % (int)settings.cbl_RepeatColumns; int iterationsCount = items.Count / (int)settings.cbl_RepeatColumns + (iMod == 0 ? 0 : 1);
各反復のサンプリング条件:
foreach (KeyValuePair<string, int> item in items.Where((item, index) => settings.cbl_Direction == Direction.Horizontal ? index / (int)settings.cbl_RepeatColumns == i : (index - i) % iterationsCount == 0))
ここで、iは反復回数です。
水平に表示するとき、列数== iで割ったときのインデックスを持つ要素を取ります:
[0 1 2 3] [4 5 6 7] [8 9 10 11]
垂直方向に表示する場合、そのインデックスと現在の反復を反復数== 0で除算した残りの要素を取得します。
[0 6 12 18] [1 7 13 19]
ヘルスチェック
最終的に得られた3つのCheckBoxListの例を見てみましょう。
<h2>Index</h2> @using (Html.BeginForm()) { @Html.Partial("~/Views/Partial/Controls/CheckBoxList.cshtml", new CheckBoxList_Model { items = Model.Languages, settings = new CheckBoxList_Settings { cbl_Name = "SelectedCheckBoxListItems", cbl_Layout = Layoutt.Table, cbl_Direction = Direction.Horizontal, cbl_RepeatColumns = RepeatColumns.FiveColumns }, htmlAttributes = new { @cellpadding = "0", @cellspacing = "0" } }) <br /> @Html.Partial("~/Views/Partial/Controls/CheckBoxList.cshtml", new CheckBoxList_Model { items = Model.Languages, settings = new CheckBoxList_Settings { cbl_Name = "SelectedCheckBoxListItemsTwo", cbl_Layout = Layoutt.Flow, cbl_Direction = Direction.Vertical, cbl_RepeatColumns = RepeatColumns.ThreeColumns } }) <br /> @Html.Partial("~/Views/Partial/Controls/CheckBoxList.cshtml", new CheckBoxList_Model { items = Model.Languages, settings = new CheckBoxList_Settings { cbl_Name = "SelectedCheckBoxListItemsThree", cbl_Layout = Layoutt.Flow, cbl_Direction = Direction.Horizontal, cbl_RepeatColumns = RepeatColumns.TwoColumns } }) <br /> <input type="submit" value="send" /> }
選択した値を取得するには、CheckBoxListsに渡す名前を持つ3つの配列をViewModel_Indexビューのモデルに追加する必要があります。
public class ViewModel_Index { public IDictionary<string, int> Languages { get; set; } public int[] SelectedCheckBoxListItems { get; set; } public int[] SelectedCheckBoxListItemsTwo { get; set; } public int[] SelectedCheckBoxListItemsThree { get; set; } }
投稿リクエストを処理するインデックスコントローラーメソッドを追加します。
[HttpPost] public ActionResult Index(ViewModel_Index model) { //TODO: return View(); }
ブレークポイントを設定したら、結果を見てみましょう。
コードの複製、表現モデル、メンテナンスにまったく関心がない場合は、ビューでCheckBoxList自体を直接呼び出して、サーバー上の選択された値を配列で直接取得できます(ただし、CheckBoxListの名前と受け入れられたパラメータを忘れないでくださいサーバー上の同じである必要があります):
さて、私たちが計画したことを完全に終えたので、最後は残っています。 オブジェクトhtmlAttributesの例を使用して、新しいプロパティでコントロールを拡張することを検討してください。
CheckBoxListの拡張
新しいプロパティを要素のコードに渡すには、コンストラクターから最初の呼び出し(または、その逆)に移動する必要があります。 すべてはControls.csのコンストラクターから始まります。新しいオブジェクトhtmlAttributesパラメーターを追加します。
public static MvcHtmlString CheckBoxList(this HtmlHelper helper, IDictionary<string, int> items, CheckBoxList_Settings settings, object htmlAttributes)
コンストラクターは、名前がCheckBoxList.cshtmlのパーシャルビューで呼び出されます 。 CheckBoxListモデルの新しいプロパティを追加します。
public class CheckBoxList_Model { public IDictionary<string, int> items; public CheckBoxList_Settings settings; public object htmlAttributes; }
パーシャルビューで呼び出しに渡します:
宛先:
@model CheckBoxList_Model @Html.CheckBoxList(Model.items, Model.settings)
後:
@model CheckBoxList_Model @Html.CheckBoxList(Model.items, Model.settings, Model.htmlAttributes)
インデックスビューでプロパティの初期化を追加し、要素のコードを補完することは残ります。
@Html.Partial("~/Views/Partial/Controls/CheckBoxList.cshtml", new CheckBoxList_Model { items = Model.Languages, settings = new CheckBoxList_Settings { cbl_Name = "SelectedCheckBoxListItemsThree", cbl_Layout = Layoutt.Table, cbl_Direction = Direction.Vertical, cbl_RepeatColumns = RepeatColumns.FourColumns }, htmlAttributes = new { @border = "3", style = "color: Grey; border-style:dashed;" } })
ワイヤフレーム選択コード(テーブル、div):
public static TagBuilder GenerateHtmlMarkup_OuterTag(Layoutt cbl_Layout, IDictionary<string, object> htmlAttributes) { ... TagBuilder tagBuilder = new TagBuilder(htmlTag); tagBuilder.MergeAttributes(htmlAttributes); ... }
以下に示すように、すべてが機能します。
おわりに
結論として、私たちがしたことを理解したいと思います。
- Html ASP .NET MVCヘルパーに基づいて独自のコントロールを作成、使用、拡張する可能性を検討しました。
- コントロールをさらに開発するためのプロジェクトフレームワークを作成しました。
- 各機能ユニットの責任を分割して、Webアプリケーションの構造を実装しました。
- 独自のコントロールの作成で競合するものがあるサイトについて学びました。
材料リスト
1.http : //mvc.devexpress.com/Editors/CheckBoxList
2. ASP.NET MVCフォームでチェックボックスを処理する方法
3. プロジェクトのソースコード