In the July issue of MSDN Magazine, Dino Esposito provided an excellent article on allowing DHMTL in ASP.Net Controls. This is a core concept for a project that I am working on, so I thought I would share my particular solution to the problem.
First, the problem. -- If you use DHTML and client-side scripting to manipulate server side objects, whatever attributes of those HTML elements that were changed since initial page render via DHTML will be lost on postback. My particluar need for DHTML was related to allowing users to move controls around a webform (a form designer) and then saving the new X/Y coordinates of those controls for later use.
Inspired from Mr. Esposito's article, I decided to model my solution using a single hidden field. I wasn't excited about this approach but I did consider it to me a good alternative to the common approach of adding one hidden field per DHTML-manipulated server control.
Since my particular need was to capture X and Y coordinates of controls that have been moved around a webform, in the DHTML javascript events fired after the control is dropped into place, I added code to se custom attributes representing the current coordinates. In this case dd.obj is whatever object was moved.
document.getElementById(dd.obj.name).setAttribute("x",dd.obj.x);
document.getElementById(dd.obj.name).setAttribute("y",dd.obj.y);
This code adds the custom attributes, X and Y to the DHTML layer that was dragged to a new location. Now, I must be able to persist these custom attributes through the postback.
To the webform, or an external .js referenced by the webform, I added a javascript function to enumerate through all attributes for all divs and set a hidden field's value to a text string that we will be able to reverse engineer on postback to re-set those attributes. (Or, do whatever we would like)
function setAttribState(hiddenID){
var attribState="";
//Which HTML tags should we include in attribstate?
//Div only for now.
var tagnames = new Array();
tagnames[0]='div';
for(var h=0;h var elem = document.getElementsByTagName(tagnames[h]);
for(var i=0;i var thisElement ="";
var attribs = elem[i].attributes;
var eleAttribs="";
for(var j=0;j if (attribs[j].specified){
var thisAttrib = "";
var thisAttrib = attribs[j].nodeName + ':' + attribs[j].nodeValue;
eleAttribs += thisAttrib;
if (j!=attribs.length-1){eleAttribs+=','};
}
}
thisElement = elem[i].id + '/' + eleAttribs;
attribState += thisElement;
if(i!=elem.length-1){attribState+=';'};
}
}
document.getElementById(hiddenID).setAttribute("value",attribState);
}To the webform, add a hidden field called "__attribState".
Then, in the codebehind for the webform I wired this javascript into the submit button's onclick event.
I did this by adding this to the page_load event:
Button1.Attributes.Add("onclick", "setAttribState('__attribState')") In the same codehind, under the form click event, I added a call to iterate through the text and set the attributes.
ReadAttribState("__attribState")
To the webform, I added the sub:
Private Sub ReadAttribState(ByVal HiddenControlName As String)
Dim hiddenctl As HtmlControls.HtmlInputHidden = CType(Page.FindControl(HiddenControlName), HtmlControls.HtmlInputHidden)
Dim elements As String() = Split(hiddenctl.Value.ToString, ";")
Dim element As String
For h As Int32 = 0 To elements.GetUpperBound(0)
Dim x, y As String
Dim ctlId As String = Split(elements(h), "/")(0)
Dim ctl As HtmlControls.HtmlGenericControl = Page.FindControl(ctlId)
Dim attribs As String() = Split(Split(elements(h), "/")(1), ",")
For i As Int32 = 0 To attribs.GetUpperBound(0)
Dim attribName = Split(attribs(i), ":")(0)
Dim attribValue = Split(attribs(i), ":")(1)
If LCase(attribName) <> "id" Then
ctl.Attributes.Add(attribName, attribValue)
End If
Next
Next
End Sub
Voilà. When your DHTML event fires, it will set your custom properties. Then, onClick, the form button will build a text string of all attributes for all divs and populate the hidden field. On postback, your code will poll this field, parse the name/value pairs for each attribute and re-set those attributes on page render. In my implementation, I then use these custom attributes to set the style of my controls on page_load.
Until Next Time,
Bill Dodd
mcp, mcsd, mcad, mcdba