Posted in: Comments

First off, there is a similar solution already in the Alloy templates. But this one is a bit more refined.

So the problem. Lets say you have a block that represents a teaser, the teaser has a tagline, heading, image and link. When the editor has specified a link, the tagline and heading should be wrapped with the link, otherwise they should be wrapped with a div. Something like this:

<div class="teaser">
	<div class="image-wrapper">
		<img src="/image.jpg" alt="" />
	</div>
	<a class="content-wrapper" href="/about-us/">
		<p>Tagline</p>
		<h2>Heading</h2>
	</a>
</div>

And without a link:

<div class="teaser">
	<div class="image-wrapper">
		<img src="/image.jpg" alt="" />
	</div>
	<div class="content-wrapper">
		<p>Tagline</p>
		<h2>Heading</h2>
	</div>
</div>

There is also the case when the teaser should be edited, the link can cause problems for the editors and it’s best to replace it then as well.

This can be solved with a lot of if-statements in the markup and check if the link is null and/or you’re in edit mode, maybe in a combination with placeholder controls with the markup that should be toggled. It will get messy though. Something like this:

<div class="teaser">
	<div class="image-wrapper">
		<EPiServer:Property runat="server" PropertyName="Image" />
	</div>
	<asp:PlaceHolder runat="server" Visible="<%# CurrentBlock.Link != null && PageEditing.PageIsInEditMode == false %>">
		<a class="content-wrapper" href="<%= CurrentBlock.Link  %>">
	</asp:PlaceHolder>

	<asp:PlaceHolder runat="server" Visible="<%# CurrentBlock.Link == null || PageEditing.PageIsInEditMode %>">
		<div class="content-wrapper">
	</asp:PlaceHolder>

		<EPiServer:Property runat="server" PropertyName="Tagline" CustomTagName="p" />
		<EPiServer:Property runat="server" PropertyName="Heading" CustomTagName="h2" />

	<asp:PlaceHolder runat="server" Visible="<%# CurrentBlock.Link == null || PageEditing.PageIsInEditMode %>">
		</div>
	</asp:PlaceHolder>

	<asp:PlaceHolder runat="server" Visible="<%# CurrentBlock.Link != null && PageEditing.PageIsInEditMode == false %>">
		</a>
	</asp:PlaceHolder>
</div>

Instead we can create a web control that handles this. We can then use this web control to wrap the content. If we have specified a tag name in CustomTagName, this tag will be rendered instead of the link if the link is null or we’re in edit mode, otherwise no tag will be rendered.

namespace DV.Controls
{
    using EPiServer;
    using EPiServer.Editor;
    using System;
    using System.Web.UI;
    using System.Web.UI.WebControls;

    /// <summary>
    /// A web control that renders a hyperlink when not in edit mode and the specified url is not null.
    /// If in edit mode or the url is null, a tag of the type specified in CustomTageName is rendered instead.
    /// If no tag is specified in CustomTageName, no tag is rendered at all.
    /// </summary>
    [ParseChildren(false)]
    [PersistChildren(true)]
    public class ToggleLink : WebControl
    {
        public Url Url { get; set; }

        public string CustomTagName { get; set; }

        private bool RenderCustomTag
        {
            get
            {
                return PageEditing.PageIsInEditMode || this.Url == null;
            }
        }

        protected override HtmlTextWriterTag TagKey
        {
            get
            {
                if (this.RenderCustomTag)
                {
                    if (string.IsNullOrEmpty(this.CustomTagName))
                    {
                        return HtmlTextWriterTag.Unknown;
                    }
                    else
                    {
                        return (HtmlTextWriterTag)Enum.Parse(
                            typeof(HtmlTextWriterTag),
                            this.CustomTagName,
                            true);
                    }
                }

                return HtmlTextWriterTag.A;
            }
        }

        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            base.AddAttributesToRender(writer);

            if (this.TagKey == HtmlTextWriterTag.A)
            {
                writer.AddAttribute(HtmlTextWriterAttribute.Href, this.Url.ToString());
            }
        }

        public override void RenderBeginTag(HtmlTextWriter writer)
        {
            if (this.RenderCustomTag &&
                this.TagKey == HtmlTextWriterTag.Unknown)
            {
                return;
            }

            base.RenderBeginTag(writer);
        }

        public override void RenderEndTag(HtmlTextWriter writer)
        {
            if (this.RenderCustomTag &&
                this.TagKey == HtmlTextWriterTag.Unknown)
            {
                return;
            }

            base.RenderEndTag(writer);
        }
    }
}

Now we can write like this instead:

<div class="teaser">
	<div class="image-wrapper">
		<EPiServer:Property runat="server" PropertyName="Image" />
	</div>
	<DV:ToggleLink Url="<%# CurrentBlock.Link %>" CustomTagName="div" CssClass="content-wrapper" runat="server">
		<EPiServer:Property runat="server" PropertyName="Tagline" CustomTagName="p" />
		<EPiServer:Property runat="server" PropertyName="Heading" CustomTagName="h2" />
	</DV:ToggleLink>
</div>

Much nicer code, if you ask me.