Ian (Fluxtah) Warwick's blog RSS 2.0
# Thursday, November 06, 2008

At work I was implementing a search form that required a 3 tier drop down, so I thought the CascadingDropDown would be perfect for this.

It turns out that the CascadingDropDown was not quite right for the requirements, the last drop down in the 3 tiers had to be enabled and items had to be selectable but also had to filter the other 2 upper tiers.

After some thought, the only way to achieve this was having the ability to programatically add my own values to be passed to the knownCategoryValues argument of the web service method for a CascadingDropDown behavior.

To do this, I had to redefine a method on the CascadingDropDown behavior, I could then invoke this method to force the ajax callback for any CascadingDropDown of my choice. Here is that method.

AjaxControlToolkit.CascadingDropDownBehavior.prototype._onParentChange2 = function(evt, inInit, moreKnownCatValues) {
     /// <summary>
     /// Handler for the parent drop down's change event
     /// </summary>
     /// <param name="evt" type="Object">
     /// Set by the browser when called as an event handler (unused here)
     /// </param>
     /// <param name="inInit" type="Boolean">
     /// Whether this is being called from the initialize method
     /// </param>
     /// <returns />

     var e = this.get_element();

     // Create the known category/value pairs string for sending to the helper web service
     // Follow parent pointers so that the complete state can be sent
     // Format: 'name1:value1;name2:value2;...'
     var knownCategoryValues = '';
     var parentControlID = this._parentControlID;
     while (parentControlID) {
         var parentElement = $get(parentControlID);
         if (parentElement && (-1 != parentElement.selectedIndex)) {
             var selectedValue = parentElement.options[parentElement.selectedIndex].value;

             if (selectedValue && selectedValue != "") {
                 knownCategoryValues = parentElement.CascadingDropDownCategory + ':' + selectedValue + ';' + knownCategoryValues;
                 parentControlID = parentElement.CascadingDropDownParentControlID;
                 continue;
             }
         }
         break;
     }

     if (knownCategoryValues != '' && this._lastParentValues == knownCategoryValues) {
         return;
     }

     this._lastParentValues = knownCategoryValues;

     // we have a parent but it doesn't have a valid value
     //
     if (knownCategoryValues == '' && this._parentControlID) {
         this._setOptions(null, inInit);
         return;
     }

     // Show the loading text (if any)
     this._setOptions(null, inInit, true);

     if (this._servicePath && this._serviceMethod) {
         // Raise the populating event and optionally cancel the web service invocation
         var eventArgs = new Sys.CancelEventArgs();
         this.raisePopulating(eventArgs);
         if (eventArgs.get_cancel()) {
             return;
         }
         if (moreKnownCatValues) {
             knownCategoryValues += ";" + moreKnownCatValues;
         }
         // Create the service parameters and optionally add the context parameter
         // (thereby determining which method signature we're expecting...)
         var params = { knownCategoryValues: knownCategoryValues, category: this._category };
         if (this._useContextKey) {
             params.contextKey = this._contextKey;
         }

         // Call the helper web service
         Sys.Net.WebServiceProxy.invoke(this._servicePath, this._serviceMethod, false, params,
            Function.createDelegate(this, this._onMethodComplete), Function.createDelegate(this, this._onMethodError));
         $common.updateFormToRefreshATDeviceBuffer();
     }
 }

The method above is just a copy of _onParentChange method of the CascadingDropDown behavior, I called it _onParentChange2 and call this one when I want to invoke an ajax callback, the only difference is the extra argument moreKnownCatValues, the value of this argument will be appended to the knownCategoryValues which will get passed to the callback method. The changes to the original method have been bolded.

Now its possible to invoke a callback on any CascadingDropDown with the code below. 

$find('CDD1')._onParentChange2(null, false, "CountryId:25");

The line above will get a reference to a CascadingDropDown behaviour that I have given a BehaviorID of CDD1, it will then invoke the new _onParentChange2 method passing through my extra value of CountryId:25.

Its a shame that the CascadingDropDown does not have a more elegant approach for adding to the knownCategoryValues programatically, but at least this workaround is not to hacky. 

 

Thursday, November 06, 2008 1:52:00 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] -
ASP.NET
# Thursday, October 09, 2008

I am quite impressed with IE8 beta 2, annoying bugs aside. One feature of IE8 that is very useful is accelerators, I decided to give it a shot today to see if I could get one working and its surprisingly easy.

You can create an accelerator by writing an xml document in the OpenService Format Specification for Accelerators and making the file accessible from a website, then creating a button with an onclick handler that calls window.external.AddService(url_to_xml_file).

As a quick test I created an xml file to describe an accelerator for my blog similar to the following.

<?xml version="1.0" encoding="UTF-8"?>
<os:openServiceDescription
    xmlns:os="http://www.microsoft.com/schemas/openservicedescription/1.0">
    <os:homepageUrl>http://www.yoursite.com</os:homepageUrl>
    <os:display>
        <os:name>Blog with your site</os:name>
        <os:icon>http://www.yoursite.com/favicon.ico</os:icon>
        <os:description>Blog on your site easily</os:description>
    </os:display>
    <os:activity category="Blog">
        <os:activityAction context="selection">
            <os:execute action="http://www.yoursite.com/blog/add_entry.aspx" method="get">
                <os:parameter name="title" value="{selection}" type="text" />
            </os:execute>
        </os:activityAction>
    </os:activity>
</os:openServiceDescription>

The most notable tags are described below:-

  • <os:activityAction context="selection">
    The value 'selection' of the context attribute of this tag specifies that this accelerator will be available when the user selects a block of text, there are two other possible options:-

    'document' - makes the accelerator available for the whole document.
    'link' - makes the accelerator available when the user selects a link.

  • <os:execute action="http://www.yoursite.com/blog/add_entry.aspx" method="get">
    The action of the execute tag does the business of redirecting to, or in this case, accelerating to the add entry page of the blog service, we can do this via a http GET or POST as described by the method attribute.

  • <os:parameter name="title" value="{selection}" type="text" />
    You can add a collection of these parameters that will be either sent as GET or POST vars, depending what you set for the method of the execute tag above, {selection} is a placeholder for information that is retrieved from the page that we are accelerating from, there are many possible options here which I will not list as they are available on msdn accelerator docs.

Once this xml document is ready and uploaded all that is needed is a link to it from a html document like the one below.

<html>
    <head><title>My Blog Accelerator</title></head>
    <body>
        <button id="installButton" onclick="window.external.AddService('my_accelerator.xml');">Add My Blog Accelerator</button>
    </body>
</html>

The example above would require the html document to be in the same path as my_accelerator.xml. 

And thats all there is to it, the documentation for developing accelerators can be found on msdn. 

Thursday, October 09, 2008 3:52:00 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Internet Explorer 8
# Wednesday, October 08, 2008

I've created a new Ajax Snippets demo video, like my last one, its very rough but I hope I communicate the general idea of the project effectively.

The video covers the new SnippetCallback wizard and best guess parameter mapping. 

Watch the demonstration video

Download the source code from the video

Wednesday, October 08, 2008 4:34:00 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
ASP.NET

I just released my latest updates to the Ajax Snippets project, the following notes summarise whats new.

ADDED

  • SnippetCallback now has an action list item, 'Javascript function stubs to clipboard'
  • SnippetCallback now has a designer action list option to configure the callback through a wizard
  • Ability to override default value extractors and add new ones
  • The Map parameters wizard step of the SnippetCallback wizard now best guesses the mappings by matching the name of the parameter to a control id
  • Snippets can now be used as ordinary web user controls where they can be dropped straight on a page with the advantage of SnippetCallbacks

FIXED

  • Javascript function stubs to clipboard feature was creating incorrect javascript
  • SnippetCallback wizard was not saving WebServiceVirtualPath property
  • Snippets now throw the correct exception if errors occur whilst retrieving them from a web service
  • SnippetManager was not managing css references correctly
Wednesday, October 08, 2008 2:29:00 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
ASP.NET
# Monday, October 06, 2008

I almost wrote off the idea that this was even remotely possible until I looked into it more on msdn and found an example.

/// <reference name "Ajax.js" assembly="System.Web.Extensions, ..." /> 

After doctoring it for my own script and assembly it was not working after trying several different renditions and wondering if it would ever work with the missing = in the name attribute and what the hell the ... was, I finally came to something that worked.
/// <reference name="AjaxSnippets.SnippetManager.js" assembly="AjaxSnippets" />

SnippetManager.js is the file in my AjaxSnippets.dll.

The only snag is that I have not been able to get it to work in a script block inside an aspx page, but it does work in a js file. 

Monday, October 06, 2008 6:47:00 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
ASP.NET

I just checked out the 1541 Ultimate website to see the status of the second batch and was surprised to find a nice video of the batch in production.

I am lucky enough to be receiving one of these cards that will bring sd cards and ethernet to my C64.

Monday, October 06, 2008 12:05:00 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
C64
# Sunday, October 05, 2008

In an SnippetCallback, when defining mapping between a web service method parameter and a control, there is a client-side function for each control type that deals with extracting the value from the control and passing it as the web service methods parameter.

By default most of the simple web controls (TextBox, Checkbox, RadioButtonList, etc) that come out-of-the-box with ASP.NET already have a corresponding client-side value extractor function defined, but obviously this is not good enough if you want to use custom controls.

To override these or add new ones there are two things that need to be done, first off a new extractor function would have to be defined in configuration.

<configSections>
  <section name="ajaxSnippets" type="AjaxSnippets.Configuration.AjaxSnippetsConfigurationSection, AjaxSnippets"/>
</configSections>
<ajaxSnippets>
  <extractors>
    <add controlType="System.Web.UI.WebControls.TextBox" functionName="myCustomTextBoxExtractor" />
    <add controlType="My.Custom.Control" functionName="fromMyCustomControl" />
  </extractors>
</ajaxSnippets>

In this example, I am overriding the textbox extractor with my own function myCustomTextBoxExtractor, secondly I am defining a new value extractor fromMyCustomControl for My.Custom.Control.

Now the next thing that needs to be done is to write the client functions, this can be done by creating a new javascript file, which would need to be included in the page that Ajax Snippets are being used.

AjaxSnippets.ValueExtractors.prototype.myCustomTextBoxExtractor = function(id){
 var el = $get(id);
 // Extract value from el and return it here 
}
AjaxSnippets.ValueExtractors.prototype.fromMyCustomControl = function(id){
 var el = $get(id);
 // Extract value from el and return it here 
}

Ajax Snippets has a ValueExtractors class, and its simply just a job of adding functions to its prototype.

To give a better example, the ValueExtractors default prototype is below in full, in its current state from Ajax Snippets 0.3a release.

AjaxSnippets.ValueExtractors.prototype = {
    fromAny: function(id) {
        return id;
    },
    fromTextBox: function(id) {
        return $get(id).value;
    },
    fromDropDown: function(id) {
        var el = $get(id);
        return el.options[el.selectedIndex].value;
    },
    fromRadioList: function(id) {
        var i, els = $get(id).getElementsByTagName('input');
        for (i = 0; i < els.length; i++) {
            if (els[i].checked) { return els[i].value; }
        }
        return null;
    },
    fromCheckList: function(id) {
        var i, els = $get(id).getElementsByTagName('input'), vals = new Array();
        for (i = 0; i < els.length; i++) {
            vals.push(els[i].checked);
        }
        return vals;
    },
    fromListBox: function(id) {
        var i, el = $get(id), vals = new Array();
        for (i = 0; i < el.options.length; i++) {
            if (el.options[i].selected) { vals.push(el.options[i].value); }
        }
        return vals;
    },
    fromCheckBox: function(id) {
        return $get(id).checked;
    }
};
Sunday, October 05, 2008 5:19:00 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
ASP.NET | General

After a busy weekend I finally got a wizard working to help with the configuration of a SnippetCallback. In the latest build of Ajax Snippets there is now an option from the action list of a SnippetCallback on the designer named 'Configure...'.

After clicking this option a wizard will popup where an asmx file can be selected and a web method from this service.

One problem with this is that the web application must be compiled before the wizard can get access to the web service method list, the wizard basically pulls out web service type defined by the class attribute in the asmx file and tries to use Type.GetType() on it, if the web application is not compiled with asmx file then the methods do not show up in the wizards list, but I did stick in a red warning label under the drop-down-list if this happens.

The next step allows the mapping of controls to the selected web methods parameters.

The main benefit of the wizard is it avoids any mapping mistakes and typos, it could also do with a page to configure other options of a SnippetCallback but at least these two steps cover the main options.

 

Sunday, October 05, 2008 4:41:00 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
General
# Saturday, October 04, 2008

All last night till 2am and when I could find the time today I have been having some great fun with developing a configuration wizard for a SnippetCallback control that is part of my Ajax Snippets project.

I wanted the user to select a webservice and then select a method from that service, but there did not seem to be an easy way to get at the web projects files during design-time, at least thats what I initially thought.

It turns out that there is an IWebApplication interface that can be got at by using the GetService method of a component sited in design mode, this then allows you to get at a project item. In my case I needed to get at the asmx file and pull out the type name of its associated code-behind file so I could get a list of methods.

This is a very simple example of how to get at project files physical path on disk from a control designer

[code:c#]
    public class MyDesigner : ControlDesigner
    {
        public void DoSomethingWithProjectItemExample()
        {
            IWebApplication app = (IWebApplication)Component.Site.GetService(typeof(IWebApplication));
            IProjectItem item = app.GetProjectItemFromUrl("~/Default.aspx");
            string physicalPath = item.PhysicalPath;
            // do something with file
        }
    }
[/code]

 

Saturday, October 04, 2008 4:02:00 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] -

# Friday, October 03, 2008

After creating a rough guide to Ajax Snippets video it brought a few things to light, one thing was the awkwardness of having to define callback functions in a SnippetCallback and then having to write them out in the page that hosts the snippet.

I added a feature that allows the developer to select an option from the SnippetCallback controls action list that will copy some javascript function stubs into the clipboard based on the controls settings.

 

The relay snippets example will copy the following into the clipboard

[code:js]
function beforeRelayFields(){
}
function relayFieldsSuccess(result){
}
function relayFieldsFailed(error){
}
[/code]

This may have been an overkill feature but still its good for forgetful people like me that could do with some extra help.

Friday, October 03, 2008 4:00:00 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] -

Archive
<November 2008>
SunMonTueWedThuFriSat
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456
Blogroll
About the author/Disclaimer

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

© Copyright 2012
Ian Warwick
Sign In
Statistics
Total Posts: 33
This Year: 0
This Month: 0
This Week: 0
Comments: 4
Themes
Pick a theme:
All Content © 2012, Ian Warwick
DasBlog theme 'Business' created by Christoph De Baene (delarou)