Until now we had two ways of selecting navigation nodes to show:
- First Level - show all direct children of the navgation root
- All levels - show all children of the navigation root
Both ways are not optimal in most cases. The first one is very restrictive whereas the second one shows too much if your navigation tree is getting a bit bigger.
The trues lies in the middle. One example would be - if one node is clicked, expand it direct children. A further version of it - expand its children and at the same time collapse the nodes which are not in the active path anymore. You can invent many more ways to select a visible part of the navigation tree depending on the current context. To encapsulate the behaviour for choosing a subset of visible navigation nodes AxCMS.net offers a concept of a Navigation Builder. Navigation Builder follows the Strategy Design Pattern. The base class NavigationBuilderBase (in Axinom.AECMS.Templates) defines an interface for algorithms which build a a visible subtree of the navigation tree. Main part of it is:
public virtual AxCategoryCollection Build(int parentGroupID)
ParentGroupID is the ID of the root navigation node for your navigation group. The purpose of the Build-method is to filter the navigation tree and return back the nodes which should be rendered for the user in the correct order. Of course every NavigationBuilder needs to know the navigation context, that is why AxNavigationContext is passed to it as a parameter in constructor.
To implement your own NavigationBuilder inherit from NavigationBuilderBase and override the Build-method.
The base-version traverses the whole navigation tree (in the tree order) and picks the nodes for which IsRelevant(node, parentGroupID) returns true. IsRelevant is defined as:
protected virtual bool IsRelevant(AxCategory category, int parentGroupID)
It means, if you instantiate NavigationBuilderBase directly, you get nothing back. But you can leave Build() intact and override IsRelevant with your decision rule.
There are two sample implementations of the NavigationBuilders in AxCMS.net (both in Axinom.AECMS.Templates.Navigation):
BasicNavigationBuilder overrides IsRelevant as described above. It always returns the first level navgiation as well as all the nodes whoes parents are in the active path. It means, if you click somehwere, the tree expands one level where you clicked and collapses elsewhere.
FirstLevelNavigationBuilder implements the behaviour we started with - show only the first level navigation (it also does not show the nodes which have no assigened elements).
Let's adapt our code (from the example in the Getting Started / Navigation) to use BasicNavigationBuilder. We only have to change one line:
navigationRepeater.DataSource = (new BasicNavigationBuilder(((TemplateBase)this.Page).NavigationContext)).Build(rootNavigationNode.AxID);
Why should you use NavigationBuilder pattern?
Well, it is up to you if you use it. If your logic for filtering the navigation tree is simple, you can take one of the predefined NavigationBuilders or implement the filtering directly into the navigation control. If the logic gets more complex, it is neater to encapsulate this logic in its own class with a clear Build()-interface. It is especially useful if you have multiple navigation controls sharing the same algorithm or if you share the same algorithm between multiple projects. In the later case you can extract the custom NavigationBuilder into another DLL and reference in all the projects which need it.
If you have a complex navigation behaviour (e.g. if you have multiple navigation controls which depend on each other) you might want to customize the NavigationContext. To do this create a new class (say, MyNavigationContext), inherit from AxNavigationContext and override InitContext. In your template code override CreateContext (a factory method) to return your NavigationContext instance:
protected override AxNavigationContext CreateContext()
return new MyNavigationContext();
If you have multiple templates in your project, you could create a common base class for all them and move the manipulations with the NavigationContext there.