In MVC 4, you can just append .Mobile to any view and mobile devices will automatically get served that view from the same controller. Is there a way to store the .Mobile files in a different folder? I really want to store the desktop files in one "Area" and the mobile in another "Area". Anyone know of something like this?
This can easily be accomplished by creating a custom implementation of RazorViewEngine and adding the custom mappings to the ViewLocationFormats. It is important to remember to add the custom mappings to the beginning of the ViewLocationFormats array as they are more specific than the existing mappings.
namespace MobileViewsInMobileFolder.Utility {
public class MyCustomViewEngine : RazorViewEngine {
public MyCustomViewEngine() {
List<string> existingViewLocationFormats = ViewLocationFormats.ToList();
//Folder Structure: Views\Home\Desktop and Views\Home\Mobile
existingViewLocationFormats.Insert(0, "~/Views/{1}/Desktop/{0}.cshtml");
existingViewLocationFormats.Insert(0, "~/Views/{1}/Mobile/{0}.cshtml");
//Folder Structure: Views\Desktop\Home and Views\Mobile\Home
existingViewLocationFormats.Insert(0, "~/Views/Desktop/{1}/{0}.cshtml");
existingViewLocationFormats.Insert(0, "~/Views/Mobile/{1}/{0}.cshtml");
ViewLocationFormats = existingViewLocationFormats.ToArray();
}
}
}
And then make sure to add the custom view engine in the Application_Start
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MyCustomViewEngine());
Related
I have read a nice article about how I can use feature folder structure in my ASP.NET Core MVC application. My plan is to use then a feature folder structure to organize my web application in a better way.
First of all lets see my folder structure:
...
wwwroot
Claims
Controllers
Services
Views
Shared
Map
...
I have followed the article and I implemented the IViewLocationExpander like the following:
public class MyViewLocationExpander : IViewLocationExpander
{
public void PopulateValues(ViewLocationExpanderContext context)
{
context.Values["customviewlocation"] = nameof(MyViewLocationExpander);
}
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
{
var viewLocationFormats = new[]
{
"/Claims/Views/{0}.cshtml",
"/Claims/Views/Shared/{0}.cshtml"
};
return viewLocationFormats;
}
}
I placed my main Claims.cshtml view in the "/Claims/Views/" folder. At the beginning of my Claims.cshtml I have to following line to render my partial view:
#Html.Partial("_NewClaimPopup");
As for the _NewClaimPopup.cshtml, it placed it into the path "/Claims/Views/Shared". But unfortunately I got the following exception when trying to GET the following url: http://localhost:13078/Claims/Claims
InvalidOperationException: The partial view '_NewClaimPopup' was not found. The following locations were searched:
/Views/Claims/_NewClaimPopup.cshtml
/Views/Shared/_NewClaimPopup.cshtml
It seems that the custom paths are successfully added by the implementation of IViewLocationExpander.
Additional infos:
What I also tried is to use "~" sign in paths of the implementation of IViewLocationExpander, so: "~/Claims/Views/{0}.cshtml" and "~/Claims/Views/Shared/{0}.cshtml" but it does not help.
I tried to use absolute path for rendering my partial view, but still nothing
#Html.Partial(""~/Claims/Views/_NewClaimPopup.cshtml");
And of course, I registered my expander in the Startup.cs:
services.Configure<RazorViewEngineOptions>(options => options.ViewLocationExpanders.Add(new MyViewLocationExpander()));
Last but not least I attach a picture about my project structure:
Any other idea? Thanks in advance for any help!
I have made sample application. You can download from below link
Custom View Location
I added MVC to my existing webforms project. All is going well except for RenderAction is looking for .aspx files
The view '_Mainmenu.cshtml' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Areas/NewPages/Views/Shared/_Mainmenu.cshtml.ascx
The view is
~/Areas/NewPages/Views/Shared/_Mainmenu.cshtml
And it does exist in that folder. Can anybody help me sort this out.
Everything else MVC is working OK I even have PITA EntityFramework working too
Any help would be appreciated
The view '[viewname]' or its master was not found or no view engine supports the searched locations indicates that you're using default view engine which prioritizes web forms view engine (the path shown as ~/Areas/NewPages/Views/Shared/_Mainmenu.cshtml.ascx means that MVC view engine prioritizes searching ASPX & ASCX files instead of Razor cshtml files). To change this behavior which MVC uses Razor view engine by default, insert these lines into Application_Start method on Global.asax:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new RazorViewEngine());
// ViewEngines.Engines.Add(new WebFormViewEngine()); => optional webforms engine registration
Additionally, if default Razor view engine still can't recognize cshtml files in areas properly, you need to create a custom view engine class which inherits RazorViewEngine and setting AreaViewLocationFormats in its constructor like this:
public class CustomViewEngine : RazorViewEngine
{
public CustomViewEngine()
{
// Route parsing convention for view engines:
// {0} means action method name
// {1} means controller class name
// {2} means area name
AreaMasterLocationFormats = new[]
{
"~/Areas/{2}/Views/Shared/{0}.cshtml"
};
AreaViewLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}.cshtml",
// other view search locations here
};
AreaPartialViewLocationFormats = AreaViewLocationFormats;
}
}
Note that the custom view engine will search all view pages inside areas specified by controller action method depending on routes defined in AreaViewLocationFormats.
Then, register custom view engine class at the same place as RazorViewEngine, i.e. in Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// clear all view engines repository first
ViewEngines.Engines.Clear();
// register Razor view engine only
ViewEngines.Engines.Add(new RazorViewEngine());
// register custom view engine class here
ViewEngines.Engines.Add(new CustomViewEngine());
// other initialization codes here
}
Similar issues:
ASP.NET MVC: When should I create custom View Engine
How do I implement a custom RazorViewEngine to find views in non-standard locations?
In the "Global.asax" file, find "Application_Start()"
Then, Please input this code --
RemoveWebFormEngines();
I am using a project called MVCForum and have created a new project in the solution which, for demo purposes, let's call "ExternalApp".
Now, I have added the ExternalApp reference to the MCVForum application, and can call the controller: http://mysite[.]com/TestController
where "TestController" is my external controller. That is, the controller lives in ExternalApp.
The problem is that when I try to return the view from "Test" in TestController, the view cannot be found.
The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Themes/Metro/Views/Test/Index.cshtml
~/Themes/Metro/Views/Extensions/Test/Index.cshtml
~/Views/Extensions/Test/Index.cshtml
~/Views/Test/Index.cshtml
~/Views/Test/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml
The application seems to be looking within it's own project for the view, and not in the ExternalApp/Views folder. How can I get my external app to render the correct view?
You can to create a custom view engine but as described hereyou have there are number of modifications you need to make:
In order for the views in our MVCExternalApp project to be available at runtime, they must be copied to the MVCForum output folder. Unless you wish to do this manually, you have to specifically tell each view to copy to output. This option forces the files to go into the bin folder.
For each view, right click and select properties. Change the 'Copy to Output Directory' option to 'Copy Always'. The will ensure that the files are always put in the output when the referencing project is built. You will also want to do this for the Web.config.
Create a custom view engine:
public class CustomViewEngine: RazorViewEngine
{
public CMSViewEngine()
{
ViewLocationFormats = new string[]
{
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml",
"~/bin/Views/{1}/{0}.cshtml",
"~/bin/Views/{1}/{0}.vbhtml",
"~/bin/Views/Shared/{0}.cshtml",
"~/bin/Views/Shared/{0}.vbhtml"
};
PartialViewLocationFormats = new[]
{
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml",
"~/bin/Views/{1}/{0}.cshtml",
"~/bin/Views/{1}/{0}.vbhtml",
"~/bin/Views/Shared/{0}.cshtml",
"~/bin/Views/Shared/{0}.vbhtml"
};
}
}
I override only PartialViewLocationFormats an ViewLocationFormats but you can override the rest of locations if needed;
Register view engine in Application_Start method in Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
//Remove all view engine
ViewEngines.Engines.Clear();
//Add Custom view Engine Derived from Razor
ViewEngines.Engines.Add(new CustomViewEngine());
}
You can use something like Razor Generator to compile your views into your ExternalApp assembly, or you can just run the two apps separately under one site.
I am new to MVC and started with MVC 4. I want to make an online shop application.
-Same shopping logic will be used by two different web sites/domains.
-And the views must alter according to domain name and their mobile versions.
Logic file structe is like this:
Controllers/HomeController.cs
Controllers/ProductController.cs
Models/Home.cs
Models/Product.cs
View file structure for first domain:
Views/my_1st_Domain/Home/Index.cshtml
Views/my_1st_Domain/Home/Index.Mobile.cshtml
Views/my_1st_Domain/Home/Terms.cshtml
Views/my_1st_Domain/Home/Terms.Mobile.cshtml
Views/my_1st_Domain/Product/Index.cshtml
Views/my_1st_Domain/Product/Index.Mobile.cshtml
Views/my_1st_Domain/Product/Detail.cshtml
Views/my_1st_Domain/Product/Detail.Mobile.cshtml
View file structure for second domain:
Views/my_2nd_Domain/Home/Index.cshtml
Views/my_2nd_Domain/Home/Index.Mobile.cshtml
Views/my_2nd_Domain/Home/Terms.cshtml
Views/my_2nd_Domain/Home/Terms.Mobile.cshtml
Views/my_2nd_Domain/Product/Index.cshtml
Views/my_2nd_Domain/Product/Index.Mobile.cshtml
Views/my_2nd_Domain/Product/Detail.cshtml
Views/my_2nd_Domain/Product/Detail.Mobile.cshtml
The question is:
How do switch between domain specific view folders automatically?
It can be done manually by repeating this line everywhere:
return View("~/Views/" + getDomainSpecificFolder() + "/Home/Index" + getMobileSuffixIfNeeded() + ".cshtml");
Is there any easier way to change base view folder globally with one shot?
Thanks in advance,
You can make own ViewEngine, and return specific view depending on your current domain.
In controller code you just need to return usual
return View();
And all logic will be handled in custom ViewEngine.
Here is some details about it -
http://weblogs.asp.net/imranbaloch/archive/2011/06/27/view-engine-with-dynamic-view-location.aspx
http://www.headcrash.us/blog/2011/10/custom-view-engine-for-localized-views-with-asp-net-mvc-razor/
Basically you need to make CustomViewEngine class, that inherits RazorViewEngine and register it in global.asax. Then in that class you need to overwrite FindView method, that will use path for view based your domain.
I have a Controller called BaseController and Controller1 and Controller2 inherit from it.
All of the views for these controllers are under ~/Views/Base/ the reason for this is that Controller1 and 2 do the exact same thing but have custom attributes for certain things on some overriden actions.
I want to be able to point to ~/Views/Base as the location to look for views in both Controller1 and Controller2. Now can i do this without having to implement my own ViewLocator as per Dale's solution in this post Views in separate assemblies in ASP.NET MVC
I would prefer to not throw all these views into ~/Views/Shared as they aren't really shared except in between these two Controllers.
You could write a custom view engine in order to add this new location in the search list:
public class MyViewEngine : RazorViewEngine // WebFormViewEngine - if you are using WebForms
{
public MyViewEngine()
{
ViewLocationFormats = base.ViewLocationFormats.Union(new[]
{
"~/Views/Base/{0}.cshtml",
"~/Views/Base/{0}.vbhtml",
"~/Views/Base/{0}.aspx",
"~/Views/Base/{0}.ascx",
}).ToArray();
}
}
which will be registered in Application_Start:
ViewEngines.Engines.Add(new MyViewEngine());