At Engage, we've really enjoyed using DDR menu for our skins. It gives us a lot more control than we had before with the DNN menu (or SolPart before it). One of the great features that we enjoyed was the ability to define a menu template using the Razor template language. This gives us full access to C# code within the menu template, making it simple to do whatever we want the menu to do. However, we ran into a snag when we started trying to update our sites to DNN 7.
DNN 7 updated the minimum requirements of the platform to require .NET 4, which allowed updating the System.WebPages dependency, which holds the Razor implementation. There were some minor changes to the Razor syntax, so that what had worked before with a hack now worked like you'd expect (but the hack now caused an error). That is, Razor is now much smarter about markup, and assumes that tags have reasonable elements and attributes. Whereas previously I would create a string like " id='level-@level'"
and then append it to an element like <ul@theId>
, Razor will now let us put the attribute right on the element, and will hide it if it the value is null: <ul id="@(level == 0 ? Model.ControlID : null)">
. Likewise, Razor will collapse whitespace within an attribute, so class="@first @last @selected @bc"
won't even render the class
attribute if all of those variables are null
.
There was another issue with DDR's implementation of Razor in DNN 7, though. Even if you fixed those syntax errors, you'd be greeted with an error about Model
not being defined. The issue stems from DDR Menu's Razor implementation using System.WebPages directly. When the System.WebPages dependency was upgraded to version 2, this broke DDR's integration. So, instead, Erik VB update DDR Menu to use DNN's built-in Razor integration, which will shield DDR Menu from breaking changes like that in the future. However, because of this change, templates which worked before need another declaration added to the top of the template file. The canonical Razor DDR template starts with the following:
@using DotNetNuke.Web.DDRMenu;
To this, we'll need to add:
@using System.Dynamic;
@inherits DotNetNuke.Web.Razor.DotNetNukeWebPage<dynamic>
If your template is based on this canonical template, you'll also need to fix the syntax error mentioned above. Originally, the template did the following to apply CSS classes to the individual page list items:
var cssClasses = new List<string>();
if (node.First) { cssClasses.Add("first"); }
if (node.Last) { cssClasses.Add("last"); }
if (node.Selected) { cssClasses.Add("selected"); }
var classString = new HtmlString((cssClasses.Count == 0) ? "" : (" class=\"" + String.Join(" ", cssClasses.ToArray()) + "\""));
<li@classString>
@** Put menu item link here **@
</li>
For the new Razor syntax, replace that with this:
var first = node.First ? "first" : null;
var last = node.Last ? "last" : null;
var selected = node.Selected ? "selected" : null;
var bc = node.Breadcrumb ? "bc" : null;
<li class="@first @last @selected @bc">
@** Still put menu item link here **@
</li>
Now, all of this perhaps seems like a lot of work, but you've got to remember, your template is running code on the website. If you're clever, you can do some crazy things in here (e.g. adding classes based on taxonomy, or anything else you can think of). The sky is the limit (just make sure you're considering performance before you go too crazy).