Posts for the tag: AspNetMVC

Nov
4
2011

Changing Your Website Theme based on the Users Language or Culture

Currently I am working on pretty interesting project where if the user is not from the United States but speaks English, I need to present to them an entirely different themed experienced while still  providing the same website functionality.

ASP.Net MVC does a very nice thing by encapsulating the view engine code so that is pluggable. As a matter of fact, you can actually use more than one view engine for your website if you wanted, as seen in Scott Hanselman’s post.

In my case, I still want to use the Razor View Engine but I just want to add functionality to dynamically change the paths to the views so that when user hits the web site and their current thread’s UI Culture is say United Kingdom English, I want to point them to an entirely different view engine location where the markup is very different but uses the same model.

Overriding the Razor View Engine

The way to do this is to override the RazorViewEngine class modifying its functions to dynamically change the path based on the current culture.

 

 1: using System.Web.Mvc;
 2: using Infrastructure.Http;
 3: using Infrastructure.Theming.Context;
 4: using StructureMap;
 5:  
 6: namespace Infrastructure.Theming.ViewEngines
 7: {
 8:     public class ThemingRazorViewEngine : RazorViewEngine
 9:     {
 10:         private readonly IThemingContext _themingContext;
 11:         public ThemingRazorViewEngine()
 12:         {
 13:             _themingContext = ObjectFactory.GetInstance<IThemingContext>();
 14:             LoadFormats();
 15:         }
 16:  
 17:         public ThemingRazorViewEngine(IThemingContext themingContext)
 18:         {
 19:             _themingContext = themingContext;
 20:             LoadFormats();
 21:         }
 22:  
 23:         private void LoadFormats()
 24:         {
 25:             AreaViewLocationFormats =
 26:                 new[]
 27:                     {
 28:                         "~/Areas/{2}/Views/%G/{1}/{0}.cshtml",
 29:                         "~/Areas/{2}/Views/%G/{1}/{0}.vbhtml",
 30:                         "~/Areas/{2}/Views/%G/Shared/{0}.cshtml",
 31:                         "~/Areas/{2}/Views/%G/Shared/{0}.vbhtml"
 32:                     };
 33:             AreaMasterLocationFormats =
 34:                 new[]
 35:                     {
 36:                         "~/Views/%G/{1}/{0}.cshtml",
 37:                         "~/Views/%G/Shared/{0}.cshtml",
 38:                         "~/Areas/{2}/Views/%G/{1}/{0}.cshtml",
 39:                         "~/Areas/{2}/Views/%G/Shared/{0}.cshtml"
 40:                     };
 41:  
 42:             AreaPartialViewLocationFormats =
 43:                 new[]
 44:                     {
 45:                         "~/Areas/{2}/Views/%G/{1}/{0}.cshtml",
 46:                         "~/Areas/{2}/Views/%G/Shared/{0}.cshtml"
 47:                     };
 48:  
 49:             ViewLocationFormats =
 50:                 new[]
 51:                     {
 52:                         "~/Views/%G/{1}/{0}.cshtml",
 53:                         "~/Views/%G/Shared/{0}.cshtml"
 54:                     };
 55:  
 56:             MasterLocationFormats =
 57:                 new[]
 58:                     {
 59:                         "~/Views/%G/{1}/{0}.cshtml",
 60:                         "~/Views/%G/Shared/{0}.cshtml"
 61:                     };
 62:  
 63:             PartialViewLocationFormats =
 64:                 new[]
 65:                     {
 66:                         "~/Views/%G/{1}/{0}.cshtml",
 67:                         "~/Views/%G/Shared/{0}.cshtml"
 68:                     };
 69:         }
 70:  
 71:         
 72:  
 73:         protected override IView CreatePartialView
 74: (ControllerContext controllerContext, string partialPath)
 75:         {
 76:             var compCulture = GetCompCulture();
 77:             string replacedPath = partialPath.Replace("%G", compCulture);
 78:             return base.CreatePartialView(controllerContext, replacedPath);
 79:         }
 80:  
 81:         protected override IView CreateView
 82: (ControllerContext controllerContext, string viewPath, string masterPath)
 83:         {
 84:             var compCulture = GetCompCulture();
 85:             string replacedMasterPath = masterPath.Replace("%G", compCulture);
 86:             string replaceViewPath = viewPath.Replace("%G", compCulture);
 87:             return base.CreateView(controllerContext, replaceViewPath, replacedMasterPath);
 88:         }
 89:  
 90:         protected override bool FileExists
 91: (ControllerContext controllerContext, string virtualPath)
 92:         {
 93:             var compCulture = GetCompCulture();
 94:             string replaceVirtualPath = virtualPath.Replace("%G", compCulture);
 95:             return base.FileExists(controllerContext, replaceVirtualPath);
 96:         }       
 97:  
 98:         private string GetCompCulture()
 99:         {
 100:             string folder = _themingContext.GetFolderName();
 101:             if (string.IsNullOrEmpty(folder))
 102:             {
 103:                 folder ="enUS";
 104:             }
 105:  
 106:             return folder;
 107:         }
 108:  
 109:  
 110:     }
 111: }

On every request this request for the view path will first to check to see if the file exists using the FileExists functionion and then if does Exists depending on what type of view request it is, the CreateView, or CreatePartialView will be called. As you can see, I have added functionality to replace the “%G” in the path with what ever the culture is returned from the GetCompCulture function.

All my GetCompCulture function is doing is injection a business object that is getting the users current culture from current thread and then stripping out the “-“ so that “en-US” is “enUS”. It is also defaulting any language culture that we have not set up to also be “enUS”.

Once I have a custom view engine, I just need to tell the MVC framework to only use it instead of the default Razor view engine. I can do that in the application startup event in the Global.cs file.

 1: ViewEngines.Engines.Clear();
 2: ViewEngines.Engines.Add(new ThemingRazorViewEngine());

The Folder Structure

Once my view engine is setup, I then need to setup my folder structure to match. Note, that if you are using a _viewStart.cshtml file, you will need to change the path of the _Layout file in that file.

 1: @{
 2:     Layout = "~/Views/enUS/Shared/_Layout.cshtml";
 3: }

The folder structure looks something like this.

internationaltheming

And that’s it. Your views can now be totally separate and totally different markup based on your language criteria, or any criteria you want to provide for that matter.

Hope that helps.

Oct
30
2011

Moving on to Orchard and Maybe Even Writing Some More

Some of you (it was probably just me) may have notices that my site has been down for about three months. Actually I didn’t care too much at the time as I was head deep in a project that really pretty much kept me awfully busy.

Well, I finally got around to fixing my site and in the process of doing that, I also upgraded the site to the Orchard Project.

I know, I am breaking the first rule blogging when I say this; but here it goes… I’m back!

No. No. I shouldn’t say that, so ignore that last sentence.

I do want to say this though. I plan to broaden my site to more than just posts about programming and Dot Net coding. This will give me more opportunities to write posts and more opportunities to share with you my experiences. I actually want to start out by writing a few posts about my experiences upgrading my site to Orchard. So you may see a few posts like that in the upcoming week.

I have also been in working in a SCRUM, Agile methodology for over a year now, so I think that now there is a lot to share, good and bad, in that area as well.

I want to write some about Social Media. You may have noticed that I am a big fan of Twitter. My wife is an even bigger fan. We have actually met quite a few people (some now are even quite close friends now) in person through that site and I think sharing our experiences with this would be fun to write about.

I still will be writing mostly about coding, since that is where my ultimate passion lies. I have learned a lot since my last post. I have since deployed two MVC 3 and Razor apps, written a few jQuery plugins and have learned some knew enterprise tools such as SiteCore for content management. I’ve even taken a stab writing some simple Windows Phone Apps, although I got say, still a lot to learn on that technology. There’s also HTML5 and CSS3 to write about and what this means to web developers; so I don’t think I’ll be running out of topics in programming any time soon.

So stay tuned and keep in touch, and happy coding.