Wednesday 27 October 2010

ASP.NET ASCX controls and ViewState

I've written a scripting engine into the ASP.NET app that I am developing. It's pretty neat - the user can choose from a list of scripts to load and work through, the script is loaded into a panel and they can follow it through.

Identified a problem yesterday whereby the second and subsequent scripts that got loaded had a strange little error - the first server control in the script didn't work on its first event (i.e. click, selectedindexchanged). It would work the second time the control was used - but that's no good for the users.

So I set about trying to solve it. After about seven hours of reading, trying things out and getting really frustrated I did solve it thanks to a couple of articles on the web. I made a load of mistakes so hopefully someone will read this and avoid making the same ones.

First, I had the scripts declared globally, i.e. Dim scriptname As Global.namespace.scriptname. Wrong wrong wrong!

What you need to do is create a ViewState called LastLoadedControl and set this to be the physical path of the control. This will reside in memory and allow you to persist the control between postbacks of the parent form. Then create a Private Sub called LoadUserControl(). This should check to make sure that the LastLoadedControl isn't null, then load the control and attach it to the Panel / Placeholder that you're using to display the controls.

You need to call this LoadUserControl method in the Page_Load event every time. Also, in the method that you're using to drive the user interaction (i.e. the list of scripts to pick from) you need to declare the physical path of the script, set the LastLoadedControl ViewState to this value, then call the LoadUserControl method.

Check out the first example in this blog (it's in C# but easily converted) for a really good example: .

A couple of warnings. First, even though the example above solved my problem, it was causing an error whereby the aspx page was trying to load the same control into the Panel / Placeholder twice and falling over as a result. What you have to do in the LoadUserControl method is specifically set the name of the control otherwise the ViewState of the control (i.e. its values) won't carry across between postbacks. Obviously you can see a scenario where you're creating the same control twice. To get around this you have to remove the control from the Panel / Placeholder every time. The example says to use Panel.Controls.Clear() but this didn't work for me. What I did was check that the Panel had > 1 control, then did Panel.Controls.RemoveAt(1) to eradicte the control and then replace it.

Second warning - some people say to do the control loading in Page_Init, rather than Page_Load. Problem with that is the Page_Init can't reference any Panels / Placeholders etc. so it's a bit of a waste of time.

POSTSCRIPT: just tried loading something into Page_Init in the .ascx control rather than the Page_Load event to stop the value of a control changing every time and it worked. I wonder if it's because I had the Page_Init set to Private before, and now I have changed it to Protected it seems to work ok. My bad.

Tuesday 26 October 2010

Cross - Domain Scripting and iFrames

I've been doing some work over the last couple of days to embed data from a third party API into the .NET web app front end that we're working on. The API isn't open to everyone - you have to access it via Javascript and it can only be accessed on the server that all the component parts run on. As API's go it's pretty useless when you want to hook into it from somewhere else.

So our javascript guru showed me how to make an app in an iframe talk to the parent app that's hosting the iframe and away I went. I built a .jsp page on the API server that delivers all the data I want and then modified the .aspx page to include some labels to display the data.

I ran into a couple of problems. The first concerns the iframe load time. I originally tried to write some javascript that would load the values from the divs on the .jsp page into server controls on the .aspx page but this failed because the iframe was always the last thing to load and all the server controls saw was NULL values.

The js guru suggested a kind of timer function that would check when the iframe had finished loading before running a script to get the values. But it turned out that the simplest solution was to make the .jsp page populate the values itself - you can make the content of the iframe control what gets written to the screen in the parent as much as you can do it the other (and more logical) way around. In this case the connotation of parent and child is wrong - they're more like siamese twins joined together and able to influence each others movements.

Another useful thing to know is that you can only make two apps on two different servers talk to each other if you suffix the server name in the URL with the name of the domain that they both share. So if my domain is www.markp3rry.com then you will need to set the servers to http://server1.markp3rry.com:8080/ and http://server2.markp3rry.com:8090/ (for example).

There's a heck of a lot of scuttlebut written on the web about this topic so tread carefully. A lot of the examples for extracting information from an iframe in both javascript and jquery script were making me tear my hair out. You have been warned!