@using () {...} を使ったカスタムHtmlヘルパーの作り方
この記事で知れること
- using (@Html.BeginForm()) {} が何をしているのかわかる
- using で書けるカスタムHtmlヘルパーを作れる
まえがき
using を使ったHtmlヘルパーといえば、@Html.BeginForm()です。
Formタグを出力したいとき、Viewでこのように書くと…
@using (Html.BeginForm()) { <input type="submit" value="追加!" /> }
このように出力されます。
<form action="/" method="post"> <input type="submit" value="追加!" /> </form>
今日は、このusingを使った独自のHtmlヘルパーを作り、
へーBeginFormってこうなっているんだーと、確認してみます。
例
このように書くと…、
@using (Html.BeginRead())
{
<p>
吾輩は猫である。名前はまだ無い。
</p>
}
このように出力されるHtmヘルパーを作成します。
ここからここまで読んだヘルパーです。
<span>----------------ここから↓</span> <p> 吾輩は猫である。名前はまだ無い。 </p> <span>----------------↑ここまで読んだ</span>
Html.BeginForm()の中身
@using (Html.BeginForm())は何をやっているのか、MVCのソースと合わせて確認します。
@using () { } は、C#の構文であるusingを、Razorで記述したものです。
using ()の中は、IDisposableを実装したクラスでなければいけません。
Html.BeginForm()メソッド は、IDisposableを実装したクラスを返しています。
メソッド実行時にformの開始タグが出力され、
IDisposableを実装したクラスのDispose()メソッドで、閉じタグが出力されます。
こんな感じ。
MVCのソースで確認
MVCのソースを見て確認してみます。
FormExtensions.cs
Html.BeginForm()メソッドの実際の処理は、このFormExtensionsクラスの1番下にあります。
FormタグをhtmlHelper.ViewContext.Writer.Write(..)で出力し、
IDisposable実装したMvcFormクラスを返しています。
MvcFormクラスのDispose()メソッドを見てみます
MvcForm.cs
メソッド内で、FormExtensions.EndForm(_viewContext);
が呼ばれているので、先ほどのクラスに戻ります。
(前はここで_writer.Write("");と書かれていました)
開始タグと同じように、出力タグも出力されています。
カスタムHtmlヘルパー実装
IDisposableを実装したクラスを返すHtmlヘルパーを実装し、
開始と終了時に、Writer.Write("<..>"); が呼ばれるようにすればOKです。
コードはこんな感じになりました。
public static class ReadingHelper { public static void BeginReadTagWrite(this HtmlHelper htmlHelper) { var tagBuilder = new TagBuilder("span"); tagBuilder.SetInnerText("----------------ここから↓"); htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.Normal)); } public static void EndReadTagWrite(this HtmlHelper htmlHelper) { var tagBuilder = new TagBuilder("span"); tagBuilder.SetInnerText("----------------↑ここまで読んだ"); htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.Normal)); } public static DisposableHelper BeginRead(this HtmlHelper htmlHelper) { Action start = () => BeginReadTagWrite(htmlHelper); Action end = () => EndReadTagWrite(htmlHelper); start(); return new DisposableHelper(end); } } public class DisposableHelper : IDisposable { public DisposableHelper(Action end) { _end = end; } private bool _disposed; private Action _end; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { _disposed = true; _end(); } } }
実用性無し?
でも、このusingを使ったカスタムHtmlヘルパー、実用性はあるのでしょうか…。
Formタグの場合は、送信時に中のinput要素の値を送る、という機能が関係しているからこそ、
このusingが生きているのだと思います。
上の例のように、ただ出力するだけのusingのヘルパーは作る必要がない…と思います。
インデント1コ消費しちゃいますし。
何か良い例はないかな〜と探していたのですが、見つけることができませんでした。
実装は、下のサイトを参考にしました。
Custom html helpers: Create helper with “using” statement support
追記
素晴らしい使い方がありました!!
ASP.NET MVC で Twitter Bootstrap を使ってみた (4)