LavaBlast Software Blog

Help your franchise business get to the next level.
AddThis Feed Button

RESX file Web Editor

clock February 7, 2008 08:35 by author EtienneT

Source CodeDEMO

If you have a multi-language site, you have probably already worked with .resx files.  Resx files are resource files that can contain strings, images, sounds... pretty much anything.  However, in ASP.NET (at least in our typical scenarios), resource files mainly contain strings to be translated in multiple languages.  Those needing a refresher course on ASP.NET localization should take a peek at this article: ASP.NET Localization. Let us take a typical ASP.NET application as an example.  When you generate a resource file for a file named Default.aspx, VS.NET generates a new folder named App_LocalResources (if it doesn't exist) and it creates a new file named Default.aspx.resx in this folder.

image

This option will only be visible in the design view of an aspx or ascx file.  

Default.aspx.resx will contain all strings that can be localized from Default.aspx.  Default.aspx.resx is the default resource file for Default.aspx.  It will contain the default language strings, in our case English.  Should you need to offer the same application in more than one language, you will need to create locale-specific resource files with a similar filename. For example, Default.aspx.fr-CA.resx would be a resource file for Canadian French. The logic to retrieve a string from the appropriate resource file is built into the .NET framework (it depends on the current thread's culture).

Those who have built a dynamic web site supporting multiple languages know that managing resx files is a burden, especially when the application changes.  TeddyMountain.com is a web site we recently created which provides English, French, and Danish versions.  We use a CMS for most of the text, but some dynamic pages like the store locator contains text which needs to be in resources files.  We collaborated with a resident of Denmark to translate the website; the translator is not a programmer and could not be trusted with the XML-based resx files. Furthermore, as the site is in constant evolution, we wanted a dynamic solution to avoid losing time exchanging Excel files.

Although there are some commercial applications out there, we decided to make a a simple tool to unify a set of resource files.  We wanted to take advantage of the fact that the we were building a website and we could have the translator use a web-based tool to translate the website. This has the advantage of being able to see the changes immediately in-context, instead of simply translating text locally. We made a class that would merge our Default.aspx.resx, Default.aspx.fr-CA.resx, and Default.aspx.da.resx files into a single C# object that is easy to use.  Once the data is in the C# object, the data can be modified and saved back to the disk later on.

We called this C# object ResXUnified.  The only constructor to ResXUnified needs a path to a resx file.  Then it will find all resx files related to it from different languages.  Once you have constructed the object, you can access the data simply by using an indexor:

ResXUnified res = new ResXUnified("Default.aspx.resx");
string val = res["da"]["textbox1.Text"];

In the above code, we access the Danish language file and query the value of the "textbox1.Text" key.  This information can be changed and saved back to the disk:

res[""]["textbox1.Text"] = "Home"; // Default language we can pass an empty string
res["da"]["textbox1.Text"] = "Hjem"; // Danish
res["fr-CA"]["textbox1.Text"] = "Acceuil"; // French
res.Save();

When we call Save(), only the files that were changed will be written to the disk.  ResXUnified simply uses a Dictionary and a List to manage the keys and the languages.  To save, it uses the ResXResourceWriter class provided by the framework, which makes it easy to manipulate resx files. Similarly, we read resx files using the ResXResourceReader class.  Without these two classes, manipulating resx files would be much more complicated.

I won't include more code here since this is a pretty straightforward collection class.

Later, for the TeddyMountain.com website, we made a quick interface in ASP.NET (see the demo here) to display all the resx files in a project. We enable the user to add a language file and translate all the fields from the default language.  Here is an example:

image 

When a string needs to be translated, the textbox background color is not the same, this way it's easy for the translator to see what needs to be translated.

The generate local resource button in Visual Studio generates a lot of "useless" strings in resx files; we don't necessarily want to enter tooltips on every Label in our application. To make it easier to read, we designed an option to hide or show these empty strings.

This tool is pretty basic right now and there are more options that could be easily added.  For example, we could add a list of files that remain to be translated or allow for multiline strings (notice we don't support line-breaks in our strings). We encourage you to modify the code and show off your enhancements!

Final notes: If you try the code out, please remember to give write access to the ASP.NET worker process in all the App_LocalResources folders (and the App_GlobalResources folder if you use it). Also, since changing resx files for the default language restarts the web application, it is recommended you use the tool on a development copy of your website.  

Source CodeDEMO

kick it on DotNetKicks.com



CheckBoxList hover extender

clock January 22, 2008 09:16 by author JKealey

Summary

This article presents a CheckBoxList extender that enables the user to hover over individual checkboxes in the list and see a popup with additional information.  The information is populated dynamically (web service call) depending on the hovered checkbox' value.

[VIEW THE ONLINE DEMO]

[DOWNLOAD THE CODE] (469.00 kb)

Where could this be useful?

If you use the ManyToManyList in SubSonic, you know that it inherits from the built-in CheckBoxList control to display data from a many-to-many database relationship.  You probably also know that this can be really useful, but it has it's limitations.  We are using the SubSonic many-to-many list to display a list of stores that a user can be associated with.  You can display only one field of information by item with the ManyToManyList control for the foreign table associated in the relationship (unless you use views!).  Here is an example:

storesList

For a particular user, we are listing all possible stores and showing the store ID as a description for the items.  This can have a meaning for some users, but when a new user comes in and doesn't know the store identifiers, you have a problem. All franchise stores share the same store name as they do business under the same name - they can be uniquely identified by their franchisor-assigned ID or by their address. However, we don't want to show too much information in this control, as it would clutter the interface, in our system. We need a way to display more information by item, without taking up too much screen real estate.  

Our solution is very much inspired by the AjaxControlToolkit controls, such as the HoverMenu to display information when the user hovers over an item in our CheckBoxList (or SubSonic ManyToManyList).  We created an AJAX Control Toolkit Extender that asynchronously calls a web service method (or a page method) to obtain the information displayed in the popup control, when the user hovers over an item.  Here is an example of the result:

checkboxListhover

How to use it

You need a CheckBoxList, a panel to display the information, our extender, and a web service method to invoke.

<asp:CheckBoxList ID="CheckBoxList" runat="server">
    <asp:ListItem Text="Item #1" Value="1" />
    <asp:ListItem Text="Item #2" Value="2" />
    <asp:ListItem Text="Item #3" Value="3" />
    <asp:ListItem Text="Item #4" Value="4" />
    <asp:ListItem Text="Item #5" Value="5" />
</asp:CheckBoxList>

<asp:Panel ID="panelInfo" runat="server" CssClass="checkboxlisthover">
    <asp:Label ID="lblTest" runat="server" Text="Label"></asp:Label>
</asp:Panel>

<ajax:CheckboxListHoverExtender
id="checkboxlistext" runat="server"
TargetControlID="CheckBoxList"
PanelID="panelInfo"
DynamicServiceMethod="GetContent"
DynamicControlID="lblTest"
DynamicServicePath="~/CheckBoxList.asmx" />

The web service class should look something like this:

[ScriptService]
    public class CheckboxList : WebService
    {
        [WebMethod]
        [ScriptMethod]
        public string GetContent(string contextKey) { return "";}

    }

Implementation

A web service method was called with the value of the hovered checkbox.  When you DataBind the CheckBoxList, it is very important to assign a value to each of your ListItems.  In this example, each checkbox has a GUID value.  This GUID is passed as a parameter to the web service call automatically by the extender.  The popup panel is then filled with the information returned by the web service.

As stated previously, the CheckBoxListExtender control is very much inspired by the HoverMenu extender.  The two controls have similarities, but we can't use the HoverMenu directly in the CheckBoxList because we don't have access to the item template of a CheckBoxList.  This prevents us from using the built-in HoverMenu extender for each CheckBoxList item.

Coding a new extender

To code a new extender, you can use existing extenders to simplify your life: that's what we did for the CheckBoxListExtender.  It re-uses the HoverExtender and the PopupExtender.  Those two extenders are not in the sample page of the AjaxControlToolkit (we see the HoverMenuExtender and PopupControlExtender but not the two we are using here), but they are in the source code if you want to see them.  Basically when we coded the CheckBoxListExtender, we had to pass the scripts we wanted to depend on:

[RequiredScript(typeof(CommonToolkitScripts))]
[RequiredScript(typeof(HoverExtender))]
[RequiredScript(typeof(PopupExtender))]
[RequiredScript(typeof(AnimationExtender))]

[Designer(typeof(CheckboxListHoverDesigner))]
[ClientScriptResource("LavaBlast.AJAX.CheckboxListExtender.CheckboxListHoverBehavior", "LavaBlast.AJAX.CheckboxListExtender.CheckboxListHoverBehavior.js")]
[TargetControlType(typeof(CheckBoxList))]
public class CheckboxListHoverExtender : DynamicPopulateExtenderControlBase

As you can see, the extender inherits from DynamicPopulateExtenderControlBase.  This means that the extender can dynamically populate the control via a web service call and all the necessary plumbing is already in place. Specifying the scripts you depend on is as easy as using the RequiredScript attribute on your extender class.

JavaScript behavior

As for the JavaScript, for each "TD" inside our CheckBoxList control, we created a HoverBehavior (this is from the HoverExtender).  Each time the HoverBehavior events are fired, we can do something about them.  In this case, we simply activated the PopupBehavior to show the popup panel and call the web service method to populate the content.  As the value of each checkbox of the list is not contained in the DOM of the page, most probably a security feature of ASP.NET, you have to somehow pass this information from the server to the extender behavior.  Since we couldn't find a way to pass a list of values from the server to the behaviour using a generic List variable, we simply used a string of comma separated values.  Right now we're using this:

[ExtenderControlProperty]
[DefaultValue("")]
[Browsable(false)]
public string Values
{
    get { return GetPropertyValue("Values", ""); }
    set { SetPropertyValue("Values", value); }
}

But would much rather like to use the following: 

[ExtenderControlProperty]
[DefaultValue("")]
[Browsable(false)]
public List<string> Values
{
    get { return GetPropertyValue("Values", ""); }
    set { SetPropertyValue("Values", value); }
}

It appears generic lists are not supported, unless we are mistaken. If someone knows if this is possible, please leave us a comment on this post.

Don't forget to look at the online demo!

CheckBoxListHoverExtenderDemo.zip (469.00 kb)

kick it on DotNetKicks.com


Hybrid Accordion/TreeView Sitemap

clock December 20, 2007 09:14 by author EtienneT

Live Demo | Source Code (VS 2008) (856.65 kb)

The TreeView site map that we use in FranchiseBlast has become too long to fit reasonably the left panel of our application. We wanted something more compact that would be as simple to use and maintain as our current solution.

Matt Berseth gave us the idea of using an AjaxControlToolkit Accordion to achieve a nice look and feel for our sidebar. Our site map is automatically generated from our Web.sitemap file. (We use different *.sitemap files for each client; we needed something dynamic to cut down on maintenance time. We simply change which *.sitemap file the Web.config points to in our configuration generation scripts.) Furthermore, we also trim what is available in the sidebar according to user roles. I've added a reference to the appropriate web.config settings to achieve this behaviour below.

<siteMap defaultProvider="secureProvider">
<providers>
<add name="secureProvider" type="System.Web.XmlSiteMapProvider" siteMapFile="Web.sitemap" securityTrimmingEnabled="true"/>
</providers>
</siteMap>

Yesterday, I began coding a quick solution to our problem. I was inspired by the code in this post but ultimately I changed it a lot. We wanted all first level nodes in the Web.sitemap to be Accordion panes and all the other levels to be contained in TreeViews inside the parent pane.

We also highlight the current page in bold in the TreeView and display a different pane color to represent the current selection. Altogether, this is pretty simple stuff but hopefully it will help you avoid re-inventing the wheel.

Happy holidays to all!

 

Live Demo | Source Code (VS 2008) (856.65 kb)

kick it on DotNetKicks.com

 

 

 

 

 



In 2007, can we afford to refuse potential customers who don’t have JavaScript enabled?

clock November 3, 2007 15:04 by author JKealey

Traditionally, I was very conservative when it came to making use of JavaScript (and even CSS) in my projects. Years ago, I was spending horrendous amounts of time double checking my sites on various browsers, particularly Netscape 4.7. As a developer, I found it was a necessary evil to get the site to work on all browsers, and became quite good at it. I now use Microsoft Virtual PC  to test my websites.

AJAX

A decade after Netscape 4 was launched, I now find myself in a similar position with JavaScript. We need to decide if we can require our users to have JavaScript enabled.  We feel that when used properly, JavaScript can increase the site’s usability. We know that approximately 94% of web users have JavaScript enabled .  Looking at the trends, we can see that this number is rising. We also notice that an increasing amount of websites are using AJAX. However, the big players typically build two versions of their sites, allowing visitors without JavaScript to use their services.   

Maybe a better question would be Can we afford to refuse potential customers who don’t have JavaScript enabled? The answer depends on who your customers are. In the franchisor/franchisee relationship, you can impose stricter constraints on the franchisees, as you don’t have thousands of them. However, the situation is different with the retail customers, who are not technically savvy. They probably have outdated software or half-a-dozen of internet security/anti-virus/anti-spyware packages that compound the problem. (I’ll keep the discussion on the Internet’s culture of fear for another day).

Therefore, by convention, we’d err on the side of caution. This position is further reinforced by the fact that AOL’s browser has broken JavaScript support.  However, taking a deeper look into the economics of the software, we have decided to require JavaScript for one of soon to be released e-commerce sites. I am purposefully omitting many arguments for the sake of conciseness as everything is debatable.

-          Trends: We see an increasing number of sites requiring JavaScript. We’d rather design for the future than the past.  We feel JavaScript has reached its critical mass and can be used in production environments.

-          Return on investment: Given our development style and legacy code, we estimate we need to invest an additional 30% effort to properly support users without JavaScript. This is prohibitively expensive for our clients.

  • New businesses need to start making revenue immediately and hopefully can afford to implement a script-less version when (and if) the site becomes popular.  
  • Our POS (in operation in a controlled environment) uses AJAX profusely.  Re-use is very tempting.
  • The ASP.NET framework includes exceptional server-side controls that make use of JavaScript for postbacks.  We’d have to avoid a large number of useful components or re-implement them.
  • We could develop without script from the ground up (thus eliminating the problem completely), but we feel it limits our potential and usability.

-          We see search engine accessibility as an orthogonal issue. Even if we require JavaScript, we can easily create a few semi-static pages (at a negligible cost) that will be scanned by search engines.  

Still, even after the previous justifications, we are still torn. Concerning accessibility, our position is not justifiable. What “feels right” for us developers (and our customer’s pocket book) will have to be monitored in the coming months. Access logs will be reviewed and customer requests, tracked. We’ll review our decision in a few months and might end up reverting our position in the future.  Therefore, we shall remain conservative in our JavaScript usage, in case we need to review our software at a later date.

I suppose the morale of this story is to be open to change and experimentation.

kick it on DotNetKicks.com


Month List

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2017

Sign in