Wednesday, March 21, 2012

Use Custom Objects as Extender Control Properties - Part II

I thought this post -http://pietschsoft.com/Blog/Post.aspx?PostID=1377 - was very useful in further developing a custom ajax control extender. Thanks to Chris Pietschmann for posting his work.

I expanded his work to override the ConvertFrom method to JSON deserialize a string back to the custom object. In this example, I am using a Person object. Please refer to Chris's article for code and adapt the code with the changes I made below in bold italics:

GenericTypeConverter class:

namespace SandboxClass{public class GenericTypeConverter<T> : TypeConverter {public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,object value, Type destinationType) { JavaScriptSerializer jss =new JavaScriptSerializer();string s = jss.Serialize(value);return s; }public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,object value) {if (value is string) { JavaScriptSerializer jss =new JavaScriptSerializer(); T t = jss.Deserialize((string)value);return t; }else return base.ConvertFrom(context, culture,value); } }}
Person class:
namespace SandboxClass{ [TypeConverter(typeof(GenericTypeConverter<Person>))]public class Person {private string _FirstName;public string FirstName {get {return _FirstName; }set { _FirstName =value; } }private string _LastName;public string LastName {get {return _LastName; }set { _LastName =value; } } }}
 
Default.aspx
 
public partialclass _Default : System.Web.UI.Page{protected void Page_Load(object sender, EventArgs e) {if (!Page.IsPostBack) { // serialize object to string to pass to client SandboxClass.Person person =new SandboxClass.Person(); person.FirstName ="John"; person.LastName ="Doe"; ajaxControlExtender.MyPerson = person; }else { // deserialize object back from client to your custom object, in this example, the Person object SandboxClass.GenericTypeConverter gc = System.ComponentModel.TypeDescriptor.GetConverter(typeof(SandboxClass.Person))as SandboxClass.GenericTypeConverter; SandboxClass.Person person = (SandboxClass.Person)gc.ConvertFromString(ajaxControlExtender.ClientState); Label2.Text = person.FirstName +" " + person.LastName; } }}
 
Also, in addition to step 5 of Chris's post and after manipulating the object on client-side, I am assigning the object to the ajax control extender ClientState property
to maintain state back to the server so the deserialization process can perform. Below is an example to include in the ajax control extender js file:
 
this._MyPerson.FirstName ='Jane';this._MyPerson.LastName ='Smith';this._MyPerson = Sys.Serialization.JavaScriptSerializer.serialize(this._MyPerson);this.set_ClientState(this._MyPerson);
Enjoy!

Thanks for your sharing.


Continuing to expand on this... It looks like this requires more work when one wants to serialize and deserialize an object with nested objects. Ugh... Any input or ideas to quickly move this along would be appreciated. I am thinking this will involve some recursive logic.


As an additional reference -http://www.manuelabadia.com/blog/PermaLink,guid,22a125d3-1ed3-422b-ba2b-89ed63febce3.aspx

It looks like I need to implement a JavaScriptResolver...


Well well... Sure enough, the JavaScriptResolver solves the problem with nested objects. The link in my previous post above explains it all.

Modified the code below in bold italics -

namespace SandboxClass{public class GenericTypeConverter : TypeConverter {public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,object value, Type destinationType) { JavaScriptSerializer jss =new JavaScriptSerializer(new SimpleTypeResolver());string s = jss.Serialize(value);return s; }public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,object value) {if (value is string) { JavaScriptSerializer jss =new JavaScriptSerializer(new SimpleTypeResolver()); T t = jss.Deserialize((string)value);return t; }else return base.ConvertFrom(context, culture,value); } }}

I am looking to move this JSON serialization of a custom object out of an AJAX control extender into a custom AJAX client control.

I have this part in my custom AJAX client control -

 IEnumerable IScriptControl.GetScriptDescriptors() { ScriptControlDescriptor descriptor =new ScriptControlDescriptor("CGAjaxClientControls.CGRadioButtonList",this.ClientID); descriptor.AddProperty("result",this.Result); descriptor.AddProperty("targetAction",this.TargetAction);return new ScriptDescriptor[] { descriptor }; }
The 'result' is just a string. That's the easy part but 'targetAction' is a custom object with nested objects as well.
The problem is I get this error message trying to deserialize on the client.

Sys.ArgumentTypeException: Object of type 'Object' cannot be converted to type 'String'. Parameter name: data

Here is the $create -

Sys.Application.add_init(function() { $create(CGAjaxClientControls.CGRadioButtonList, {"result":"Test","targetAction":{"ID":0,"TargetRadioButtonListId":"CGRadioButtonList1","RadioButton":[{"ID":0,"Text":"Yes","Value":"True","Selected":false,"Container":"panel","TargetControlId":"divCGRadioButtonList1Y"},{"ID":1,"Text":"No","Value":"False","Selected":false,"Container":"panel","TargetControlId":"divCGRadioButtonList1N"}]}},null,null, $get("CGRadioButtonList1"));});

Can anyone help me crack this? I do notice the type resolver tags are not getting added.

Thanks!


I think I've got this figured out. It is not required to deserialize the object when you create a custom AJAX client control. It's already done for you through IScriptControl.

No comments:

Post a Comment