SiteMap External Link “not a valid virtual path”

So, I was asked to add a link to an external Web Site from the Main Menu of our app and to display it in a new browser window.

The Main Menu is composed of SiteMapNodeInfo nodes that are consumed by a custom SiteMapProvider derived from StaticSiteMapProvider and eventually fed to a TreeView for rendering.

Using the principles of Orangutan-Driven Development I sledgehammered the code with a blunt instrument to see what would fall out the tree. Thus:


SiteMapNodeInfo cityScopeNode = new SiteMapNodeInfo("Links", "http://CityScope.com", "Links");
siteMapBuilderService.AddNode(cityScopeLinkNode, Globals.RuleNames.ExternalLinks, 3);

..and was duly greeted with the System.Web.HttpException and message “http://CityScope.com not a valid virtual path” (no bananas).

The bulk of my problem was solved by Joe Audette who showed me that the URL in the SiteMapInfo node must be a path that resides within the application, the validation for this being the .NET Framework call:


System.Web.StaticSiteMapProvider.AddNode(SiteMapNode node, SiteMapNode parentNode)

Deep within my Reptilian Brain Stem, inchoate impulses began to coagulate: SiteMap…Site…yeeessss…in-TER-naaaaaallll. Fair enough, since its a SiteMap then external Links are probably contra-indicated, but it IS nevetheless possible to include an external link when using a SiteMapProvider, by kind of sneaking around it.

Joe pretty much gave me the solution by referring to the MenuItemDataBound event of his Menu Control and explaining that “AFTER the SiteMapNode is in the nodes collection you can get away with changing the Url to an external link.”

I didn’t have a Menu control, but I did have a TreeView – ergo:

Step 1: Initialize SiteMapNode to something that passes the AddNode validation – an empty string will do.

Step 2: Handle the TreeView’s TreeNodeDataBound event to set the Node’s URL to an external link.

Step 3: Set the SiteMapNode’s ‘Target’ property to “_blank” if you want the external Link displayed in a new browser window.

From our Web Client Software Factory ModuleInitializer :


protected virtual void RegisterSiteMapInformation(ISiteMapBuilderService siteMapBuilderService)
{
SiteMapNodeInfo LinksRootNode =
new SiteMapNodeInfo(LinkNames.Links.Name, LinkNames.Links.URL, LinkNames.Links.Name, "");
// Put string.Empty in place of external Link to avoid HttpException SiteMapNodeInfo CityScopeNode =
new SiteMapNodeInfo(LinkNames.CityScope.Name, string.Empty, LinkNames.CityScope.Name);
siteMapBuilderService.AddNode(LinksRootNode, 4);
siteMapBuilderService.AddNode(CityScopeNode, LinksRootNode);
}

In our MasterPage where the TreeView is rendered:

// HandleTreeNodeDataBound
protected void TreeView_TreeNodeDataBound(object sender, TreeNodeEventArgs e)
{
// Initialise the CityScope Link (which is external)
// and display it in a new browser window when selected

if (e.Node.Text == LinkNames.CityScope.Name)
{
e.Node.NavigateUrl = "http://CityScope.com.au";
e.Node.Target = "_blank";
}
}

Life Energy Consumed: 41 mins.

Acknowledgments

Thanks to Scott Berkun for the inspiration for ODD

Advertisements

Tags: , ,

6 Responses to “SiteMap External Link “not a valid virtual path””

  1. Using SiteMap for external links and additional attributes « Left Lobe Logic Says:

    […] to a few external websites. The WCSF uses a custom SiteMapProvider that is part of its framework. A coworker highlighted how he solved the problem by simply overriding the URL as part of the TreeView (our […]

  2. Sheir Says:

    How are you defining your LinkNames class with the links and where do you put that class?
    In the Shell module??

  3. baraholka1 Says:

    Hiya Sheir,

    LinkNames is just a static class that we put in the “Common” dll which is used by all the projects in the application:

    public static class LinkNames
    {
    public static class CityScope
    {
    public const string Name = “City Scope”;
    // City Scope Link is an external link
    public static string URL
    {
    get
    {
    return String.Format(“~/Redirect.aspx?url={0}”, HttpUtility.UrlEncode(ConfigurationManager.AppSettings[“CityScopeLink”]));
    }
    }
    }

    Regards,

    Baraholka

  4. Chris Mulvey Says:

    I used this method to inject a javascript call from the sitemap to show an Ajax modelPopupextender from the client-side:

    First, I added some js to a user control on my page (contains the model popup):
    function showAboutHelp() {
    var myBehavior = $find(“clientBehavior”);
    myBehavior.show();

    Then, I added a dummy page…
    at: ~/Navigation
    called: CallJavaScriptFromSiteMap.aspx

    In my ModeuleInit…:
    moduleNode = New SiteMapNodeInfo(“About”, “~/Navigation/CallJavaScriptFromSiteMap.aspx?f=showAboutHelp”, “About”, “About Help”)

    In my menu_DataBound code-behind method:
    If (e.Item.Text.Contains(“CallJavaScriptFromSiteMap”)) Then
    Dim jsFunctionIndex As Integer = e.Item.Text.IndexOf(“:”) + 1
    Dim jsFunction As String = e.Item.Text.Substring(jsFunctionIndex)
    e.Item.NavigateUrl = “JavaScript:” + “();”
    End If

    Very useful…

  5. tgaw Says:

    Thanks for this post. All I needed was your humourous line “SiteMap…Site…yeeessss…in-TER-naaaaaallll.” to point me in the right direction! Thanks for taking the time to type this up.

    • baraholka1 Says:

      Hi Tgaw,

      Humorous ? Aw shucks ….
      Try this:

      Knock! Knock!
      Who’s There ?
      Abby.
      Abby who ?
      Abby Birthday to you.

      Nyuk Nyuk

      – Barra

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: