ASP.NET MVC: Multiple View Folders and _ViewStart.cshtml file - asp.net-mvc

I have an MVC project that requires there to be 2 different View folders. One is at ~/Views/ and one at ~/Framework/Views/. This is done by creating a custom view engine based on the razor view engine like this:
public class MyViewEngine : RazorViewEngine
{
private static string[] AdditionalViewLocations = new[]{
"~/Framework/Views/{1}/{0}.cshtml",
"~/Framework/Views/{1}/{0}.vbhtml",
"~/Framework/Views/Shared/{0}.cshtml",
"~/Framework/Views/Shared/{0}.vbhtml"
};
public MyViewEngine()
{
base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(AdditionalViewLocations).ToArray();
base.ViewLocationFormats = base.ViewLocationFormats.Union(AdditionalViewLocations).ToArray();
base.MasterLocationFormats = base.MasterLocationFormats.Union(AdditionalViewLocations).ToArray();
}
}
The problem is that I want to use a different _ViewStart.cshtml file in each of the 2 Views folder (i.e. ~/Views/_ViewStart.cshtml for views found in the ~/Views/ folder and ~/Framework/Views/_ViewStart.cshtml for views found in the ~/Framework/Views/ Folder), however the View Engine just uses the first one it finds which is the original one in ~/Views/.
Is this possible to do?
Thank you

This is definitely possible, I think you just missed something.
I have tested this myself using the view engine you supplied (copied and pasted verbatim). I am not seeing the same behavior as you. I have two _ViewStart.cshtml files, one at ~/Framework/Views/_ViewStart.cshtml, and one at ~/Views/_ViewStart.cshtml.
When I run a view within ~/Framework/Views/, it uses the _ViewStart.cshtml in the Framework folder. When I run a view within ~/Views/, it uses the _ViewStart.cshtml in the Views folder.
Double checking the code in RazorViewEngine using DotPeek also confirms that this is exactly how it should behave. The view engine starts checking in for a file named _ViewStart.cshtml within the same folder as the view being rendered, and then walks up the directory tree until it gets to the root of the application.

The selection of _ViewStart is hierarchical, but you've added ~/Framework/Views parallel to ~/Views. I don't think Razor is set up to actually do what you want (i.e. two completely parallel view locations). If you were to put Framework into the main Views folder, your _ViewStarts would load properly, though.

Related

.net MVC proper way to link to views within an area

I have a .Net MVC app with a couple areas, one of which is called Admin. When I execute /Admin/Home/Index, the Index() method in the Admin area Home controller executes and the correct Index.cshtml is returned. So far, so good.
My issue is related to _Layout.cshtml. I want the layout to be different for each area and for the main site. To that end I have added _ViewStart.cshtml to my Admin area's Views folder, and within that _ViewStart I have the following:
#{
Layout = "/Areas/Admin/Views/Shared/_Layout.cshtml";
}
Again, so far, so good, but using a fully qualified path seems inelegant and I have to think there is a better way.
A similar question was asked here:
Area doesn't use the right view
The one answer that was given says that the fully qualified path is the way to go, though it was not marked as the answer by the OP.
So my question is this. Is it necessary to use a fully qualified path to reference views within an area? Is it not possible to do this in an area:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Based on the controller that is executing, is the view engine not capable of resolving that I want the view from the area's shared folder and not the view from the main site's shared folder?
Thanks,
Chris
Your Views inside of the Areas will automatically look to the Views folder in that area for a _ViewStart.cshtml or a _ViewImports.cshtml file. My advice in your case would be to put a _ViewStart.cshtml file into your Area's View folders (Not in their Shared folders!) and have it reference the _Layout file of your choice such as:
_ViewStart.cshtml
#{
Layout = "_Layout";
}

Finding Razor partial views in the area EditorTemplates folder

I am starting to use EditorFor helper method to render my razor partial views, but I couldn't get the partials in the Areas folder to work.
Here is the path to the partial:
~\Areas\Products\Views\Shared\EditorTemplates\_Edit.cshtml
The partial is really simple with only one "div" tag to do the testing.
Try to use in my page view (~\Areas\Products\Views\EditPage.cshtml) as
#Html.EditorFor(m => m.ProductEditModel, "_Edit")
Visual studio tells me that "Cannot resolve template '_Edit'".
Now, if I move the partial to the root view folder:
~\Views\Shared\EditorTemplates\_Edit.cshtml
It works, Visual studio has no problems to resolve the template, and the div is renderred correctly in my browser.
I also tried to customize the RazorViewEngine, did not work either
namespace MySite.Web
{
public class RazorViewEngine : System.Web.Mvc.RazorViewEngine
{
public RazorViewEngine()
: this(null)
{
}
public RazorViewEngine(IViewPageActivator viewPageActivator)
: base(viewPageActivator)
{
AreaPartialViewLocationFormats = new[]
{
"~/Areas/{2}/Views/Shared/EditorTemplates/{0}.cshtml"
}.Union(AreaPartialViewLocationFormats).ToArray();
}
}
}
Just wondering what did I do wrong? BTW, I am using MVC3 at the moment, can't upgrate to MVC4 due to some old components.
When calling a partial view or view from a different area in MVC, specify the full path of the partial view or view. Since MVC is based on convention, by convention it will look in the same area the calling code in the view (or controller) resides for any partial views or views referenced, unless a specific path is used. Try using the full path to reference the partial view when it is located in the products area:
#Html.EditorFor(m => m.ProductEditModel, "~/Areas/Products/Views/Shared/EditorTemplates/_Edit.cshtml")
Since the view referenced is a shared view it doesn't matter if you specify the full path if you are in the same area. However, if you are trying to access a view within a different directory than the view trying to reference it and this directory is not named shared you will need to specify the full path regardless of area. It is similar when the controller calls the view; if a controller from the same area as the referenced view specifies the short name for the view and this view is from a parent directory named different than its own (ignoring the "controller" suffix) the view engine will not find your view. Unless of course the parent directory for the view is in the shared folder.
Whether it's in the controller or a view you can't use the "short name" across areas because the view engine has a convention for where to look when the path isn't used. Areas are meant to do this to keep your code separated, or decoupled if you will, at a high level by default. So any decision to "cross the barrier" should be thought about mindfully, but certainly not discouraged. It's all about convention.
I am answering my own question now.. My page view path was not correct. Since my area is Products, controller is ProductController, my page view should be placed in ~\Areas\Products\Views\Product\EditPage.cshtml, that way, it matches what the view engine expects, and the partial will be corrected resolved.

How to see all the views in mvc application

I added a default MvcApplication (MVC 4) (its name is MvcApplication3 matching the name of my solution's) width Home views (About, Index, Contact) and that will be my startup (bold in VS solution explorer's interface) project. Then I added another project (MvcApplication, but this time an empty one) called MvcApplication2 to the solution. Then I added the latter project as a reference to the first. I also added a controller called TestController (green line) to the referenced project and generated a view for its Index (red arrow) method. However, when I go to a link /Test or /Test/Index, the view I am expecting (red arrow) is not shown. Then I added the same folder Test with Index.cshtml (blue arrow) to the main project and now I am seeing its contents rather than the project's where my controller sits in.
Is it possible to make the application look for the views in the other project rather than the startup one?
I am adding the image of the structure to make it easier to follow.
P.S.: probably related: the breakpoint IS being hit in the Index method of TestController.
tldr; blue view is used instead of a red one
I think your problema is that you have set MvcApplication3 as startup Project and is causing you to open the view off that Project.
Is it possible to make the application look for the views in the other
project rather than the startup one?
Yes its posible, you can redirect your application the url. Think this your application have a url http://localhost:(someport) you can set redirect to the port of the second application.
I put a link to for better understanding a routing system of MVC: Documentation of routing system
As far as I know, you can't link to projects together like that. Each project becomes its own website with its own address. The reason you might put multiple projects together in one solution is to share things like classes, services, etc. I think what you're needing is areas:
http://www.codeproject.com/Articles/714356/Areas-in-ASP-NET-MVC
With some ideas from me and a friend of mine and a link about overriding RazorViewEngine I finally got what I wanted working exactly how I was expecting it to:
I created a folder named ViewsBase in the main project.
I rewrote RazorViewEngine this way: I only changed the place that was needed for me, leaving everything else like I found in the constructor of RazorViewEngine:
public class MyCustomViewEngine : RazorViewEngine
{
public MyCustomViewEngine()
{
...
ViewLocationFormats = new[]
{
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml",
"~/ViewsBase/{1}/{0}.cshtml",
"~/ViewsBase/{1}/{0}.vbhtml"
};
...
(I find it rather disturbing how I am unable to format the code properly. Can someone give me a hand please?)
and in Global.asax of the main project I added:
ViewEngines.Engines.Clear();
var ourViewEngine = new
ViewEngines.Engines.Add(ourViewEngine);
AreaRegistration.RegisterAllAreas();
...
I added a post-build event command:
xcopy /s "$(ProjectDir)Views\*.*" /Y "$(SolutionDir)$(SolutionName)\ViewsBase\"
It started looking for the views in the expected order
I added a ViewStart file to the base project to make it render the Layout too.

Changing ASP.Net MVC directory structure

I've been working with ASP.Net MVC (3) for some time now and i like it a lot. But one thing i find a bit annoying is having to browse between the controllers / views / model / script directory all the time. So i'm wondering if there's a way to tell MVC to look for the files in a different location?
Maybe someone can tell me how to simply group the files together by controller like:
Directory: /Membership
MembershipController
LogOnView
LogOnModel
RegisterView
RegisterModel
Kind regards
Olav
I know exactly what you're talking about. Here are the conditions where I find the default MVC folder structure to be onerous:
I'm using a model-per-view approach
My controller basically only works with that one particular view
I have some javascript that only pertains to that view
Why do I want to put each of these pieces in a different folder?
I create a folder for the view in the Views folder, so you have a folder ~/Views/MyEntityList (just like the traditional MVC approach), but I put everything that pertains to that component there:
~/Views/MyEntityList/
MyEntityListController.cs
MyEntityListModel.cs
MyEntityList.js
MyEntityList.aspx
I find this structure leads all the developers to keep views decoupled from one another. No special MVC configuration is required, except for allowing browsers to access the .js resources directly.
There are some architectural patterns where this might not be a good way to go. For a model-per-view approach (see Los Techies for more description) I really like this structure.
I think you need to get the Solution Navigator extensions via Power Tools update for VS 2010.
That way, you can display in the Solution Navigator, as opposed to the solution explorer, only the open files, for example. Makes it easier.
By the way, delete all the model folders and create a separate model project, eg:
MyApp.Domain
Any solution that is beyond basic will benefit from this.
As stated in the comments to your question, Areas will also reduce your navigation requirements.
The only "looking of files" going on is with views, everything else is just a convention, so if you want you could have:
Directory: /Membership
MembershipController
LogOnView
LogOnModel
RegisterView
RegisterModel
... but the views must be in ~/Views/Membership
It looks like you have to override some behavior in the view engine. You can See this question to get a better idea.
One way I can think of to achieve this is by writing your custom view engine. You can place all these below files in Controllers/Membership
MembershipController
LogOnView
LogOnModel
RegisterView
RegisterModel
Models will not be a problem you can simply change the namespace for the models, the only problem is with the views. For this write your custom view engine so that your mvc application knows the physical location of the view files as follows.
public class CustomViewEngine : RazorViewEngine
{
public CustomViewEngine()
{
ViewLocationFormats = new[]
{
"~/Controllers/{1}/{0}.cshtml",
};
}
}
In global.asax.cs add the ViewEngine in Application_Start() by including the following code
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());
You may also have to take care of various other factors like updating the Layout attribute depending on where you place the _Layout.cshtml.
In case you are using areas, add the AreaViewLocationFormats string array as well.
You can do further customization by overriding some of the methods like FileExists, CreateView, CreatePartialView.
Note: Do not forget to copy web.config in the views folder to the Membership controller. Otherwise application does not find the required mvc namespaces and it does not find the symbols like viewbag, model etc.

How to organize views, partial views and their view models?

I'm developing in asp.net mvc2. I am beginning to create a lot of views and partial views, for most of which I've had to create a view model. This is looking like soon it's going to become unmanageable to remember what view goes with what model. I've tried to use inheritance in my view models as much as possible.
I'm wondering how others manage this in their projects?
I place my PV's inside a PV folder within the Views folder.
so Views/Home/PartialViews;
I then register that path in my global.asax file;
public static void RegisterViewEngine()
{
ViewEngines.Engines.Clear();
WebFormViewEngine viewEngine = new WebFormViewEngine();
viewEngine.PartialViewLocationFormats = (new[] {
"~/Views/Shared/PartialViews/{0}.ascx",
"~/Views/{1}/PartialViews/{0}.ascx"
}).Concat(viewEngine.PartialViewLocationFormats).ToArray();
ViewEngines.Engines.Add(viewEngine);
}
I'm also these days leaning towards putting the FormViewModels within the views folder.
/Views/Home/IndexFormViewModel.cs
The above is recent as before this I put them in a Models project but found that sometimes I could end up with a cyclic reference situation with my Model and DataRepository.

Resources