Using resource files in embedded user controls in skins

If you are editing ASCX skins, then you have probably been tempted to introduce a bit of reusability by creating custom user controls and embedding them to your skins. These can actually function like skin objects, but without all the fuss. I’ve found it easy to create a couple of those in cases where I may need, for example, a common footer for all my skins, but of course you can do a great deal more with them, like introduce functionality without having to copy all the code to every skin file you make.


In any case, you have to register your control inside your skin file like this (names of folders, portal and skin path and tagprefix/name are arbitrary):


<%@ Register TagPrefix="MyCompany" TagName="FOOTER" src="~/Portals/0/Skins/myskin/myskinobjectsfolder/footer.ascx" %>


And use it in your skin file like this:


<MyCompany:FOOTER id="myFooterId" runat="server" />


The question is: How do you localize these little bastards? Even if you add DNN controls on them, the ResourceKey property continues to get data only from the skin’s resource file, not the control’s.


Of course, you can use your skin’s resource file to get localized strings and other data but this isn’t very convenient since you’ll have to add data to every resource file corresponding to each one of your skins. And I’m not going to talk about app_globalresources either! :)


(If you don’t know that you can have resource files for your skins, yes, you can. You can have a skins/app_localresources folder and you can have .resx files there  and you can use the ResourceKey attribute in your DNN control tags to get localized data for your controls in your skins).

Well, based on this excellent post here about localizing embedded controls, I thought it would be good to summarize and simplify the process a bit. So here it goes:


You can give an embedded user control its own resource file by following two different approaches:


1. Make it inherit PortalModuleBase

2. Specify the resource file yourself and make the control use it.


Approach 1: Inheriting PortalModuleBase

You just have to add:




in your control’s header.



<%@ Control language="vb" AutoEventWireup="false" Explicit="True" Inherits="DotNetNuke.Entities.Modules.PortalModuleBase" %>


Doing so will give you access to the LocalResourceFile property, which will in turn point to the .resx file located in the app_localresources folder right under your control’s location.


But what .resx file, exactly? Not the one you expected.


The resx file will NOT follow the file name of your user control, but the ID by which it is called. If your control’s filename is MyFooter.ascx, and you call it with an ID of “myFooterId” then your resource file must be named “myFooterId.ascx.resx”.

Then, you can use code like this to get a localized value, for example, for the “Footer.Text” key:


<%=DotNetNuke.Services.Localization.Localization.GetString("Footer.Text", LocalResourceFile)


Ugly, because you have to remember to always use the same ID and also because you can’t use two instances of your control in the same skin. Also, inheriting PortalModuleBase restricts your coding flexibility.


Approach 2: Forcing the user control to use a resource file


If you don’t want your user control to inherit PortalModuleBase but you still need it to have its own resource files, you can define a LocalResourceFile property yourself. This is written directly in the .ASCX file but, of course, you can put it in code-behind too. (sorry about the line breaks):


<script runat="server">
Public ReadOnly Property LocalResourceFile As String
         Return Me.TemplateSourceDirectory & _
     "/" & _
DotNetNuke.Services.Localization.Localization.LocalResourceDirectory & _
     "/" & _
     End Get
End Property

Public Function GetLocalizedString ( _

ByVal key as string) as string
    Dim retVal as String
    retVal = _
DotNetNuke.Services.Localization.Localization.GetString( _
    key, LocalResourceFile)
    return (retVal)
End Function



What we did here was to create our own LocalResourceFile property. This tells the control to use a resource file that follows the file name of the control itself. So, if your control is named footer.ascx, your resource file will lie in app_localresources\footer.ascx.resx. If you need to specify a differently – named resource file, you just replace the GetFileNameWithoutExtension call with your own string. E.g. if you use “myownFooter” there, then there must be a myownFooter.ascx.resx file in the app_localresources folder.


In addition, we created a GetLocalizedString function which will just take the key and return the string from the resource file.

We can use this function in our user control to get localized data as follows:




This is much simpler and much more flexible.



How to quickly switch your "localhost" binding to another dnn site

Warning: This is for advanced users only.


The situation

  • You've got a development machine which has more than one DNN sites that you are currently working on.
  • You're using host headers on those sites to have multiple root-level DNN sites. (And you know the terms "host header" and "binding")
  • You are using IIS7 or higher with Windows 7 or Windows 2008. (I suspect it may work on Vista too, but who's still got Vista? :) ).

(Don't continue reading if the above don't match your case. One of the most common reasons for the above scenario could be that there are root-level urls inside your DNN site, generated by custom modules or just hard-coded into skins / modules, so you have to make your development PC believe that this is a root-level site and not a virtual directory of your default web site. Or, even worse, if you're working on a copy of a site that's already online and there are links and other stuff pointing to the WHOLE domain, you may have to alter your hosts file to make your PC believe that your local site is running on the exact same domain. If you're dealing with DNN a lot, you've probably seen that we don't live in a perfect world and such cases do exist.)



The problem

You need to configure some modules on your installations, but the modules you have installed can run without limits only on "localhost". Furthermore, some modules, (for example, IndooGrid) need you to buy a licence if you run them ANYWHERE else than localhost - even if it's a test / development machine. But you don't want to buy additional licences since you can do your development work on localhost.


What you need

You need a way to quickly switch between what you see when you type "localhost" on your PC's browser, so that you can work on the site of your choice each time.


Of course, you can do it by altering your sites' host headers in IIS but that wouldn't be quick. And, I won't say a word about using different virtual machines, each with one localhost site - I've seen it happen. :)


The solution

Let's suppose I've got two sites, let's say Site1 and Site2. These listen to their respective host headers, let's say site1.mypc.local and site2.mypc.local. You have configured your Site1 to listen to "localhost"  too. Currently, when you type "localhost" on your browser you see Site1.


Now, you either have configured your IIS to have an extra host header (*:80) for Site 1 or you have changed the location of your "default web site" to point to Site1's folder on your hard disk.


Do the following:


1. STOP your default web site (if that's what you're using to do the trick).


2. Go to all the sites that you need to implement the "switching" on, and add a portal alias for localhost so that they can respond to the call (if there is not one already). If you do so, an IISRESET would be good too, in order to avoid the dreaded "redirect loop" error that can happen from time to time.


3. Create a batch file with the following and save it as changehost.bat (or any name you choose):


@echo off

if "%1" == "site1" goto site1
if "%1" == "site2" goto site2
if "%1" == "" goto error

%windir%\System32\inetsrv\appcmd set site /site.name:site2 /-bindings.[protocol='http',bindingInformation='*:80:']
%windir%\System32\inetsrv\appcmd set site /site.name:site1 /+bindings.[protocol='http',bindingInformation='*:80:']
goto end

%windir%\System32\inetsrv\appcmd set site /site.name:site1 /-bindings.[protocol='http',bindingInformation='*:80:']
%windir%\System32\inetsrv\appcmd set site /site.name:site2 /+bindings.[protocol='http',bindingInformation='*:80:']
goto end

@echo You have to provide a parameter!


(Sorry for the wrapping, each call to appcmd should be on a single line.)


Let's explain what this batch file does. You call it like this (make sure you have administrator rights):


changehost site2


And you expect to see your "site2" responding to localhost instead of "site1".


What the batch file does is go to the matching section of the batch code, following the IFs. When it gets there, it does two things:


First, it uses appcmd.exe (an utility located in your windows directory\system32\inetsrv folder, which allows you to access several IIS properties from the command line) to add a binding to localhost (*:80) to the site you need and REMOVE this binding from any other of your sites that possibly has it. Essentially, it does what you would do by hand - go to iis, add a binding to the site of your choice, remove the binding from the previous site since you are not allowed to have two sites with the same host header.


The result is that you will always have one and only one site bound to localhost, and that you can change what site this is by just executing your batch file.


Some things to have in mind:


The parameter's values can be anything you want, as long as there is a section inside the batch file you can GOTO if you match a parameter value. I've used the same names as the names of the sites in order to have some consistency, but this isn't needed. "Foo" and "Poo" would do the same job, as long as there was a "Foo" section for site1 and a "poo" section for site2.


The /site.name switch needs the actual site's name as it is declared in IIS.


You must FIRST remove any possible localhost bindings (/-bindings switch) and add the localhost binding (*:80) last (/+bindings switch), otherwise you'll get an error and the binding won't be added because it will exist on another site.


If a site does not have a localhost binding, you'll get a message that it can't be found, like this one:


ERROR ( message:Cannot find requested collection element. )

Don't be alarmed, it's normal. Appcmd just tried to remove a non-existent binding, no problem.


If you try to run this script twice for the same site, you'll also get an error like this:


Cannot add duplicate collection entry of type 'binding' with combined key attributes 'protocol,bindingInformation' respectively set to 'http, *:80:'. )


This means that you tried to add a binding to *:80 to the site that already had it. That's perfectly normal, too, nothing to worry about, no changes will be made.


The example is for two sites, if you have three you'll have to adjust the number of sections, your IF statements and your appcmd calls accordingly. Remember, we're doing a simple thing: Attempting to remove localhost bindings from sites that may have it (only one will) and add a localhost binding to the site we want.


I hope this helps a bit. It worked for me, if anyone tries it, please let me know if you have succeeded.


Standard disclaimer / warning: Even if you are an advanced user, please use this information at your own risk. It's easy to mess things up (actually, the only thing you'll mess up is your bindings - you won't lose any data but it's enough to make your development sites unaccessible if you don't know what you're doing). I cannot be held responsible if any of the above information proves misleading or incorrect.


Happy localhosting! :)

Related Posts with Thumbnails

Recent Comments

Free DotNetNuke Stuff

Free DotNet Videos

  © Blogger template The Professional Template by Ourblogtemplates.com 2008

Back to TOP