views:

31

answers:

1

I'd like to have an area called "Products", where I can use routes such as

http://localhost/products/foo

http://localhost/products/bar

I would like to have the views and other assets organized into a folder structure like

/areas/products/views/foo/index.aspx
/areas/products/views/bar/index.aspx

I'd like to keep images, etc specifically related to each product (foo, bar) in their respective /area/products/views/(foo|bar)/ folder.

I also do not want to have to add a controller action for each product.

If I declare a route like

context.MapRoute(
    "products-show-product"
    , "Products/{id}"
    , new { controller = "Products", action = "Index", id=UrlParameter.Optional }
    );

and request the url

http://localhost/products/foo

then ProductsController.Index() is called, as I would expect. However, since the view "foo" is not in the views/products or views/shared folder, it isn't being found.

How can I do this so that I can keep each product's pages in a separate folder?

+1  A: 

I don't have a concrete answer to your question since I am not sure about my understanding of it. However I have a general feeling for the direction for the solution.

When one starts to change locations of views, the corresponding methods that find those views also need to change. A simple approach would be to override the FindView and FindPartialView methods.

A simple demo. I created an Area called Blog, a Blog controller with an Index method. In my case I user the controller action as the SubFolder but I am sure that this can be extended to your case for each product folder. I assume that the product will be a request argument. Area

The basic idea is to interrogate the controllercontext for the controller, area, action and id and modify the what the default viewengine looks for. The default locations for area views looks like "~/Areas/{2}/Views/{1}/{0}.aspx", so we can basically inject values for the view name and in this case ActionName/Index. The view location will end up being ~/Area/Blog/Views/Blog/Index/Index.aspx.

This is just a rough outline, of the code that can be used. The string comparisons can definitely be updated to more robust methods. As it stands this method will work for the entire app as expected, except for the case when a request is made to the Blog area for the Index action.

public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        if (controllerContext.RouteData.DataTokens ["Area"] == "Blog" )
        {
            if (String.Compare(controllerContext.RouteData.Values ["Action"].ToString(),"Index",true) == 0)
            {
                var viewLocation = String.Format("{0}/{1}", controllerContext.RouteData.Values["Action"].ToString(), viewName);
                return base.FindView(controllerContext, viewLocation , masterName, useCache);
            }
        }
            return base.FindView(controllerContext, viewName, masterName, useCache);
    }
Ahmad
Thanks for the detailed answer. In this case, I only needed this for one controller, so I wound up just adding a route that sent everything to the Index action, which builds the view path from the route parameters.
David Lively
glad that you managed to solve your problem...
Ahmad