Ormai sono abituato ad evitare a tutti i costi i popup nelle
applicazioni web. Tuttavia mi rendo conto che c'è ancora qualcuno che non riesce
a farne a meno, a torto o a ragione non mi interessa sapere. Durante la track
web dei CommunityDays sono stato molto incuriosito da una domanda che è venuta
dal pubblico e che recitava più o meno così: "è possibile effettuare il
cross-page postback in un popup impostando il PostBackUrl?". La risposta
naturale è no, ma quest'oggi, per puro diletto ho provato a capire se questo è
proprio vero. In effetti se ragioniamo bene sul problema è evidente che esiste
almeno un modo di effettuare il postback in una nuova finestra del browser, cioè
impostando l'attributo "target" della form. Però impostare questo attributo a
runtime mediante javascript è fattibile ma espone ad un fastidioso problema. Il
mio primo tentativo è stato quello "semplice" di usare l'evento "OnClientClick"
come segue:
btnPopup.OnClientClick = "document.forms[0].target = '_blank';";
Impostare l target in questo modo funziona perfettamente, ma
lascia la form in uno stato inconsistente che fa si che tutti i controlli
presenti effettuino il postback nella nuova finestra dopo che il pulsante è
stato azionato per la prima volta. In effetti la cosa migliore sarebbe quella di
ripristinare il target originale dopo il postback, ma in questo l'evento
OnClientClick non ci può essere di aiuto perchè il runtime quando fa il merge
degli script aggiunge il codice per il postback subito dopo quello immesso in
questo attributo. La soluzione perciò è solamente quella di estendere il
controllo Button per modificarne il rendering:
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO;
using System.Text.RegularExpressions;
namespace Elite.Web.UI.WebControls
{
/// <summary>
/// Summary description for ButtonEx
/// </summary>
public class ButtonEx : Button
{
private string postBackTarget;
public ButtonEx()
{ }
protected override void Render(HtmlTextWriter writer)
{
if (!string.IsNullOrEmpty(this.PostBackUrl) &&
!string.IsNullOrEmpty(this.PostBackTarget))
{
using (StringWriter tempWriter = new StringWriter())
{
using (HtmlTextWriter htmlWriter = new HtmlTextWriter(tempWriter))
{
base.Render(htmlWriter);
Regex rx = new Regex(
@"onclick=""javascript:(?<code>[^""]*)""",
RegexOptions.IgnoreCase);
writer.Write(
rx.Replace(
tempWriter.ToString(),
new MatchEvaluator(Evaluator)));
}
}
}
else
base.Render(writer);
}
private string Evaluator(Match match)
{
return string.Format(
@"onclick=""javascript:document.forms[0].target='{0}';{1};" +
"setTimeout("document.forms[0].target=''", 100);""",
this.PostBackTarget,
match.Groups["code"].Value);
}
public string PostBackTarget
{
get { return postBackTarget; }
set { postBackTarget = value; }
}
}
}
Il codice è in effetti un po' tirato, lo confesso, ma è davvero
l'unico modo che sono riuscito a trovare, dopo aver analizzato la classe Button
del framework, per iniettare il codice prima e dopo la generazione del postback.
in sostanza nel metodo render viene fatto generare il codice del pulsante in una
stringa, e tramite una regular expression si effettua la sostituzione
all'interno dell'evento onclick. Questo codice viene poi immesso nuovamente nel
flusso del rendering. In questo modo impostando la proprietò PostBackTarget del
ButtonEx si otterrà il sospirato postback in popup. L'unica curiosità che rimane
è il fatto che ho dovuto usare un timeout di 100 millisecondi per reimpostare il
target. Infatti semplicemente impostando la proprietà subito dopo
WebForm_DoPostBackWithOptions non si ottiene l'effetto desiderato.
powered by IMHO 1.3