SiteMenu Builder

February 13, 2010 Digital Experience

This post will show you how to create custom navigation menu for your website, where you can specify which pages you want to see and which not. There are many cases where you want to have two level navigation or several navigational  menus. Each time you will have to do some coding over the menu so that it will start showing the appropriate items/nodes.

In this post I am creating a custom class that inherits from WebUITypeEditor.

First I am going to build WebUITypeEditor using RadTreeView control which supports multiple selection. This seems to be the most appropriate control to achieve my goal. I will bind the control to a list containing all pages by using the CmsManager.GetPages() method. Then I will return the ID of each node to my property as a Guid array.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Telerik.Cms.Web.UI;
using Telerik.Web.UI;
using Telerik.Framework.Web;
using Telerik.Cms;
using System.Collections;
using System.Web.UI;
using Telerik.Cms.Web;
 
/// <summary>
/// Summary description for DynamicMenuBuilderWebUITypeEditor
/// </summary>
public class DynamicMenuBuilderWebUITypeEditor : WebUITypeEditor<Guid[]>
{
    public DynamicMenuBuilderWebUITypeEditor()
    {
 
    }
 
    public override Guid[] Value
    {
        get
        {
            Guid pageId = Guid.Empty;
            List<Guid> selectedIds = new List<Guid>();
            foreach (RadTreeNode node in TreeView.SelectedNodes)
            {
                if (node != null)
                {
                    CmsSiteMapNode smNode = (CmsSiteMapNode)SiteMap.Provider.FindSiteMapNode("~/" + node.FullPath + ".aspx");
                    ICmsPage cmsPage = manager.GetPage(smNode.CmsPage.ID) as ICmsPage;
                    pageId = cmsPage.ID;
                    selectedIds.Add(pageId);
                }
            }
            return selectedIds.ToArray();
        }
        set
        {
            this.ViewState["selectedPage"] = value;
            base.ChildControlsCreated = false;
        }
    }
 
    public string Templte
    {
        get
        {
            object o = this.ViewState["Template"];
            if (o == null)
                return ("~/CustomControls/PageIDWebEditor.ascx");
            return (string)o;
        }
        set
        {
            this.ViewState["Template"] = value;
        }
    }
    
 
    protected override void CreateChildControls()
    {
        base.CreateChildControls();
        this.manager = new CmsManager();
        this.template = ControlUtils.GetTemplate<SelectorTemplate>(this.Templte);
        this.template.InstantiateIn(this);
        var manager = new CmsManager();
        IList allPages = manager.GetPages();
        TreeView.MultipleSelect = true;
        TreeView.DataSource = allPages;
        TreeView.DataTextField = "MenuName";
        TreeView.DataFieldParentID = "ParentID";
        TreeView.DataFieldID = "ID";
        TreeView.DataNavigateUrlField = "StaticUrl";
        TreeView.DataBind();
    }
 
    protected virtual RadTreeView TreeView
    {
        get
        {
            return this.Controls[0].FindControl("RadTreeView1") as RadTreeView;
        }
    }
 
    public class SelectorTemplate : ITemplate
    {
        public void InstantiateIn(Control container)
        {
        }
    }
 
    private ITemplate template;
    private CmsManager manager;
 
}

My Selector template is shown below. I declared there a RadTreeView instance:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="PageIDWebEditor.ascx.cs" Inherits="CustomControls_PageIDWebEditor" %>
 
 
<asp:Label runat="server" ID="Label1" Text="Select a page from the tree" />
<telerik:RadTreeView ID="RadTreeView1" runat="server" Skin="Black"/>

In the code behind I have to disable the NavigateUrl as otherwise when a node is selected a postback will appear, which will clear the selected nodes collection.

public partial class CustomControls_PageIDWebEditor : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        RadTreeView1.NodeDataBound += new Telerik.Web.UI.RadTreeViewEventHandler(RadTeerView1_NodeDataBound);
    }
 
    void RadTeerView1_NodeDataBound(object sender, Telerik.Web.UI.RadTreeNodeEventArgs e)
    {
        foreach (RadTreeNode node in RadTreeView1.GetAllNodes())
        {
            node.NavigateUrl = "";
        }
    }
}

My selector is ready and now I need a custom control. The custom control will inherit from the well know CompositeControl class. This custom control does not need a template, because the SiteMenu will be dynamically generated.

I need a ViewState property from where I will call the WebUITypeEditor. I will use the built-in GuidArrayConverter, so that I will not have to create a custom DictionaryConverter for my property that is not just a simple string

[WebEditor("DynamicMenuBuilderWebUITypeEditor, App_Code")]
    [TypeConverter("Telerik.Framework.Utilities.GuidArrayConverter, Telerik.Framework")]
    public Guid[] PageIDs
    {
        get
        {
            object obj = this.ViewState["PageIDs"];
            if (obj != null)
                return (Guid[])obj;
            return new Guid[0];
             
        }
        set
        {
            this.ViewState["PageIDs"] = value;
        }
    }

I will override CreateChildControls() where I will dynamically create a RadMenu control and List of all selected pages. Then, I will bind the menu to this list similar to the way that is used in the custom selector to populate the RadTreeView control.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
using Telerik.Cms.Web.UI;
using System.ComponentModel;
using Telerik.Web.UI;
using Telerik.Cms;
 
/// <summary>
/// Summary description for DynamicMenuBuilder
/// </summary>
public class DynamicMenuBuilder : CompositeControl
{
    public DynamicMenuBuilder()
    {
 
    }
 
    protected override void CreateChildControls()
    {
        base.CreateChildControls();
        RadMenu menu = new RadMenu();
        var Manager = new CmsManager();
        List<ICmsPage> selectedPages = new List<ICmsPage>();
 
        foreach (Guid g in PageIDs)
        {
            ICmsPage p = Manager.GetPage(g, false) as ICmsPage;
            if (p != null)
            {
                selectedPages.Add(p);
            }
        }
        menu.DataSource = selectedPages;
        menu.DataTextField = "MenuName";
        menu.DataFieldParentID = "ParentID";
        menu.DataFieldID = "ID";
        menu.DataNavigateUrlField = "StaticUrl";
        menu.DataBind();
        Controls.Add(menu);
    }
 
 
    [WebEditor("DynamicMenuBuilderWebUITypeEditor, App_Code")]
    [TypeConverter("Telerik.Framework.Utilities.GuidArrayConverter, Telerik.Framework")]
    public Guid[] PageIDs
    {
        get
        {
            object obj = this.ViewState["PageIDs"];
            if (obj != null)
                return (Guid[])obj;
            return new Guid[0];
             
        }
        set
        {
            this.ViewState["PageIDs"] = value;
        }
    }
}

The Progress Team