Relative Content Path in MVC3 Areas
Asked Answered
I

5

15

I found this question Can't use relative paths with areas in ASP.NET MVC 2 which is the same issue I am having. Is this still the case in MVC3?

Is there a way to keep content files in an area relative to the area?

So that a layout file in an area can have something like

Without having to either make a fully qualified link, requiring the areas directory and the area name or the solution of the above question which requires a check for each area on each request.

update/edit

I've decided to use both the solution in the above question and the one below (html helper) - depending on the project/situation. My implementation of the above uses app.setting to store the area names and the extensions so that I can just have the module as part of my library.

var context = HttpContext.Current;
var path = context.Request.Path;
var list = ...       //code that gets from app.config and then saves it
var extensions = ... // to the cache as non-removable with a dependency on web.config
foreach (var area in list)
{
   if (!path.Contains(area + "/")) continue;
   foreach (var extension in extensions)
   {
      if (path.EndsWith("." + extension))
      {
         context.RewritePath(path.Replace(area + "/", "Areas/" + area + "/"));
      }

    }
 }
Isolationist answered 26/2, 2011 at 22:47 Comment(0)
L
4

You can try creating an extention on HtmlHelper to work this out:

public static string Image(this HtmlHelper html, string imagePath)
{
    var area = html.ViewContext.RouteData.Values["area"];
    if (!string.IsNullOrEmpty(area))
        area = "Areas/" + area + "/";
    return VirtualPathUtility.ToAbsolute("~/" + area + "Content/img/" + imagePath);
}

so in your view you can use:

<img src="@Html.Image("myimage.png")" alt="..." />

I didn't try the code so correct me if I'm wrong.

Lebrun answered 28/2, 2011 at 13:51 Comment(2)
I like this solution. I wonder what's faster, though: a check for the path in the module or a dictionary lookup and then the check. Even still, this can be optimized with a variety of tricks.Isolationist
An additional benefit is that the solution provided in the question above does not allow for reference to assets out of the area, since it will always try to rewite to the area which may not be desired.Isolationist
E
18

I personally like the extension method route, based on the first answer I came up with this and tested that it works ... Instead of using @Url.Content, use @Url.ContentArea and no need to put in '~/', '/' or '../', etc..

The helper does some checking to automatically remove these, so just use is like this ...

@Url.ContentArea("Content/style.css") or @Url.ContentArea("Images/someimage.png") :)

When you create this Url Helper Extension, your choice, but I created a 'Helpers' folder off the root of the web then I include the @using YourWebNameSpace.Helpers; in my _Layout.cshtml (razor/masterpage) in whatever 'Area' I am in.

You can still use @Url.Content for references outside the current area (basically you can mix based on the resource needed and its location).

namespace YourWebNamespace.Helpers
{
    public static class UrlHelperExtensions
    {
        public static string ContentArea(this UrlHelper url, string path)
        {
            var area = url.RequestContext.RouteData.DataTokens["area"];

            if (area != null)
            {
                if (!string.IsNullOrEmpty(area.ToString()))
                    area = "Areas/" + area;

                // Simple checks for '~/' and '/' at the
                // beginning of the path.
                if (path.StartsWith("~/"))
                    path = path.Remove(0, 2);

                if (path.StartsWith("/"))
                    path = path.Remove(0, 1);

                path = path.Replace("../", string.Empty);

                return VirtualPathUtility.ToAbsolute("~/" + area + "/" + path);
            }

            return string.Empty;
        }
    }
}

in your master page, or any page (Razor only for this example) ...

@using YourWebNamespace.Helpers

<html lang="en">
<head>

    <link href="@Url.ContentArea("Content/reset.css")" media="screen" rel="stylesheet" type="text/css" />

    <link href="@Url.ContentArea("Content/style.css")" media="screen" rel="stylesheet" type="text/css" />

</head>
<body>
Extremely answered 19/4, 2011 at 15:53 Comment(0)
L
4

You can try creating an extention on HtmlHelper to work this out:

public static string Image(this HtmlHelper html, string imagePath)
{
    var area = html.ViewContext.RouteData.Values["area"];
    if (!string.IsNullOrEmpty(area))
        area = "Areas/" + area + "/";
    return VirtualPathUtility.ToAbsolute("~/" + area + "Content/img/" + imagePath);
}

so in your view you can use:

<img src="@Html.Image("myimage.png")" alt="..." />

I didn't try the code so correct me if I'm wrong.

Lebrun answered 28/2, 2011 at 13:51 Comment(2)
I like this solution. I wonder what's faster, though: a check for the path in the module or a dictionary lookup and then the check. Even still, this can be optimized with a variety of tricks.Isolationist
An additional benefit is that the solution provided in the question above does not allow for reference to assets out of the area, since it will always try to rewite to the area which may not be desired.Isolationist
M
1

I tried the UrlHelperExtensions above for some images which I use in my masterpage as data for a jQuery scrolling banner. I had to add the following else clause so that the helper would return tha path when there was no area:

        else
        {
            if (path.StartsWith(@"~/"))
            {
                var result = VirtualPathUtility.ToAbsolute(path);
                return result;
            }
        }

        return string.Empty;

In my view page I used:

<img src="@Url.ContentArea("~/images/ScrollPicture1.png")" alt="Gallery picture 1" />

Thanks for posting this solution. Works great with my fix for images.

Mcpeak answered 27/9, 2011 at 21:40 Comment(0)
I
1

I just had a similar problem in a style sheet that I solved by just putting in both relative paths:

#searchbox {
    ...
    background: url("../Content/magglass.png") no-repeat;
    background: url("../../Content/magglass.png") no-repeat;
    ...
}
Iridis answered 22/2, 2012 at 23:10 Comment(0)
C
-1

I had similar problems with my ASP.NET MVC 3 web application and I fixed it writing instead of

<img src="mypath/myimage.png" alt="my alt text" />

this:

<img src='@Url.Content("mylink")' alt="my alt text" />

where the last mylink is a path like ~/mypath. I used it to link my javascript files, however I didn't try it with images or links so you should try yourself...

Canada answered 27/2, 2011 at 10:27 Comment(1)
it doesn't work within the Areas. It points back to the root and ignores the fact that it was initiated within an area.Isolationist

© 2022 - 2024 — McMap. All rights reserved.