9

IDisposable を使用して他のコードをラップする BeginForm スタイルの html ヘルパーを作成しようとしています。特定の条件が満たされた場合 (ユーザーが特定の役割にあるなど) にのみ、ヘルパーがラップされたコードをレンダリングするようにします。

私は単純に、Begin メソッドで context.Writer を切り替えて、Dispose メソッドで元に戻すことができると考えました。以下のコードはコンパイルおよび実行されますが、ラップされたコンテンツは常にレンダリングされます。それをステップスルーすると、ラップされたコンテンツは新しい StringWriter に書き込まれないため、制御できません。

    public static IDisposable BeginSecure(this HtmlHelper html, ...)
    {
        return new SecureSection(html.ViewContext, ...);
    }

    private class SecureSection : IDisposable
    {
        private readonly ViewContext _context;
        private readonly TextWriter _writer;

        public SecureSection(ViewContext context, ...)
        {
            _context = context;
            _writer = context.Writer;
            context.Writer = new StringWriter();
        }

        public void Dispose()
        {
            if (condition here)
            {
                _writer.Write(_context.Writer);
            }

            _context.Writer = _writer;
        }
    }

私がやろうとしていることは、htmlヘルパーで可能ですか?

Razor の宣言型 HTML ヘルパーはおそらく機能することはわかっていますが、MVC3 の Razor ヘルパーの app_code 制限を考えると、可能であれば標準の html ヘルパー アプローチを好むでしょう。

4

2 に答える 2

10

実際には、BeginForm のような構造で条件付きでコンテンツを非表示にすることができます。内部の StringBuilder を少しいじるだけです。

public class Restricted: IDisposable
{
    public bool Allow { get; set; }

    private StringBuilder _stringBuilderBackup;
    private StringBuilder _stringBuilder;
    private readonly HtmlHelper _htmlHelper;

    /// <summary>
    /// Initializes a new instance of the <see cref="Restricted"/> class.
    /// </summary>
    public Restricted(HtmlHelper htmlHelper, bool allow)
    {
        Allow = allow;
        _htmlHelper = htmlHelper;
        if(!allow) BackupCurrentContent();
    }

    private void BackupCurrentContent()
    {
        // make backup of current buffered content
        _stringBuilder = ((StringWriter)_htmlHelper.ViewContext.Writer).GetStringBuilder();
        _stringBuilderBackup = new StringBuilder().Append(_stringBuilder);
    }

    private void DenyContent()
    {
        // restore buffered content backup (destroying any buffered content since Restricted object initialization)
        _stringBuilder.Length = 0;
        _stringBuilder.Append(_stringBuilderBackup);
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    public void Dispose()
    {
        if(!Allow)
            DenyContent();
    }
}

次に、上記のオブジェクトのインスタンスを作成する HtmlHelper を作成するだけです。

public static class RestrictedHelper
{
    public static Restricted RestrictedContent(this HtmlHelper htmlHelper, bool allow)
    {
        return new Restricted(htmlHelper, allow);
    }
}

使用方法は次のとおりです。

@using (var restricted = Html.Restricted(true))
{
    <p>This will show up</p>
}
@using (var restricted = Html.Restricted(false))
{
    <p>This won't</p>
}

利点:

  • コンテンツを表示/非表示にするカスタム ロジックを記述し、それを Restricted コンストラクターに渡します。
  • Restricted オブジェクトのパブリック プロパティは、ビューのコード ブロックでアクセスできるため、そこで計算された値を再利用できます。

ASP.Net MVC 4 でテスト済み

于 2013-01-22T18:46:28.977 に答える
6

を返すヘルパー メソッドの本体コンテンツを条件付きでレンダリングすることはできませんIDisposable。常にレンダリングされます。ヘルパーが要素に対して行うusingように、ブロックの本体をカスタム マークアップでラップする場合は、このスタイルのヘルパーを使用できます。BeginForm<form>

templated Razor delegate代わりにa を使用できます。

public static class HtmlExtensions
{
    public static HelperResult Secure(this HtmlHelper html, Func<object, HelperResult> template)
    {
        return new HelperResult(writer =>
        {
            if (condition here)
            {
                template(null).WriteTo(writer);
            }
        });
    }
}

その後:

@Html.Secure(
    @<div>
         You will see this text only if some condition is met
    </div>
)
于 2012-08-14T11:39:47.410 に答える