独自のCheckBoxListコントロールを作成する

まえがき



この記事では、独自のコントロールを作成し、それをASP.NET MVC 3.0の新しいプロジェクトの一部として使用する可能性について説明します。 以下に書かれているものはすべて作者の視点であり、広く普及している、または一般に受け入れられているコントロールの作成方法と一致しない場合があるため、批判やコメントを歓迎します。



はじめに



最も可能性が高いのは、以前にWinFormsまたはASP .NET WebFormsを使用してプロジェクトで作業していた多くのユーザーが、ASP .NET MVCプロジェクトのHTMLヘルパーがCheckBoxListなどのコントロールを作成できないことでした。ユーザープロファイル内またはHabrahabrに新しいトピックを追加する際に、データまたは非構造化データを複数選択します。 もちろん、単一のCheckBoxまたはCheckBoxForの使用を禁止する人はいませんが、このようなチェックボックスのグループで作業するのが便利で、メンテナーが簡単に拡張、理解でき、重複から保護されるコードは、将来のプロジェクトで成果を使用する予定のプログラマーにとって最後の質問ではありません。 そして、視覚表示のコントロールにいくつかの便利なオプションを追加できることを考慮すると、作成の必要性がますます明らかになります。



最後にサブジェクトエリアに飛び込む前に、問題のコントロールを作成するアイデアは、 http://mvc.devexpress.comのサイトを表示した後にインスピレーションを受けたことに注意したいと思います。すごい。 この記事が承認され、肯定的なフィードバックが寄せられた場合、コミュニティのハブに関する一連の記事の一部としてサイトで提示されたコントロールのさらなるレビューと実装が可能です。



分析



そもそも、何をしようとしているのかを理解する必要があります。 コントロールは、渡されたデータコレクションを表示できるCheckBoxesのグループによって表示され、フォームをサーバーに送信するときにモデルバインディングの概念を使用し、次の表示設定があります。



プロジェクト作成



空の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 })
      
      







プロジェクトを開始すると、馴染みのある同じレコードが表示されます。







現在のアプリケーション構造の分析



先に進む前に、アプリケーションの現在の構造を理解し、場合によっては変更する必要があります。 ここで私が考えたいことは次のとおりです。



上記から、必要なものは次のとおりです。



注:ただし、アプリケーションコードの記述を開始する前に、これらの質問や他の多くの質問を考慮に入れて、自問する必要があります。 この記事は、初めて似たようなことをしようとする人を対象にしています。その後、一貫して試行錯誤を繰り返して解決策を導いていきます。



ビューモデルの追加



プレゼンテーションモデルは、その中のデータを送信するだけでなく、受信するのにも役立つため、送信されたコレクションに加えて、選択した要素の配列を追加する必要があります。 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); ... }
      
      







以下に示すように、すべてが機能します。







おわりに



結論として、私たちがしたことを理解したいと思います。





材料リスト



1.http//mvc.devexpress.com/Editors/CheckBoxList

2. ASP.NET MVCフォームでチェックボックスを処理する方法

3. プロジェクトのソースコード



All Articles