How to get current page? Navigation and traversal.

Vote:
 

I've been trying to get the current page outside of the context of an web page or user control but still as part of a http request. I'm quite new to EPiServer and am building a webforms site at the moment and have been tackling the navigation, for which none of the inbuilt controls seem to be particularly useful to me.

 

Anyway I've created some classes to help wrap what I want but wondered how best to get the current page. The first 2 things I looked at were:

PageReference.SelfReference

DataFactory.Instance.CurrentPage

Neither of these seem to return the current PageReference or PageData.

What I have currently is:

public class Navigation
{
    private PageReference _current = null;
    private PageReference _root = null;
    private INavigationFilter _filter = null;
    private PageData _currentPage = null;
    private NavigationData _item = null;
 
    public PageData CurrentPage
    {
        get
        {
            if (_currentPage == null && _current != null)
                _currentPage = DataFactory.Instance.GetPage(_current);
            return _currentPage;
        }
    }
 
    public NavigationData Item
    {
        get
        {
            if (_item == null)
                _item = new NavigationData(DataFactory.Instance.GetPage(_root), this);
            return _item;
        }
    }
 
    public INavigationFilter Filter
    {
        get { return _filter; }
        set
        {
            _filter = value;
            _item = null;
        }
    }
 
    public Navigation()
        : this(PageReference.StartPage, null)
    { }
 
    public Navigation(PageReference root, PageReference current)
    {
        this._current = current;
        this._root = root;
    }
 
}

public class NavigationData
 {
     private Navigation _navigation = null;
     private List<NavigationData> _children = null;
 
     public PageData Page { getprivate set; }
     public int Level { getprivate set; }
     public NavigationData Parent { getprivate set; }
     public bool Selected { getprivate set; }
 
     public List<NavigationData> Children
     {
         get
         {
             if (_children == null)
                 _children = GetChildren();
             return _children;
         }
     }
 
     public NavigationData(PageData page, Navigation navigation)
         : this(page, navigation, 0null)
     { }
 
     private NavigationData(PageData page, Navigation navigation, int level, NavigationData parent)
     {
         this._navigation = navigation;
 
         this.Page = page;
         this.Level = level;
         this.Parent = parent;
 
         if (navigation.CurrentPage != null)
             this.Selected = navigation.CurrentPage.PageLink == page.ParentLink;
     }
 
     private List<NavigationData> GetChildren()
     {
         List<NavigationData> list = new List<NavigationData>();
 
         var children = DataFactory.Instance
             .GetChildren(this.Page.PageLink)
             .Where(c => !this._navigation.Filter.ShouldFilter(c, this));
 
         if (children.Any())
         {
             foreach (var child in children)
             {
                 list.Add(new NavigationData(child, this._navigation, this.Level + 1this));
             }
         }
         return list;
     }
 }

public class DefaultNavigationFilter : INavigationFilter
{
    private IContentFilter _filter = new FilterContentForVisitor();
 
    public DefaultNavigationFilter()
    { }
 
    public virtual bool ShouldFilter(IContent content, NavigationData parent)
    {
        if (!(content is PageData))
            return true;
 
        PageData pageData = content as PageData;
 
        return _filter.ShouldFilter(pageData) || !pageData.VisibleInMenu;
    }
}
 

With the idea I can then create my own filters like so:

public class PrimaryNavigationFilter : DefaultNavigationFilter
{
    public override bool ShouldFilter(IContent content, NavigationData parent)
    {
        if (base.ShouldFilter(content, parent))
            return true;
 
        if (parent.Level >= 3)
            return true;
 
        if (parent.Level == 0 && !(content is SectionPage))
            return true;
 
        return false;
    }
}

And render the menu using the following:

public static class Html
{
    public static IHtmlString PrimaryNavigation()
    {
        Navigation navigation = new Navigation();
        navigation.Filter = new PrimaryNavigationFilter();
 
        StringBuilder html = new StringBuilder();
        TraverseNavigation(html, navigation.Item);
        return new HtmlString(html.ToString());
    }
 
    private static void TraverseNavigation(StringBuilder html, NavigationData data)
    {
        if (data.Children.Any())
        {
            html.Append("<ol>");
            foreach (var child in data.Children)
            {
                html.AppendFormat("<li{2}><a href=\"{0}\">{1}</a>", 
                    child.Page.LinkURL, 
                    child.Page.Name, 
                    child.Selected ? " class=\"selected\"" : "");
                TraverseNavigation(html, child);
                html.Append("</li>");
            }
            html.Append("</ol>");
        }
    }
}

However I'm stumbling over checking whether the PageReference or PageData is the current page, or at least the PageReference for the current page passed into my Navigation object.

I appreciate the rendering is a bit MVCish in a WebForms world but the course I was on only covered WebForms but WebControls are more of a PITA than they're worth. Anyway the only real problem with it is my current uncertainty over how best to get the current page.

I had hoped something like the following might have worked:

public Navigation()
    : this(PageReference.StartPage, DataFactory.Instance.CurrentPage.ParentLink)
{ }
 
#85911
May 08, 2014 13:15
Vote:
 
Normally you inherit from TemplatePage or UserControlBase which gives you access to CurrentPage.

I have never encountered any situation in webforms where you would like to render a menu that is aware of the current page outside a page or user control.

So, it's probably possible to do this, but it's not common practice, so maybe you should rethink the solution?
#85917
May 08, 2014 15:29
Vote:
 

Hi Mark,

I would simplify the navigation logic a bit.

 

public class NavigationProvider
{
    public List<NavigationData> GetNavigationData(ContentReference currentPage, INavigationFilter filter)
	{
	    ....
	}
}

    

public static class Html
{
    public static IHtmlString PrimaryNavigation(ContentReference currentPage)
	{
	    var navigationProvider = ServiceLocator.Current.GetInstance<NavigationProvider>();
		var filter = new PrimaryNavigationFilter();
		var data = navigationProvider.GetNavigationData(currentPage, filter);
		...
	}
}

    

And as Fredrik mentioned, if you inherit the view from TemplatePage or UserControlBase, you'll have access to CurrentPage

#85922
May 08, 2014 16:03
Vote:
 

Actually just stumbled across a way of doing it in the Alloy demo, they use it for the master page which doesn't seem to get any specific EPiServer base class or context.

As such something like this actually works:

public static class Site
{
    public static PageData CurrentPage
    {
        get
        {
            if (HttpContext.Current == null)
                throw new NotSupportedException("Cannot get current page without HttpContext");
 
            var pageBase = HttpContext.Current.Handler as PageBase;
            if (pageBase == null)
                throw new NotSupportedException("Cannot get current PageBase object");
 
            var currentPage = pageBase.CurrentPage;
            if (currentPage == null)
                throw new NotSupportedException("Current page is not set.");
 
            return currentPage;
        }
    }
}

That said I don't know if it is the best way. 

 
#85928
May 08, 2014 17:51
Vote:
 

One option is to get an instanse of EPiServer.Web.Routing.ContentRouteHelper (or the similar class PageRouteHelper if you know the routed item in the context is always a PageData instance) from the IOC container.

That will give you the content/page that the current request was routed to.

#85931
May 08, 2014 18:45
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.