I encountered an issue when I tried to add webparts of differing types into the same web part page - the result was a stack overflow causing IE to abruptly terminate.
The problem arises when the script defined in the EnsurePanelFix method above, is inserted more than once into a page. The execution of the first script block is harmless. The execution of any subsequent block however, eventually results in a stack overflow. The reason is that the subsequent assignment to RestoreToOriginalFormActionCore is from the newly defined RestoreToOriginalFormAction (defined in the frist script block), and not the original RestoreToOriginalFormAction method.
In the subsequent execution of the script block, RestoreToOriginalFormActionCore and RestoreToOriginalFormAction are made equivalent, so the RestoreToOriginalFormAction method becomes infinately recursive.
var RestoreToOriginalFormActionCore =
RestoreToOriginalFormAction;
RestoreToOriginalFormAction = function()
{
if (_spOriginalFormAction != null)
{
RestoreToOriginalFormActionCore();
There are a number of ways around this, (that I can think of) but none of them are ideal. The first is to ensure that only one script block is inserted into the page, no matter how many types of your web parts are added to the page. This is done by modifying the call to RegisterStartUpScript. I found that it is the combination of the type parameter and key parameter together that determines whether the script is inserted. So I just created a base web part (MyBasePart), and used that type as the type parameter. typeof(object) also worked; I assume you can use any type that you want.
ScriptManager.RegisterStartupScript(this, typeof(MyBasePart), "UpdatePanelFixup",fixupScript, true);
Well this works if you publish all of the webparts that go into the page, but there will be issues if you also want to use web parts published by others, because depending on what type/key they used, you might still end up with more than one script block in your page. So the solution that I'm using for the time being is to define RestoreToOriginalFormAction based on what it is originally. (But this will break if the definition of RestoreToOriginalFormAction in WSS changes)
String fixupScript = @"
if (typeof(_spBodyOnLoadFunctionNames) !== 'undefined'){
_spBodyOnLoadFunctionNames.push(""_initFormActionAjax"");
function _initFormActionAjax() {
if (_spEscapedFormAction == document.forms[0].action){
document.forms[0]._initialAction =
document.forms[0].action;
}
}
RestoreToOriginalFormAction = function() {
if (_spOriginalFormAction != null) {
if (_spEscapedFormAction==document.forms[0].action){
document.forms[0].action=_spOriginalFormAction;
}
_spOriginalFormAction=null;
_spEscapedFormAction=null;
document.forms[0]._initialAction = document.forms[0].action;
}
};
}";
I used the VS script debugger to find the original definition of RestoreToOriginalFormAction and incorporated it into the above code block. The new definition simply adds one line at the end:
document.forms[0]._initialAction = document.forms[0].action;
I removed the call to RestoreToOriginalFormActionCore. This is important because if a third party web part appears in the page after yours and again redefines the RestoreToOriginalFormAction method, there is still no possibility for recursion. i.e. RestoreToOriginalFormAction will become:
RestoreToOriginalFormAction = function()
{
if (_spOriginalFormAction != null)
{
RestoreToOriginalFormActionCore();
document.forms[0]._initialAction =
document.forms[0].action;
}
}";
But RestoreToOriginalFormActionCore is our function, which is not recursive. The only side effect is that
document.forms[0]._initialAction = document.forms[0].action;
will be called twice in succession, but that should be harmless.
Naturally, all bets are off if you want to use your web parts with more than one web part from elsewhere.
***********
The other way to work around this problem is to use a Page property – ClientScript. The ClientScript property gets a ClientScriptManager object to manage, register, and add script to a web page. This is to keep the original script as it is.
The ClientScript returns a ClientScriptManager object, and the object exposes a method, IsClientScriptBlockRegistered() that we use to make sure whether the same script block has already been registered into the web page or not.
This keeps the script block presented in this walkthrough as it is. Below is the sample code:
string
scriptKey = "UpdatePanelFixup";
if (!Page.ClientScript.IsClientScriptBlockRegistered( scriptKey ))
ScriptManager.RegisterStartupScript( this, typeof( SayHelloWebPart ), scriptKey, fixupScript, true );
Hope it helps!
- James Zheng