Evander Vanderfeesten
Sep 20, 2011
(0 votes)

Creating a custom View tab in the EditPanel

The EditPanel contains the default View tab to view the page you are editing.

But what if you have a PageData object that has no explicit view? For instance a teaser or menu block which is reusable throughout your website project and is part of another page template?

It would be useful if we could create a custom view in the EditPanel View tab that displays the PageData object ‘in place’ on a specific page.

This post explains how to create such a custom View tab. Bear in mind this is just an example of how to achieve such a custom View. In this specific problem there are other ways of handling the View of such PageData objects. The main objective of this post is to give insight in a possible solution to the problem and should not by default be read as the best solution. All code mentioned within this post is usable, but could well be optimized in many ways.

To accomplish this we need to create a custom View plugin UserControl and ‘Hook’ into the EditPanel LoadComplete event.


Create our GUI Plugin UserControl

We start by creating the correct code which will render the content of the View tab. This code would be very similar to the PreviewControl.ascx UserControl which is used for the default View tab that comes with your standard EPiServer installation.

Take a look at the default PreviewControl.ascx UserControl in your EPiServer install directory. This should be available at the following location, dependent on which version of EPiServer you're running:
C:\Program Files (x86)\EPiServer\CMS\5.2.375.236\Application\UI\Edit\PreviewControl.ascx.

   1: <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="CustomViewGUIPlugin.ascx.cs" Inherits="EPiServerDemo.Business.View.CustomViewGUIPlugin" %>
   3: <div Runat="server" ID="FirstLanguageDiv">
   4:     <EPiServerUI:SystemIframe Id="PreviewFrame" Name="PreviewFrame" CssClass="previewframe" runat="server" />
   5:     <EPiServerScript:ScriptEvent ID="ScriptEvent1" EventTargetId="PreviewFrame" EventType="ReadyStateChange" EventHandler="SkipReadyState" runat="server"/>
   6:     <EPiServerScript:ScriptEvent ID="ScriptEvent2" EventTargetClientNode="window" EventType="Load" EventHandler="SetupFrameContentLoaded" runat="server"/>
   7:     <EPiServerScript:ScriptResizeWindowEvent ID="ScriptResizeWindowEvent1" EventTargetClientNode="window" ResizeElementId="PreviewFrame" runat="server" />
   8: </div>    
   9: <div Runat="server" ID="SecondLanguageDiv" />

Our ‘CustomViewGUIPlugin’ UserControl,displayed above, looks pretty much the same as the default PreviewControl.ascx UserControl mentioned. At this point we have the rendering of our View tab in place.


The code behind of our custom GUI Plugin UserControl.

To create a UserControl that will act as a GUI Plugin we need to add the ‘[GuiPlugIn]’ attribute to our class definition as shown in the following code fragment.

   1: [GuiPlugIn(
   2:         Area = PlugInArea.EditPanel,
   3:         DisplayName = "View",
   4:         SortIndex = 100,
   5:         Url = "~/Plugins/CustomViewGUIPlugin.ascx",
   6:         RequiredAccess = AccessLevel.Publish)]
   7: public partial class CustomViewGUIPlugin : UserControlBase, ICustomPlugInLoader
   8: {
   9:     //logic
  10: }

Make sure the different GuiPlugIn properties reflect your implementation. The SortIndex property is important here. The default View tab doesn’t have a SortIndex, since this is not being generated through a plugin. The first tab that has a SortIndex is the Edit tab which has a SortIndex of 200. So by setting the SortIndex property of our custom GuiPlugIn to a value lower than 200 (in our case to 100) would automatically mean it will be displayed first in the list of tabs in the EditPanel. (Thanks to the article ‘Setting the Edit Panel Tab Order in CMS 5’ by Jeff Wallace!)


We also need a way to provide the Url of the page we want to use for our custom View. Since our objective is to create a generic solution we need to make sure we can set this custom Url differently for specific pages. A good way to provide such a flexible Url is to add a Dynamic Property to the website project.

So I’ve added a ‘Page’ property as a Dynamic Property to the website project called ‘CustomViewUrl’. The main reason for choosing a Dynamic Property was simply because those are available on any PageData object within our website project.

Within our GUI Plugin we need to check if this Dynamic Property has been set for the current page.

Take a look at the post ‘Access Dynamic Properties when PageData is writable’ by Fredrik Hagland for more details on how to do this. In this example the CustomViewUrl has been implemented as a protected property with only a getter and no setter.

Further we need to check if the value has been set, and if it can be parsed back to the Page provided, since we need the LinkURL of that page.

   1: /// <summary>
   2: /// Initialize the plugin
   3: /// </summary>
   4: protected override void OnLoad(EventArgs e)
   5: {
   6:     base.OnLoad(e);
   8:     if (string.IsNullOrEmpty(CustomLinkUrl))
   9:     {
  10:         Visible = false;
  11:         return;
  12:     }
  14:     //set the url to the previewframe
  15:     PreviewFrame.SourceFile = Url;
  16: }


The next step is to implement the ‘PlugInDescriptor[] List()’ method from the ICustomPlugInLoader interface.

   1: public PlugInDescriptor[] List()
   2: {
   3:     _pageToEdit = CurrentPage.CreateWritableClone();
   5:     PlugInDescriptor[] desc = null;
   7:     // hook LoadComplete-event on EditPanel page
   8:     EPiServer.UI.Edit.EditPanel editPanel = HttpContext.Current.Handler as EPiServer.UI.Edit.EditPanel;
  10:     if (null != editPanel)
  11:     {
  12:         //Only modify tabs when the page selected is of a specific type - in this case the types name starts with "[Data]".
  13:         if ((editPanel as PageBase).CurrentPage.PageTypeName.StartsWith("[Data]"))
  14:         {
  15:             editPanel.LoadComplete += new EventHandler(EditPanelLoadComplete);
  17:             //disable View tab if no valid url is present
  18:             if (string.IsNullOrEmpty(CustomLinkUrl))
  19:             {
  20:                 return new PlugInDescriptor[0] { };
  21:             }
  23:             desc = new PlugInDescriptor[1];
  24:             // Smart way to load plug-ins
  25:             desc[0] = PlugInDescriptor.Load(GetType());
  26:         }
  27:     }
  28:     return desc;
  29: }

In this implementation we add a ‘LoadComplete’ event for the EditPanel so we can manipulate the rendering of the EditPanel tabs. Thanks to the article ‘Neat Trick: Modifying Edit Mode Tabs’ by Allan Thræn! Further we check if a valid CustomViewUrl is present. If there isn’t a valid CustomViewUrl present, we simply return a new empty PlugInDescriptor so no new tabs will be rendered.

We also need to implement our LoadComplete event of the EditPanel for which we’ve added a new EventHandler. Again taken directly from the article by Allan Thræn mentioned above.

   1: protected void EditPanelLoadComplete(object sender, EventArgs e)
   2: {
   3:     // find the TabStrip with id = "actionTab"
   4:     TabStrip actionTabStrip = this.FindControl<TabStrip>(sender as Control, "actionTab");
   8:     // remove View-tab and set active tab to next first tab.
   9:     if (actionTabStrip.SelectedTab == 0) actionTabStrip.SetSelectedTab(1);
  10:     ((Tab)actionTabStrip.Controls[0]).Visible = false;
  11: }

We’re only manipulating our tabs by doing two things. First we need to disable the original View tab. Secondly we set the active tab to the first next tab available, which is our custom View tab thanks to the SortIndex we’ve set earlier.


That’s it, just make sure your project compiles and open up your website. Set the Dynamic Property, on the PageData object you want to provide a custom view for, with another page in your website project where the object will be used and has a default View. Edit your PageData object which doesn’t have an explicit View, and open up the View tab (which is our custom View tab). You should now see the provided page in your View instead of an empty View.

Sep 20, 2011


Sep 21, 2011 03:14 AM

Nice post!

if you are using CMS 6 R2, don't forget about container pages:


Eric Petersson
Eric Petersson Nov 8, 2016 12:06 PM

When setting access rights for different tabs, make sure PageTypeBuilder is set with the EPiServer.Security.AccessLevel and the level you would like your content editors to access the tabs. There was no collection of subtabs to the parent tabs in Control class what I could find.

Please login to comment.
Latest blogs
Delete a content – the right way

I stumped upon this blogpost Delete content directly from the Optimizely database – Tomas Hensrud Gulla by Tomas, an OMVP. While the post is helpfu...

Quan Mai | Feb 8, 2023 | Syndicated blog

The Content Revolution - Generative AI

Hi Optimizely Community,  For the past two decades, you and Optimizely have been at the forefront of content management - creating, managing,...

Alex Atzberger | Feb 6, 2023

Delete content directly from the Optimizely database

Do you know your way around the Optimizely CMS database? Deleting, or updating, content directly in the database is pretty straightforward, but of...

Tomas Hensrud Gulla | Feb 6, 2023 | Syndicated blog

Rolling your site out to China

How to rollout your website to the China market Continue reading →

Andrew Markham | Feb 5, 2023 | Syndicated blog

Optimizely PIM - Variant Management Overhaul & Rollout

Optimizely PIM has completely overhauled variant management with improved usability and support for variant swatches. In order to enable this...

Arthur Vander Voort | Feb 4, 2023

You Just Got Vectored! SVG Image Formats

 If you're reading this, then you've come across a need that nearly all Opti developers encounter in their careers; You need to display a vector...

Greg J | Feb 3, 2023 | Syndicated blog