Pages

Saturday, December 2, 2006

Multiple calls to RegisterOnSubmitStatement and Client-Side Validation

Ok! Here is a new thing I discovered yet again the hard way...

In short: Do not call Page.ClientScript.RegisterOnSubmitStatement after the Page Load event.

(What?!)

Well yes! It's not under all circumstances that you can notice the difference but it's there and it's major!
I do not really wan to describe this, so I 'll take you though it with an example:
Let's say you have an aspx page. The page has two controls in it. For simplicity lets make those controls UserControls. The controls are pretty simple: just a TextBox and a RequiredFieldValidator in each of them.

So there you have it:

Control A (let's call it OnSubmitControlA):
and the code file:

Control B (let's call it OnSubmitControlB):

and the code file:

And finally the page itself:

(the codefile has nothing special in it...)

The page has of course a submit button so that we can submit and test it...

So! What we have here!?
  • A Page,
  • two controls that wan to access client-side code just before the page submits (for no particular reason)
  • and at least a Validator Control that will fail validation at some point. (If we did not have a validator then I would not have a case here!)
Now go render the page an see the result. If you leave either TextBox empty and click on the submit button, you will notice that only the alert from the first control pop's up. The other registered script is never called....

Now go back and make a slight change. Move in both contols' codefile the call to Page.ClientScript.RegisterOnSubmitStatement from OnPreRender to OnLoad, like this:

do the same on the other control:

Done! Go back and render the page! Leave either TextBox empty and click submit... See??? Now both alerts pop up!!!

Why is that???

Well look at the source of the rendered page before and after the change to see what' going on:

Here is the script rendered when the call to RegisterOnSubmitStatement is placed in the OnPreRender event:

And here is the script rendered when the call to RegisterOnSubmitStatement is placed in the OnLoad event:

Got it?

If RegisterOnSubmitStatement is called after OnLoad, then the first time it's called the framework appends the statement that calls ValidatorOnSubmit() and returns false if it fails; (effectively blocking the rest of the script from executing). Subsequent calls of RegisterOnSubmitStatement (after OnLoad) are appended to the script generated by the first call (and get blocked by the effect I just described).

Instead, if all your calls to RegisterOnSubmitStatement come before the end of the OnLoad phase, then all registered scripts are appended to previously generated scripts before the eventual injection of the call to ValidatorOnSubmit().

Hoping for comments on this...

Wednesday, November 29, 2006

xsl:include, xsl:import & msxml:script blocked on WSS 3 XML WebParts

I discovered today (the hard way), that we can no longer use xsl:include and xsl:import elements in XSL Transformations for Windows SharePoint Services 3.0 XML WebParts.

Along with those msxml:script is also blocked.

I am going to investigate this further and see if there is an administrative way to bypass this security restriction. XSLT code reuse is a useful thing and I want to keep it as an option in some environments.

Anyhow...

I am including two relevant links I found on the web:
I would appreciate comments from people who know more on the subject.

Saturday, November 25, 2006

Moving to WSS 3

I am in the process of replacing my old personal site with a new version based on Windows SharePoint Services 3.0.

My previous site is based on Windows SharePoint Services 2.0, and is currently located at http://rousso.gr/.

Though I could just gradually upgrade the old site to the new version of WSS I opted not to do so because the last few months I have been considering of restructuring my sites completely. I am not currently getting into any more details on this, but the bottom line is that I came to have far too many personal sites which rendered my main site almost useless. On the other hand I need I WSS based site in order to enjoy the goodies of WSS like custom lists and document sharing with my family, friends and colleagues.

So what you can expect to see here is either a private site or a public site replacing the current versions of http://www.rousso.gr and http://blog.rousso.gr.

Tuesday, September 19, 2006

CS:Editor ViewState

Quick Tip:
CommunityServer 2.1 uses TinyMCE as a rich text editor. They use what appears to be a wrapper to TinyMCE: CommunityServer.Controls.Editor. As I am developing an application on top of CS 2.1 I am using the same rich text editor and encountered the following problem:
I was setting declaratively the Height Property of the Editor control, but it could not retain it's value after postbacks.
Look at the ASPX part:

So far so good but, after a postback the Editor control returned to its default height (messing-up my layout).

To make a long story short, it figured that something is wrong either with timing or with implementation. I mean that either the wrapper, or the wrapped control do not assign the value of Height before ViewState is saved, or they assign it to either a Property that is not saved in view state, or during an Event that fires before LoadViewState.

So what I did to work-around the issue was just disable ViewState in the Editor control.

The problem has gone away and that means that LoadViewState was overwriting my value.

I did not devote any time in looking further into this matter so I'm just posting this work-around, but if I do, I 'll let you know the details.

Monday, May 29, 2006

UseSubmitBehavior & GetPostBackEventReference

funny thing…

I needed a button (in an ASP.NET 2.0 page) to post-back and raise an event for another control. That means that I click a button but the post-back event is not handled by the button clicked but instead by another control on the same page.

The easy way was to add a System.Web.UI.WebControls.Hyperlink and set its NavigateUrl property to Page.ClientScript.GetPostBackClientHyperlink(myOtherControl, "Arguments")
But I wanted it to look like a Button so instead I added a System.Web.UI.HTMLControls.HTMLButton Control and did the same thing to its OnClientClick property.
All worked fine.

The funny thing happened when I decided that I like most to use a System.Web.UI.WebControls.Button instead.

ASP.NET Buttons are rendered as <input type="submit" /> controls. and by default their client-side onClick event is wired to the __doPostBack javascript method by ASP.NET.
To change that behavior in ASP.NET 2.0 we are supposed to use the UseSubmitBehavior Property which when set to false causes the Button to be rendered as <input type="button" /> and allows you to set the client-side onClick event programmatically (or declaratively) by setting the OnClientClick server-side Property.

That's what I did, only to discover that to whatever I was assigning to the OnClientClick Property, the original __doPostBack call was automatically appended by the framework.
I did not spend all the time in the world to figure this out and as I failed to find a reasonable way out of the problem, what I did, was to trick the client-side out of it by appending a JavaScript “return;” statement to the script I was assigning to OnClicntClick.

To make all these more obvious here is the hands on:
in the aspx/ascx file:


Notice that lines 5 and 6 above define the Hyperlink and HTMLButton that worked fine and line 7 defines the WebControls.Button that did not behave. Lines 1 to 3 define a DetailsView just because that is the control I wanted to handle the post-back event.

Now in the code file:

These get rendered as follows at run-time (if you look at the source in the browser):

This works just fine if you click on the Hyperlink1 or Button1 controls, but not at all if you clicked at Button2. That is because Button2 makes the call to __doPostBack twice. Once because I asked it to do so (by assigning its OnClientClick Property as you see in line 4 of the code-file above) and once because the framework automatically inserted the default post-back event reference for the button (Button2) itself.

To overcome this behavior (as I could not find another way around it) I just changed the last line in the code-file (line 4 in the snippet) by appending a javascript return statement as follows:

This caused Button2 to render as follows:

Notice the return statement (in bold) rendered between the two calls to __doPostBack. Although this does not prevent the second __doPostBack call from beeing inserted by the framework, it does prevent it from executing….

If you know a proper way out of this little puzzle  please let me know….

P.S:
  1. As you obviously noticed I needed the button to create an Insert button that would enable a DetailsView to go to Insert Mode even if the view has no items to display. The button (when clicked) causes the DetailsView to receive an Insert Command.
  2. Of course you fortunately do not need to do all this to make a details view to enter Insert Mode. It would be enough to just and any button with a server-side Click event handler changing the mode to Insert by just calling DetailsView1.ChangeMode(DetailsViewModes.Insert);