I’m just playing around, extending the “Markupbuilder” code in this article (I mentioned this in this blog entry). Its a complete code mess and I’d need to take it light years further (and clean up the naming of stuff) to actually do anything w/it (although, i’m slightly considering). Anyhow, I’ve got the concept baked down into a asp.net server control you just drop on your form and go with minimal coding required on the developer’s part. The control wires up the client-side event handler with the [wcf | asmx] web service via Asp.Net Ajax’s client-side Sys.Net.WebServiceProxy object. It also provides lots of events to hook into, as well as all the goodness in Dino’s article (header/item/footer templates, and the formatting event). His code didn’t work in FireFox with the way <xml/> tags were used. There were ways to make those work, but I wrap the data in <!–[CDATA tags. Steps to use are:
1. Drop a “DatabinderExtender” control on your form and set properties. Example of markup:
<%@ Register TagPrefix=”cc” Namespace=”ClientsideDatabinding” %>
<cc:DatabinderExtender
ID=”DataBinder1″
runat=”server” ScriptPath=”Javascript/ClientDataBinder.js”
TargetControlID=”peepsContainer” OnBeforeInvoke=”onBeforeInvoke” InvocationElementClientId=”button1″ InvocationEventName=”click”
ServiceProxyName=”CrazyServices.IService” ServiceProxyMethodName=”GetPeeps” ServiceProxyParameterRetrieverDelegateName=”gatherParams” HeaderTemplateClientId=”headerTemplate”
ItemTemplateClientId=”itemTemplate” FooterTemplateClientId=”footerTemplate” FormatterClientDelegateName=”makeDataPretty”
ErrorFunctionClientDelegateName=”displayPeepsError” NoDataClientDelegateName=”noDataFound” />
2. You’ll note the various events that you can trap on your client. For example, use “ServiceProxyParameterRetrieverDelegateName” (crapy name) for the method on your form to set the parameters prior to the Ajax call. It receives a javascript dictionary of parameter info for whatever service you configured on the DatabinderExtender (I use reflection on the server to inspect the configured Service and Method to snag information about the parameters, and bake that into the client-side control). For example, here is the “gatherParams” method assigned as the event handler:
function gatherParams(paramsDictionary) {
// Set via integer index
for(var i=0;i<paramsDictionary.length;i++){
if(paramsDictionary[i].name=="ageRangeLow")
{
paramsDictionary[i].value = parseInt($get('txtAgeLow').value);
}
// .....(others...)
}
// -- or --
// Set via string index
paramsDictionary["ageRangeLow"].value = parseInt($get('txtAgeLow').value);
paramsDictionary["ageRangeHigh"].value = parseInt($get('txtAgeHigh').value);
paramsDictionary["namePattern"].value = $get('txtName').value;
}
3. Other events allow for canceling the operation, formatting the results, or handling a scenario where no data was returned.
4. You also need to define your container (often a <div/> or similar), and the markup for the “Header”, “Item”, and “Footer” templates such as (this is all similar to how Dino had it in his article, except the use of [CDATA[ directives):
<span id=”headerTemplate” style=”visibility:hidden;“><!–[CDATA[<table cellpadding=”3″><thead><tr><th>Name</th><th>Age</th></tr></thead>]]–></span>
<span id=”itemTemplate” style=”visibility:hidden;“><!–[CDATA[<tr><td>#Name</td><td>#Age</td></tr>]]–></span>
<span id=”footerTemplate” style=”visibility:hidden;“ ><!–[CDATA[</table>]]–></span>
For the curious, here is the cheeseball code I threw together this evening which builds that client-side dictionary of parameter info:
private string AddParamsDefinitionDictionary()
{
StringBuilder result = new StringBuilder();
result.AppendLine("function (){");
result.AppendLine("var result = [];");
Type type = Type.GetType(this.ServiceProxyName);
MethodInfo methodInfo = type.GetMethod(this.ServiceProxyMethodName);
ParameterInfo[] parameters = methodInfo.GetParameters();
if (parameters == null || parameters.Count() <= 0)
{
return null;
}
const string ITEM_TEMPLATE = @"result[""PARAM_NAME""] =
eval('({""name"":""PARAM_NAME"",""value"":"""", ""type"":""TYPE_NAME""})');";
string item = string.Empty;
string typeName = string.Empty;
int counter = 0;
foreach (ParameterInfo parameterInfo in parameters)
{
item = ITEM_TEMPLATE.Replace("PARAM_NAME", parameterInfo.Name);
typeName = GetTypeName(parameterInfo);
item = item.Replace("TYPE_NAME", typeName);
result.AppendLine(item);
result.AppendLine(string.Format("result[{0}] = {1}", counter.ToString(), item));
counter++;
}
result.AppendLine("return result;");
result.AppendLine("}");
return result.ToString();
}
private string GetTypeName(ParameterInfo parameterInfo)
{
string result = string.Empty;
if (parameterInfo.ParameterType.Name.Contains(typeof(short).Name) ||
parameterInfo.ParameterType.Name.Contains(typeof(Int32).Name)
|| parameterInfo.ParameterType.Name.Contains(typeof(long).Name))
{
result = "number";
}
else if (parameterInfo.ParameterType.Name.Contains(typeof(bool).Name))
{
result = "boolean";
}
else if (parameterInfo.ParameterType.Name.Contains(typeof(object).Name))
{
result = "object";
}
else
{
result = "string";
}
return result;
}
Well, anyhow, just something i’m playing around with. It helps to build something to make some of the concepts sink in further. -J