1

We are building a class library that provides certain structured types for the view model, e.g. InlineImage.

I need to be able to define default HTML output for such instance when used with @Html.DisplayFor(...), basically ...

  • When /Views/DisplayTemplates/InlineImage.cshtml is available, then use that template
  • When not available, it should output the default

However ...

  • When I override just ToString() of that class, it gives me the correct output, but it gets HTML encoded

I found out analyzing MVC source code that I am able to disable encoding by adding [DisplayFormat(HtmlEncode = false)] to the CLASS

BUT ... the attribute does not target class, so I hack it by wrapping it to another attribute which I add to the class. It is nasty, but at least works :)

My code currently looks like this:

[AttributeUsage(AttributeTargets.Class)]
class DisableHtmlEncodeAttribute : DisplayFormatAttribute
{
    public DisableHtmlEncodeAttribute()
    {
        HtmlEncode = false;
    }
}

[DisableHtmlEncode]
internal class InlineImage : IInlineImage
{
    public string AltText { get; set; }

    public string Src { get; set; }


    public override string ToString()
    {
        return $"<figure><img src=\"{Src}\" alt=\"{AltText}\"></figure>";
    }
}

It works for the default display, but when display template is provided in file system, it is not used. Probably because something along the way cuts it of because of that data annotation.

I have already tried several other approaches similar to this:

  • Using first property with Html data annotation
  • Using display property with Html data annotation
  • Implementing IHtmlString

But the framework seems to check the metadata only for the class itself, but not for its properties in this case. And IHtmlString is completely ignored.

I am looking for any hints how to provide default display template for the given class from a class library, that could be overriden just by placing standard display template to views folder.

martinh_kentico
  • 913
  • 7
  • 17

1 Answers1

1

So it turned out that I was closer than I thought and with the last trial I made it working.

The trick is similar to what I did with the DisableHtmlEncode attribute, but with UIHint attribute. This way you can apparently tell the engine to apply display template to a class, and it is stronger than the Format attribute.

Here is the final code that provides the ability for default HTML markup, and when display template is provided in FS, it uses that template:

[AttributeUsage(AttributeTargets.Class)]
class DisableHtmlEncodeAttribute : DisplayFormatAttribute
{
    public DisableHtmlEncodeAttribute()
    {
        HtmlEncode = false;
    }
}

[AttributeUsage(AttributeTargets.Class)]
class UseDisplayTemplateAttribute : UIHintAttribute
{
    public UseDisplayTemplateAttribute(string uiHint)
        : base(uiHint)
    {
    }
}

[DisableHtmlEncode]
[UseDisplayTemplate("InlineImage")]
internal class InlineImage : IInlineImage
{
    public string AltText { get; set; }

    public string Src { get; set; }

    public override string ToString()
    {
        return $"<figure><img src=\"{Src}\" alt=\"{AltText}\"></figure>";
    }
}

I am not particularly proud about the hacks, but as they say "it ain't stupid if it works".

If anyone has got a better idea, please share ...

martinh_kentico
  • 913
  • 7
  • 17