The Imaginative Universal

Studies in Virtual Phenomenology -- @jamesashley

Atlanta .NET User Group

October 24
by James Ashley 24. October 2008 18:38

I will be presenting on "Working with new ASP.NET features in .NET Framework 3.5 Service Pack 1" at the Atlanta .NET User Group on Monday, October 27th.  Magenic will be providing refreshments, as usual.   The meeting will begin at 6:00 PM at Microsoft's Offices in Alpharetta.  It's a lot of material to pack into an hour long presentation, but I think I have a few good strategies for working with that.  The presentation will cover Dynamic Data, Entity Framework, Data Services, the Silverlight Media Control, the Ajax browser history feature built into the Script Manager, and Script Combining.  That gives me about 10 minutes per technology.  Whew.

Microsoft Corporation
1125 Sanctuary Pkwy.
Suite 300
Atlanta, GA 30004

Directions to Microsoft

Tags: , ,

.Net | Ajax | Recommended

Styling the Validator Callout Extender

September 04
by James Ashley 4. September 2008 02:13

The Validator Callout Extender is one of the controls included in the Ajax Control Toolkit.  It allows one to replace the normal appearance of an asp.net validation control with an attractive cutout.  For instance:

This is a required field.

A common problem concerns how to restyle the callout to give it a custom appearance.  The documentation for this control makes it clear that the default CSS class may be overridden in order to change the appearance of the callout. The Callout Extender has a CssClass property which may be set to a custom style.  Unfortunately, the documentation also includes an apparent error.  According to the documentation for the Validation Callout Extender:

If your CssClass does not provide values for any of those then it falls back to the default value.

This turns out not to be true.  Instead, one must override all the nested classes of the Callout Extender in order to get a reasonably good looking callout.  If any of the nested classes are not overridden, once a value has been assigned to the CssClass property, no style is applied to the nested style.

Consequently, in order to do something as simple as setting the back-color of the callout to Blue, the custom style will end up looking like this:

    <style type="text/css">
        .customCalloutStyle div, .customCalloutStyle td
        {
            border: solid 1px Black;
            background-color: Blue;
        }
        .customCalloutStyle .ajax__validatorcallout_popup_table
        {
            display: none;
            border: none;
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_popup_table_row
        {
            vertical-align: top;
            height: 100%;
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_cell
        {
            width: 20px;
            height: 100%;
            text-align: right;
            vertical-align: top;
            border: none;
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_table
        {
            height: 100%;
            border: none;
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_table_row
        {
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_arrow_cell
        {
            padding: 8px 0px 0px 0px;
            text-align: right;
            vertical-align: top;
            font-size: 1px;
            border: none;
            background-color: transparent;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_arrow_cell .ajax__validatorcallout_innerdiv
        {
            font-size: 1px;
            position: relative;
            left: 1px;
            border-bottom: none;
            border-right: none;
            border-left: none;
            width: 15px;
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_arrow_cell .ajax__validatorcallout_innerdiv div
        {
            height: 1px;
            overflow: hidden;
            border-top: none;
            border-bottom: none;
            border-right: none;
            padding: 0px;
            margin-left: auto;
        }
        .customCalloutStyle .ajax__validatorcallout_error_message_cell
        {
            font-family: Verdana;
            font-size: 10px;
            padding: 5px;
            border-right: none;
            border-left: none;
            width: 100%;
        }
        .customCalloutStyle .ajax__validatorcallout_icon_cell
        {
            width: 20px;
            padding: 5px;
            border-right: none;
        }
        .customCalloutStyle .ajax__validatorcallout_close_button_cell
        {
            vertical-align: top;
            padding: 0px;
            text-align: right;
            border-left: none;
        }
        .customCalloutStyle .ajax__validatorcallout_close_button_cell .ajax__validatorcallout_innerdiv
        {
            border: none;
            text-align: center;
            width: 10px;
            padding: 2px;
            cursor: pointer;
        }
    </style>

This will give you the following change in appearance:

This is a required field.

In order to play with the nested classes in order to customize the look of the callout, it would be useful to know what the HTML for the Validator Callout actually looks like.  Unfortunately, the callout is generated using client script, making it impossible to simply peek at the source in order to figure out what the markup looks like.  After about an hour of reading through the behavior class for the Callout Extender, I was finally able to come up with this:

        <table id="_popupTable" cellpadding="0" cellspacing="0" border="0" class="customCalloutStyle">
            <tbody>
                <tr class="ajax__validatorcallout_popup_table_row">
                    <td class="ajax__validatorcallout_callout_cell">
                        <table width="200px" cellpadding="0" cellspacing="0" border="0" 
                            class="ajax__validatorcallout_callout_table">
                            <tbody>
                                <tr class="ajax__validatorcallout_callout_table_row">
                                    <td class="ajax__validatorcallout_callout_arrow_cell">
                                        <div class="ajax__validatorcallout_innerdiv">
                                            <div style="width: 14px">
                                            </div>
                                            <div style="width: 13px">
                                            </div>
                                            <div style="width: 12px">
                                            </div>
                                            <div style="width: 11px">
                                            </div>
                                            <div style="width: 10px">
                                            </div>
                                            <div style="width: 9px">
                                            </div>
                                            <div style="width: 8px">
                                            </div>
                                            <div style="width: 7px">
                                            </div>
                                            <div style="width: 6px">
                                            </div>
                                            <div style="width: 5px">
                                            </div>
                                            <div style="width: 4px">
                                            </div>
                                            <div style="width: 3px">
                                            </div>
                                            <div style="width: 2px">
                                            </div>
                                            <div style="width: 1px">
                                            </div>
                                        </div>
                                    </td>
                                    <td class="ajax__validatorcallout_icon_cell">
                                        <img alt="" border="0" src="alert-large.gif" />
                                    </td>
                                    <td class="ajax__validatorcallout_error_message_cell">
                                        This is a required field.
                                    </td>
                                    <td class="ajax__validatorcallout_close_button_cell">
                                        <div class="ajax__validatorcallout_innerdiv">
                                            <img alt="" border="0" src="close.gif" />
                                        </div>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>
            </tbody>
        </table>
 

This probably isn't completely correct, but it's pretty close.  If you want to try to write your own custom style for the Validator Callout Extender, just copy the markup above, as well as the CSS class above that, into your favorite WYSIWYG editor and have at it.  At a minimum, you should be able to customize the colors either to match your site design (the default background color is Chiffon Yellow, after all), or to indicate different sorts of validation errors.

One question I've seen on the ASP.NET Forums concerns whether it is possible to switch the arrow that flies off the left side of the callout to the right.  Unfortunately, the arrow is actually coded into the HTML generated by the Validator Callout, and is not controlled by CSS styles.  It would be nice to see this as a feature of future releases of the Ajax Control Toolkit.

Tags: ,

.Net | Ajax

ASP.NET AJAX: Script Globalization without the ScriptManager

August 21
by James Ashley 21. August 2008 00:07

bigfoot

I just spent about four hours trying to solve a problem I'll probably never actually encounter in the wild.  Someone posted on the asp.net forums about script globalization using ASP.NET AJAX.  This can be set up pretty easily by simply using the ScriptManager control and setting its EnableScriptGlobalization property to true.  ScriptManager takes care of importing the necessary scripts and handling all the underlying code required to make things work.

What the seeker on the forums wanted to know, however, was whether this could be accomplished without the ScriptManager.  In theory, all the ASP.NET AJAX framework scripts can be downloaded and used in a non-platform dependent manner.

Certain software problems are ubiquitous, and people tend to fall over themselves blogging and posting about them until Microsoft or some other vendor eventually either fixes the problem and it goes away.  On the other hand, there are problems in the software bestiary that are so rare that working on them is the programming equivalent of doing cryptozoology.

This was a juicy problem of that sort.  And I believe I have a solution. It basically involves redoing some of what the ScriptManager does automatically, but what the hey.

To support globalization in ecmascript, the ScriptManager basically sets a variable called __cultureInfo behind the scenes.  This is then used by various ASP.NET AJAX script methods to provide formatting information.  This is actually explained in the Microsoft documentation for the feature.

Behind the scenes, it seems clear, the ScriptManager is querying the CurrentUICulture of the current thread in order to determine the browser's preferred language, and passing this to the __cultureInfo variable.  The trick, then, is to determine the format of the culture data passed to __cultureInfo.

Here I had to use reflection to discover that there is an internal ClientCultureInfo type in the Sys.Web.Extensions assembly.  It made sense that this was being serialized and passed to the __cultureInfo variable.  With some trial and error, I finally got the serialization correct.

To make this work, you will need to download the Ajax Framework Library, which is a collection of javascript files.  You will need to import the MicrosoftAjax.js file into your web project, and then reference it in your page, like this:

<script src="MicrosoftAjax.js" type="text/javascript"></script>

You will also need to create the ClientCultureInfo class for your project, and make sure that it is serializable.  I tried DataContractJsonSerializer class to do my serializing,  but had problem getting the DateTimeFormatInfo type to serialize correctly, so I finally opted to use the now obsolete JavaScriptSerializer.  Here's what the class looks like:

    public class ClientCultureInfo

    {

 

        public string name;

        public DateTimeFormatInfo dateTimeFormat;

        public NumberFormatInfo numberFormat;

 

        public ClientCultureInfo(CultureInfo cultureInfo)

        {

            this.name = cultureInfo.Name;

            this.numberFormat = cultureInfo.NumberFormat;

            this.dateTimeFormat = cultureInfo.DateTimeFormat;

        }

 

        public static string SerializedCulture(ClientCultureInfo info)

        {

            JavaScriptSerializer js = new JavaScriptSerializer();

            return js.Serialize(info);

 

        }

 

    }

Now that we have the culture info formatted correctly, we need to be able to pass it to the client variable.  We'll create a public property in the code-behind that is accessible from the client, and set its value in the Page.Load event handler:

 

        protected void Page_Load(object sender, EventArgs e)

        {

            if (!Page.IsPostBack)

            {

                CultureInfo cultureInfo = System.Threading.Thread.CurrentThread.CurrentUICulture;

                ClientCultureInfo o = new ClientCultureInfo(cultureInfo);

                SerializedCulture = ClientCultureInfo.SerializedCulture(o);

            }

 

        }

 

        public string SerializedCulture

        {

            set

            {

                ViewState["kultur"] = value;

            }

            get

            {

                if (ViewState["kultur"] == null)

                    return string.Empty;

                else

                    return ViewState["kultur"].ToString();

            }

        }

Finally, we  need to use this code-behind property to set __cultureInfo.  Just add the following script block to your markup in order to set the current culture:

 

<script type="text/javascript">
    // Get the name field of the CurrentCulture object   
    var __cultureInfo = '<%= this.SerializedCulture %>';
    Sys.CultureInfo.CurrentCulture = Sys.CultureInfo._parse(__cultureInfo);
</script>

 

I added this to the bottom of the page.  To test whether this works, you will need to set the language property of your browser (if you are using IE, like I am).  I set mine to French for testing.  Then append the following script block below the one setting the culture above:

<script type="text/javascript">
    var currentCultureInfoObj = Sys.CultureInfo.CurrentCulture;
    var d = new Date();
    alert("Current culture is " + currentCultureInfoObj.name 
    + " and today is " 
    + d.localeFormat('dddd, dd MMMM yyyy HH:mm:ss'));  
</script> 

 

If you encounter any problems, it is possible that the web.config file is overriding the culture information from the browser.  In that case, make sure the culture is set to "auto" in the config file, like this:

 

    <system.web>

        <globalization uiCulture="auto" culture="auto" />

    </system.web>

Tags: , ,

.Net | Ajax | C#

BlogRoll

Download OPML file OPML