Author Archives: Erik Nordin Wahlberg

EPiServer:PageList with ItemDataBound

In my last post I explained how you can extend your episerver:pagelist with your own templates (i.e. SelectedItemTemplate, AlternatingItemTemplate). In this post I will show you how to extend your PageList so you can use ItemDataBound. This is very useful if you want to use asp:controls in your templates and then populate them from code behind.

In this post I will start with the code from my last post and extend it further.

1. Creating PageListEventArgs
First of all we need to create our own EventArgs by extending EventArgs. In this case we need to access to the PageData that is bound to the Template, and we also need access to the Template which should be rendered. So this is the class we need:

public class PageListEventArgs : EventArgs
{
public PageData DataItem { get; set; }
public Control ControlItem { get; set; }
}

If you look at the RepeaterItemEventArgs, which is used by the asp:Repeater, you can get some ideas what else that can be useful in this EventArgs.

2. Creating PageListEventHandler
This is where we create our Eventhandler that will use the PageListEventArgs we just made. It’s just two lines of code:

[Serializable]
public delegate void PageListEventHandler (object sender, PageListEventArgs e);

3. Add PageListEvenHandler to the PageList
Ok, now that we have made an EventHandler we need to use it somehow. So, in the PageList we add the PageListEventHandler:

public event PageListEventHandler ItemDataBound;

By doing this, we can now add events to our PageList. We now need to make sure that the event is triggered when it should. So let’s jump into CreateChildControls, and find where the Item/SelectedItemTemplate is added. Right below the

ItemTemplate.InstantiateIn(control2);

we add

if (ItemDataBound != null)
{
PageListEventArgs args = new PageListEventArgs();
args.ControlItem = control2;
args.DataItem = pages[i];
ItemDataBound.Invoke(this, args);
}

When this is done, we have the option to add the ItemDataBound-event to our PageList!

This is how you do it:

<Antecknat:PageList ID="PageList1" PageLinkProperty="PageLink" OnItemDataBound="PageList1_ItemDataBound" runat="server">
<ItemTemplate>
<asp:Literal ID="Literal1" runat="server"></asp:Literal><br />
</ItemTemplate>
</Antecknat:PageList>

or from code behind:

PageList1.ItemDataBound += new Antecknat.Web.WebControls.PageListEventHandler(PageList1_ItemDataBound);

And to modify the controls in the current template:

protected void PageList1_ItemDataBound(object sender, <span>Antecknat.Web.WebControls</span>.PageListEventArgs e)
{

Literal l = e.Item.FindControl("Literal1") as Literal;
if(l!=null)
l.Text = e.DataItem.PageName;
}

EPiServer:PageList with SelectedItemTemplate

This is a quick guide how to extend EPiServer:PageList with a SelectedItemTemplate. This is a good start if you want to try to extend it with a SeparatorTemplate or something more spectacular.

1. Create a class
I.e. PageList.cs, a recommendation is to follow EPiServers namespace, so in this case I would create my class in Antecknat.Web.WebControls.
This class should inherit EPiServer.Web.WebControls.PageList

namespace Antecknat.Web.WebControls
{
public class PageList : EPiServer.Web.WebControls.PageList
{

2. Create the SelectedItemTemplate
This is done by the following code:

private ITemplate _selectedItemTemplate;
[TemplateContainer(typeof(PageTemplateContainer)), Browsable(false), PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate SelectedItemTemplate
{
get
{
return this._selectedItemTemplate;
}
set
{
this._selectedItemTemplate = value;
}
}

3. Modify CreateChildControls()
With Reflector (the best program ever made?) we can reuse the way controls are added to the regular PageList. We need to change the current method so that the SelectedItemTemplate is used when is supposed to.
In this case I want the current page, the parent page, the parents parents page and so on to count as selected. So we’ll make a PageReferenceCollection containing these pages.

// Find pages that should be marked as Selected
PageReferenceCollection prcActivePages = new PageReferenceCollection();
PageReference prCurrentLevel = CurrentPage.PageLink;
while (prCurrentLevel != null && prCurrentLevel != PageReference.EmptyReference)
{
prcActivePages.Add(prCurrentLevel);
prCurrentLevel = EPiServer.DataFactory.Instance.GetPage(prCurrentLevel).ParentLink;
}

And where the regular ItemTemplate is added, we change the code from:

ItemTemplate.InstantiateIn(control2);

to:

if (prcActivePages.Contains(pages[i].PageLink)) // Add to SelectedItemTemplate if in Selected collection
SelectedItemTemplate.InstantiateIn(control2);
else // Add to normal ItemTemplate
ItemTemplate.InstantiateIn(control2);

4. Change web.config
This is not a mandatory step, but if you always want to be able to use your fresh PageList from any aspx or ascx in the project, add teh following line in <system.web><pages><controls>

<add tagPrefix="Antecknat" namespace="Antecknat.Web.WebControls" assembly="Antecknat"/>

5. Use your PageList
Now you can use your PageList in your .aspx and .ascx files.
Just type

<Antecknat:PageList ID="PageList1" runat="server>
<HeaderTemplate><ul></HeaderTemplate>
<FooterTemplate></ul></FooterTemplate>
<ItemTemplate><li><%# Container.CurrentPage.PageName %></li>
<SelectedItemTemplate><li class="selected"><%# Container.CurrentPage.PageName %></li>
</Antecknat:PageList>

And then add a PageDataCollection from code behind and databind it.

Download sourcecode
PageList.zip

Add property to multiple page types

How fun is it when you realize that you have to add a property to 20 different page types?
Been there done that. So today when I realized that I had to do it again I wrote an admin plugin instead.

With this plugin you can create a new property just like you do in EPiServer, and then you select wich PageTypes the property should be saved to.

If a Property with the same name already exists it will leave the old one.

This Plugin has only been tested with EPiServer CMS R2, but I think it’ll work with any CMS5 releases.

If you are interested in a plugin like this to EPiServer 4, then you should check out PageTypeUtil on EPiCode.

Download the plugin

Extension Methods in .NET 3

I really love Extension methods released in .NET 3. Here are two simple, but useful examples:

This method will add a default text to your textbox, the text will be removed when the user focus the textbox, and if if it’s emtpy onblur, the default text will be shown again.

public static void AddTextRemovedOnFocus(this System.Web.UI.WebControls.TextBox tb, string text)
{
bool isPostback = ((System.Web.UI.Page)System.Web.HttpContext.Current.CurrentHandler).IsPostBack;
if (tb.TextMode != System.Web.UI.WebControls.TextBoxMode.Password &amp;amp;&amp;amp; !isPostback)
tb.Text = text;
else
tb.Attributes.Add("value", text);
tb.Attributes.Add("onfocus", string.Format("javascript: if({0}.value == '{1}') {0}.value='';", tb.ClientID, text));
tb.Attributes.Add("onblur", string.Format("javascript: if({0}.value == '') {0}.value='{1}';", tb.ClientID, text));
}

Another small method is this, just check if the string is empty or has no value when trimmed.

public static bool IsNullOrTrimEmpty(this string s)
{
return (s == null || s.Trim().Length == 0);
}

The first extension method will apperar as a method on TextBox-objects, and the second on string objects.

Feel free to submit some other nice extension methods. πŸ™‚

Implementing the FCKEditor

One of our customers really needed an editor that would work for FireFox when they are Mac-users, so I tried to implement the FCKEditor which EPiServer has an howto about: http://world.episerver.com/Download/Code-Samples/Editor-Tools/Editor-for-non-IE-browsers/.

I followed the steps in the article, but the editor only loaded about half of the times. Wierd, so I started debugging. No luck at all. Tried to deploy the project to our test-server and it worked all of the time.

Some problems/wanted features that I bumped into was:

  1. Got a Javascript error when trying to insert an EPiServer page
  2. The document tab was selected when i wanted to insert a EPiServer page
  3. When the property had no toolbar-options choosen(in admin) it still showed the rich text editor

Well, I really needed this to work ASAP so i made some modification to the EPiServer-FCKEditor project.

Solutions
1. In the WrapServerObjectBrowser.aspx I changed the content for the SelectPage-panel to this:

<script type="text/javascript"><!--
function OpenGetPageDialog(url, selectedpage, value, text, lang)
{
EPi.CreatePageBrowserDialog(url, selectedpage,'False','False', text, value, lang, enddialog, null);
}
// --></script>
<input id="PageAddText" disabled="disabled" type="text" Runat="server" />
<input type="button" value="Select page" onclick="OpenGetPageDialog('<%= EPiServer.UriSupport.ResolveUrlFromUIBySettings("edit/pagebrowser.aspx") %>','', '<%= PageUrlValue.ClientID %>', '<%= PageAddText.ClientID %>', 'en');" />
<script type="text/javascript"><!--
OpenGetPageDialog('<%= EPiServer.UriSupport.ResolveUrlFromUIBySettings("edit/pagebrowser.aspx") %>','', '<%= PageUrlValue.ClientID %>', '<%= PageAddText.ClientID %>', 'en');
// --></script>

2. In the WrapServerObjectBrowser.aspx.cs OnInit-method I added these lines

if (!IsPostBack)
{
if (Request.QueryString["type"]!= null)
actionTab.SetSelectedTab(Request.QueryString["type"]);
}

3. In PropertyLongStringControlAdapter.cs, declared a TextBox, checked if the property had any settings choosen and then choosed if to use the textbox or the FCKEditor. When the proeprty was saved I had to do the check again to know wich webcontrol to fetch the value from.

protected WrapFCKEditor editor;
protected TextBox textbox;

public override void ApplyEditChanges()
{
if (((PropertyLongString)this.PropertyData).EditorToolOptions == 0)
this.PropertyData.Value = textbox.Text;
else
this.PropertyData.Value = editor.Value;

}

public override void CreateEditControls()
{
if (((PropertyLongString)this.PropertyData).EditorToolOptions == 0)
{
textbox = new TextBox();
textbox.TextMode = TextBoxMode.MultiLine;
textbox.Rows = 10;
textbox.Columns = 40;
textbox.Text = this.PropertyData.Value as string;
this.Control.Controls.Add(textbox);
}else{
// display the editor...
}
}

Using XForms to store information

At the moment I’m adding some functionallity to vinsprit.se, so that a visitor can add products, that aren’t in the regular assortment, to a cart and by a simple click send this cart by fax to the closest Systembolag. A pretty nice feature. The customer also wanted to store the orderinfo in episerver if something goes wrong, so I thought that you must be able to do that with XForms. And yes, it was very simple.

First of all I added a “XForms form”-property to the pagetype. Then I made an empty Form. The rest is handled from code behind. The following example works both with 4.6 and CMS 5. Hopefully in CMS 5 R2 aswell.. πŸ™‚

// Get guid for the form and creates instance
XForm form = XForm.CreateInstance(new Guid((String)CurrentPage["XFormPropertyName"]));
// This is where data is stored
XFormData form_data = form.CreateFormData();
//We want this info related to the order page
form_data.PageId = CurrentPage.PageLink.ID;
// We want it saved in the database
form_data.ChannelOptions = ChannelOptions.Database;

// Here we add the values.
form_data.SetValue("Key 1", "some value");
form_data.SetValue("Key 2", "some other value");
// and saves it
form_data.Send();

Pretty nice if you want to avoid creating your own sql-table or some other way to store data.

MultiPage Property to EPiServer 5

I know there is a great MultiPage Property on EPiCode, but sometimes I want to use a bit more simple MultiPage picker, and not be able to choose to link documents and other pages etc. So when I had some spare time a few weeks ago, I wrote my own multipage property.

To make the property as powerful as possible I’ve added the oppertunity to specify the startnode when you add a page, and there is also a possibility to choose wich PageTypes you are allowed to use. If you leave the help-text blank you will be able to add all pagetypes and the startnode will be the start page. Defining these values in help-text may not be the best way to go, but it’s the easiest way if you want it as dynamically as possible. I usually change where to define these values in my projects, etc a settings page or web.config, but in this case the help text is wonderful. πŸ™‚

If the editor tries to add an invalid page type, the following msg will appear.

To get the selected pages when you code, just use this line

PageDataCollection pdc = Obg.SpecializedProperties.Util.MultiPage.GetPageDataCollectionByPropertyValue(CurrentPage["MultiPageContacts"]);

You can download the file here: MultiPage Property (binaries)

In this dll, my DropDown Property is also added.

Feel free to give me feedback on these properties or you want the source code etc.

EPiServer 4.6x, Friendly URL and Windows Vista / IIS7

Got so frustrated yesterday that I hade to leave one hour earlier. Must have been very tired cause I couldn’t manage to get Friendly URL running with EPiServer 4.6 on Vista, even though I have managed to do it before.

Well, this morning I solved it in a few minutes.. πŸ™‚
The problem was that in IIS7, you have to specify how the IIS handles error pages, it’s not enough to just change 404 to /Util/NotFound.aspx. By default, IIS gives you detailed information on what went wrong, you have to change this so it uses yours/episerver notfound-page.

Then you have to make sure that you run Classic mode in your App-pool and that your <module> in web.config says <module runAllManagedModulesForAllRequests=”true”>

You can read more at http://world.episerver.com/Forum/Pages/Thread.aspx?id=12984&epslanguage=en.

Good luck

Prepopulate fields when creating page

Made a joblisting about a month ago, the site I made it for is a globalized site with three different languages (so far). Every work ad needs a start- and a stop-publish date, and the work ad is first created in one language, then translated by some other to other languages.

When I checked around the site a week ago i noticed that the joblisting had different sorting depending on what language I had choosen. So I started my research and noticed that the start- and stop publish was only set in the master language, and the other pages had a different start publish and no stop publish… Of course, you need to set publish dates on all languages. This was not an option..

Then I remembered that you can prepopulate data in the edit-fileds, but how? Started thinking again.. global.asax.. events.. hmm.. advanced developing with episerver, that’s right. Found my course material and checked a random page in it, and there it was! So this is how I solved it:

In Global.asax

protected void Application_Start(Object sender, EventArgs e)
{
DataFactory.Instance.LoadedDefaultPageData += new PageEventHandler(Instance_LoadedDefaultPageData);
}

protected void Instance_LoadedDefaultPageData(object sender, PageEventArgs e)
{
if (e.Page.PageTypeID == Utils.Settings.PageTypeJobitem && e.Page != null && e.Page.PageLink != PageReference.EmptyReference)
{
PageDataCollection languages = DataFactory.Instance.GetLanguageBranches(e.Page.PageLink);
if (languages.Count > 0)
{
e.Page.StartPublish = languages[0].StartPublish;
e.Page.StopPublish = languages[0].StopPublish;
}
}
}