ASP.net MVC - using custom culture - resources not being loaded - asp.net-mvc

I have an ASP.net MVC site, which I want to use a couple of resource files to set some strings.
I have a class library containing my viewmodels, and I have added a resource file (ValidationMessages) there, with a single string (called Test), and then have a property like so in my view model:
public string TestResource
{
get
{
return ValidationMessages.Test;
}
}
And that works fine, when output on my view like so:
<div>#Model.TestResource</div>
If I add a ValidationMessages.en-au.resx (my default would be en-gb) file to my class library and create a different version of the test string, and then have the following in my global.asax:
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-au");
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-au");
}
And this also works fine.
What I want to do is add a custom culture 'en-gb-ly' - I have registered this culture on my machine ok (using code from here). When I set the current culture to "en-gb-ly" in my global.asax and include a ValidationMessages.en-gb-ly.resx in my class library, the output has reverted back to the 'base' version of the Test string, not the one in my en-gb-ly resource.
Anybody any idea why this might be happening?

A first thing to check is that the en-ly satellite assembly gets deployed as expected. Unfortunately if your custom culture is not installed on your build machine, then it won't even get built!

Related

Dotnetnuke Custom Module Settings

I have created a dotnetnuke module, it has multiple controls wrapped in a single moduleNow i want to access the settings variable across module, say for example i have a setting for dateformat, now the dateformat user selects should be used throughout moduleIt works fine with the view control which comes by default with Dotnetnuke (ChrisToc Template)But when i add new control it does not works, i also added proper inherits, it never throws compile error (in case it does not gets proper namespace)
Below is the code i am using:
public partial class ViewEntry : WireModuleBase
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write("SETTINGS: " + Settings["WireDateFormat"]);
}
}
Any help will be appreciated
I don't ever use the Setting dictionary in my module views. First, it leaves you open to code errors by having to hardcode the key string when accessing the setting. Second, it makes it hard to share with you business logic or other views. My preferred pattern for settings is to create an interface and class for my settings that provides class attribute for my settings and performs the plumbing calls to DNN core to get and set those settings.
Follow this link to a Codeplex project where you will find a class SettingsRepository and ISettingsRepository interface.
Once you modified the public properties for your settings (ie: WireDateFormat) into the class and interface, you can then use it in your module settings implementation.
Get the setting:
ISettingsRepository settingsCtrl = new SettingsRepository(this.ModuleId, this.TabModuleId);
txtSetting1.Text = settingsCtrl.Setting1;
Write the setting
ISettingsRepository settingsCtrl = new SettingsRepository(this.ModuleId, this.TabModuleId);
settingsCtrl.Setting1 = txtSetting1.Text;
Once the settings is stored (in this case using the TabModuleId, but you can use the ModuleID constructor overload if you want to share the setting across modules on a page), you can use the same "get" code in any of your module views or business logic.

Turning off the WebFormViewEngine when using razor?

I downloaded Glimpse this morning to try it out and noticed this when I click on the views tab:
It checks all of the loaded view engines. I found where the RazorViewEngine is specified in web.config, but I couldn't find where the WebFormViewEngine was. Since I know my project will never have an web form view in it,
Is it ok/safe to turn off WebFormViewEngine?
How can I turn off WebFormViewEngine?
It is perfectly OK to remove the web forms view engine if you are not using it. You can do it like:
public class Global : HttpApplication
{
public void Application_Start()
{
// Clears all previously registered view engines.
ViewEngines.Engines.Clear();
// Registers our Razor C# specific view engine.
// This can also be registered using dependency injection through the new IDependencyResolver interface.
ViewEngines.Engines.Add(new RazorViewEngine());
}
}
The above method calls go in your global.asax file.
source of code
An alternative would be to remove only the view engine you want to remove:
var webformVE = ViewEngines.Engines.OfType<WebFormViewEngine>().FirstOrDefault();
ViewEngines.Engines.Remove(webformVE);

ASP.Net MVC 3 Display-templates and Editor-Template Custom Location, how to?

i am going Nuts,
i am using MVCContrib, to create pluggable site using Portable Areas, and everything is working well so far, except that when i started using MVC Templates, what is happening is if i put the The templates in the respective folder of the View it works, examples
HostApplication/Views/Home/DisplayTemplates/FirstName.cshtml
HostApplication/Areas/PortableArea_Blog/Views/Home/DisplayTemplates/Auther.cshtml
but what i want really is the ability to create common templates Set and utilize it from either Host Application or Portable Area, so to do that i created a new Portable Area Called DisplayTemplates(to utilize MVCContrib Ability to compile Views), here is the portable Area structure
DisplayTemplates
|-Views
|-CommentTemplate.cshtml
now in my host Application i have created a Test Model and added UIHint Attribute
public class HostModel
{
[UIHint("~/Areas/DisplayTemplates/Comment.cshtml")]
public string Name { get; set; }
}
but it is not working, so i thought it has something to do with Partial Views Location so i created a CustomView Engine to find Partial Views in that Location and registerd it in Global.asax, here is a short idea about so i wont bore you with full code
public class AreaViewEngine : RazorViewEngine
{
public AreaViewEngine()
{
// {0} = View name
// {1} = Controller name
// View locations
ViewLocationFormats = new[]
{
"~/Areas/DisplayTemplates/{0}.cshtml"
};
PartialViewLocationFormats = ViewLocationFormats;
AreaPartialViewLocationFormats = ViewLocationFormats;
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return new RazorView(controllerContext, partialPath, null, true, new[] { "cshtml" });
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
return new RazorView(controllerContext, viewPath, masterPath, true, new[] { "cshtml" });
}
}
what is even more weird, is that it seems that that UIHint with Explicit location to Display Template, does not work, here is an example
public class HostModel
{
//this works
[UIHint("FirstName")]
//this does not work
[UIHint("~/Views/Home/DisplayTemplates/FirstName.cshtml")]
public string Name { get; set; }
}
and yes
FirstName.cshtml is in HostApplication/Views/Home/DisplayTemplates/FirstName.cshtml
again sorry for the long post, but i gave up on finding a solution, so any help would be totally appreciated.
Danny is correct. The Templates are found the same way that Partial Views are found.
By default the WebFormViewEngine and RazorViewEngine are going to search the following locations for a template.
For display templates:
~/Views/{controller}/DisplayTemplates
~/Views/Shared/DisplayTemplates
For editor templates:
~/Views/{controller}/EditorTemplates
~/Views/Shared/EditorTemplates
I think the name of the sub-directories (i.e., "DisplayTemplates" and "EditorTemplates") are hard-coded into MVC somewhere (I know it's open source and I could find it, but I'm not going to).
I think the easiest way to change the location somewhat is to override the ViewEngine. My custom ViewEngine is pretty complicated at this point, but I suspect you could get away with the following.
Let's say you want your templates to be in ~/Views/Templates.
Create a class that inherits from the view engine you're using now (probably WebFormViewEngine or RazorViewEngine). Add an empty constructor. It should looks like this:
namespace MySite
{
public class MySiteViewEngine : RazorViewEngine // <<-- or WebFormViewEngine
{
public MySiteViewEngine()
{
// We'll put some code here in a later step
}
}
}
Now, add the following lines to the Application_Start method of Global.asax.cs:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MySiteViewEngine());
At this point, compile and run your application. Everything should be running exactly like it is running now. You're basically using the same view engine you were using before.
But now, we want to add a location to search when looking for PartialViews. This is simply done by adding to the PartialViewLocationFormats. So, now in the constructor of your custom view engine, you want to add to the base class' array like so:
base.PartialViewLocationFormats = new string[] {
"~/Views/Templates/{0}.cshtml"
}.Union(base.PartialViewLocationFormats).ToArray<string>();
A couple of notes about the above:
The entry above will make it so that your view engine looks for the String display template at ~/Views/Templates/DisplayTemplates/String.cshtml.
The location format in these view engines includes the file extension, so if you're using Razor/C# use "cshtml", Razor/VB use "vbhtml", WebForms add "aspx" and "ascx".
The way I'm doing it above, I'm adding my location format to the top of the list but keeping all the default locations. You might consider removing those.
Watch the current formats and you'll see that you will also get a controller in the {1} position in the format, so if you wanted to have a Templates directory underneath every controller you could.
Careful, once you get started moving things around with a view engine, it gets addictive. You might find yourself moving everything around.
Good luck.
Instead of creating a new ViewEngine you can easily modify the existing ones at runtime:
private void FixMvcTemplateAreaBug(string areaName)
{
foreach (BuildManagerViewEngine viewEngine in ViewEngines.Engines)
{
List<string> viewLocations =
new List<string>(viewEngine.PartialViewLocationFormats);
foreach (var extension in viewEngine.FileExtensions)
viewLocations.Add("~/Areas/" + areaName +
"/Views/Shared/{0}." + extension);
viewEngine.PartialViewLocationFormats = viewLocations.ToArray();
}
}
Place the above in an appropriate location (like area registration) and you'll be fine.
I think you've had no answers, because there isn't one :-(
I've searched high and low the last few days trying to find a solution to this (since as you mentioned, it's had a lot of views). Unfortunately I can't find any way to override this.
I think you're stuck :(
Edit: Came across this post for reading views from a database instead of the disk:
ASP.NET MVC load Razor view from database
I wonder whether the DisplayTemplates are read this way. If so, you could try hijacking them and reading them from another location (instead of DB)?
Pardon if I missed something above, but is there a reason that the override of Html.DisplayFor that allows the template to be specified won't work here? For example:
#Html.DisplayFor(m => m.MyItem, "~/Views/Controller/DisplayTemplates/MyItem.cshtml")
Cheers,
It might be a little late :) but this is now possible in MVC 4. The generic templates has to be placed in ~/Views/Shared/EditorTemplates
Source

Multilingual URLs with ASP.NET MVC

I’m working out the concepts for a new project where I need to support for multilingual URL’s. Ideally all URL’s need to be in the native language of the user. So we don’t want to use domain.com/en/contact and domain.com/es/contact but we like domain.com/contact and domain.com/contactar (contactar is Spanish for contact). Internally both should be routed to the same ContactController class.
This could be handled by adding multiple static routes to Global.asax.cs for each language but we’d like to make this very dynamic and would like the user of the system to be able to change the translation of the URL’s through the content management system. So we need some kind of dynamic mapping from URL’s to controllers and actions.
By looking at the source code of MVC3 I figured out that the ProcessRequestInit method of MvcHandler is responsible for determining which controller to create. It simply looks in the RouteData to get the name of the controller. One way to override the default MVC routing would be to create a simple default route that uses a custom RouteHandler. This RouteHandler forces MVC to use my own custom subclassed version of MvcHandler that overrides the ProcessRequestInit method. This overridden method insert my own dynamically found controller and action into the RouteData before calling back to the original ProcessRequestInit.
I’ve tried this:
Global.asax.cs
routes.Add(
new Route("{*url}", new MultilingualRouteHandler())
{
Defaults = new RouteValueDictionary(new { controller = "Default", action = "Default" })
}
);
MultilingualRouteHandler.cs
public class MultilingualRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MultilingualMVCHandler(requestContext);
}
}
MultilingualMvcHandler.cs
public class MultilingualMVCHandler : MvcHandler
{
public MultilingualMVCHandler(RequestContext context) : base(context)
{
}
protected override void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
if (RequestContext.RouteData.Values.ContainsKey("controller"))
{
RequestContext.RouteData.Values.Remove("controller");
}
if (RequestContext.RouteData.Values.ContainsKey("action"))
{
RequestContext.RouteData.Values.Remove("action");
}
RequestContext.RouteData.Values.Add("controller", "Product");
RequestContext.RouteData.Values.Add("action", "Index");
base.ProcessRequestInit(httpContext, out controller, out factory);
}
}
In this handler I hardcoded the controller and action for testing purposes to some fixed values but it’s not difficult to make this dynamic. It works but the only problem is that I had to modify the source code of ASP.NET MVC3 to get it working. The problem is that the ProcessRequestInit method of MvcHandler is private and thus cannot be overridden. I’ve modified the source code and changed it to protected virtual which allows me to override it.
This is all great but possibly not the best solution. It’s cumbersome that I would always need to distribute my own version of System.Web.Mvc.dll. It would be much better that it would work with the RTM version.
Am I missing any other possibilities of hooking into ASP.NET MVC that would allow me to dynamically determine the controller and action to launch, depending on the URL? One other way I thought of is to build the RouteCollection dynamically on *Application_Start* but I think that will make it more difficult to change it on the fly.
I would appreciate any tips of hooks that I’ve not yet found.
This is fairly old now, nut just in case anyone else is looking for something similar...
Unless I'm completely misunderstanding what you want to do, it's pretty simple really.
Step 1: Add a new route to global.ascx.cs containing a reference to your personal routing engine
routes.Add(new MyProject.Routing.ContentRoutingEngine());
Make sure that it is in the right place in the list of routes so that other routing engines can catch stuff before it if required, or continue the route search if your engine doesn't handle a particular route. I put it after the ignores, but before the MVC default routes.
Step 2: Create the Content Routing Engine, making sure that it inherites from System.Web.Routing.RouteBase abstract class, and overrides the GetRouteData and GetVirtualPath methods as required e.g.
public class ContentRoutingEngine : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var routeHandler = new MvcRouteHandler();
var currentRoute = new Route("{controller}/{action}", routeHandler);
var routeData = new RouteData(currentRoute, routeHandler);
// set your values dynamically here
routeData.Values["controller"] = "Home" ;
// or
routeData.Values.Add("action", "Index");
// return the route, or null to have it passed to the next routing engine in the list
return routeData;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
//implement this to return url's for routes, or null to just pass it on
return null;
}
}
and that should do it. You can change routes as dynamically as you wish within your engine, and no changes to MVC source required. Let the standard MVC RouteHandler actually invoke the controller.
Postscript: Obviously the code above is not production standard - it's written to make it as obvious as possible what's going on.
If you are allowing modification of urls through your CMS, then you will have to keep all old versions of the urls so that you can 301 redirect to the new ones.
The best bet for this will be to put the url tokens eg "contactar" in the db along with its corresponding controller.
query that, and create your routes out of that.
create a route that will handle the 301s
I think that most elegant solution would be using some action filter combined with custom ActionInvoker. That way, you could invoke an action that has specific filters applied. Something like ActionName attribute, only capable to accept multiple values (names).
Edit: Take a look at ActionMethodSelectorAttribute, meybe you don't need a custom ActionInvoker after all.

Is there any way to re-use editor & display templates in asp mvc applications?

We're developing 3 asp mvc applications, all will require some of the same shared editor & display template views. Rather than copy/paste these across all 3 projects, is it possible somehow to put them in a shared component and reference them somehow in all applications?
You're going to need to create your own ViewEngine if you want to take to views from a place other than the Views folders.
public class CustomViewEngine : WebFormViewEngine {
public CustomViewEngine() : base() {
MasterLocationFormats = new[] {
"/YourFolder/{1}/{0}.master",
"/YourFolder/Shared/{0}.master"
};
ViewLocationFormats = new[] {
"/YourFolder/{1}/{0}.aspx",
"/YourFolder/{1}/{0}.ascx",
"/YourFolder/Shared/{0}.aspx",
"/YourFolder/Shared/{0}.ascx"
};
PartialViewLocationFormats = ViewLocationFormats;
}
//Override the FindView method to implement your own logic
public override ViewEngineResult FindView(
ControllerContext controllerContext, string viewName,
string masterName, bool useCache)
return base.FindView(controllerContext, viewName, masterName, useCache);
}
}
Then to register your ViewEngine in the Global.asax :
protected void Application_Start() {
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());
}
Also, this post might be helpful. (too bad the download link at the bottom is broken though)
Edit : It appears this solution won't work in practice, since ASP.NET won't allow loading content from outside of the application's directory (see Eilon's comments).
But the link above, storing views in DB, could still be helpful.
I am not sure how it can be done but there must be a way to compile the pages in a DLL and share the library.
But if you can afford to use custom view engine and another template system (for example Stringtemplate) then you can share the views as if you are sharing a library:
Create a class library project
Create your templates as normal and mark them as 'Embedded resource'. This will make sure they will be put in the library's DLL
Write your custom view engine to pull the templates from the assembly (DLL). A good start might be editing/forking an existing one: http://code.google.com/p/string-template-view-engine-mvc/source/browse/trunk/StringTemplateViewEngine/StringTemplateViewEngine.cs
Then register your view engine in your ASP.NET MVC project
To share user controls (or almost any content) between multiple ASP.NET applications you can use the trick listed here: http://aspadvice.com/blogs/ssmith/archive/2006/10/05/Tip_3A00_-Share-User-Controls-Between-Applications-in-ASP.NET.aspx
The basic idea is to place the ASCX files in a folder and make that folder in to a virtual folder of other applications (you can have multiple virtual folders pointing at the same physical folder).

Resources