ControlParameter e masterpage... bug?

Proprio ieri è apparso un messaggio sul forum in cui veniva segnalato uno strano problema riguardante i controlparameter nelle masterpage. Ricordando che parecchio tempo fa mi era capitato di osservare lo stesso problema, ricordavo che avevo risolto con la costruzione di un controlparameter personalizzato.

Ma partiamo con ordine. Il bug si mostra nel caso nella pagina sono presenti due o più contentplaceholder in cui il datasource e il controllo che dev'essere utilizzato per la query non sono nello stesso contentplaceholder. Per esempio:

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<asp:TextBox ID="TextBox1" runat="server">aa001</asp:TextBox>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder2" Runat="Server">
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="true" DataKeyNames="id"
DataSourceID="AccessDataSource1" />
<asp:AccessDataSource ID="AccessDataSource1" runat="server" DataFile="~/esempio.mdb"
SelectCommand="SELECT * FROM [tabella] WHERE ([nome] = ?)">
<SelectParameters>
<asp:ControlParameter ControlID="TextBox1" Name="nome" PropertyName="Text" Type="String" />
</SelectParameters>
</asp:AccessDataSource>
</asp:Content>

Una volta richiamata una pagina contenente questo codice, sarà visualizzato un errore perché il webcontrol TextBox1 dichiarato come controlparameter non viene trovato anche se, come si vede, è ben visibile.

Andando a spulciare con il reflector il codice di ControlParameter, vedo il problema nella funzione FindControl nella classe interna DataBoundControlHelper, funzione richiamata da ControlParameter per avere l'istanza del controllo interessato. Ecco il codice:

public static Control FindControl(Control control, string controlID)
{
Control control1 = control;
Control control2 = null;
if (control == control.Page)
{
return control.FindControl(controlID);
}
while ((control2 == null) && (control1 != control.Page))
{
control1 = control1.NamingContainer;
if (control1 == null)
{
throw new HttpException(SR.GetString("DataBoundControlHelper_NoNamingContainer", new object[] { control.GetType().Name, control.ID }));
}
control2 = control1.FindControl(controlID);
}
return control2;
}

Viene cercarto il controllo per il suo ID, se non viene trovato si risale gerarchicamente la struttura dei controlli della pagina fino a livello di pagina. Ma il problema nel caso visualizzato sopra, dove sta? Nella costruzione della pagina per ogni content viene creato un contentplaceholder:

Page
->ContentPlaceHolder1
-->TextBox1
->ContentPlaceHolder2
-->ControlParameter

Come si può vedere il controllo TextBox1 è all'interno di un altro Content, e nella routine qui sopra, risaliti a livello di Page nella ricerca, non è possibile trovare TextBox1 perché è all'interno di un altro controllo. Per risolvere mi ero creato un controlparameter personalizzato in grado di cercare in modo più approfondito nei meandri della struttura gerarchica dei controlli:

using System;
using System.ComponentModel;
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;

///
/// ControlParameter per masterpage
///

namespace aspitalia.com
{
public class ControlParameterMP : ControlParameter
{
protected override object Evaluate(HttpContext context, Control control)
{
if (control == null)
{
return null;
}
string text1 = this.ControlID;
string text2 = this.PropertyName;
if (text1.Length == 0)
{
throw new ArgumentException("ControlParameter_ControlIDNotSpecified");
}
Control control1 = this.FindControl(control, text1);
if (control1 == null)
{
throw new InvalidOperationException("ControlParameter_CouldNotFindControl");
}
ControlValuePropertyAttribute attribute1 = (ControlValuePropertyAttribute)TypeDescriptor.GetAttributes(control1)[typeof(ControlValuePropertyAttribute)];
if (text2.Length == 0)
{
if ((attribute1 == null) || string.IsNullOrEmpty(attribute1.Name))
{
throw new InvalidOperationException("ControlParameter_PropertyNameNotSpecified");
}
text2 = attribute1.Name;
}
object obj1 = DataBinder.Eval(control1, text2);
if (((attribute1 != null) && string.Equals(attribute1.Name, text2, StringComparison.OrdinalIgnoreCase)) && ((attribute1.DefaultValue != null) && attribute1.DefaultValue.Equals(obj1)))
{
return null;
}
return obj1;
}
//
// Codice per la ricerca del controllo
//
private Control FindControl(Control control, string controlID)
{
Control control1 = control;
Control control2 = null;
if (control == control.Page)
{
return control.FindControl(controlID);
}
while (control2 == null)
{
control1 = control1.Parent;
if (control1 == null)
{
throw new HttpException("DataBoundControlHelper_NoNamingContainer");
}
control2 = control1.FindControl(controlID);
if (control2 != null) break;
foreach (Control cc in control1.Controls)
{
ContentPlaceHolder ph = cc as ContentPlaceHolder;
if (ph != null)
{
control2=ph.FindControl(controlID);
if (control2 != null) break;
}
}
if (control1 == control.Page) break;
}
return control2;
}
}
}

E' possibile inserirlo direttamente nella directory code della webapplication, in tal caso, per usarlo nelle nostre pagine dovremo registrarlo nell'intestazione di pagina:

<%@ Register TagPrefix="az" Namespace="aspitalia.com" Assembly="__code" %>

E per utilizzarlo nell'esempio sopra:

<asp:AccessDataSource ID="AccessDataSource1" runat="server" DataFile="~/articoli.mdb"
SelectCommand="SELECT * FROM [table] WHERE ([nome] = ?)">
<SelectParameters>
<az:ControlParameterMP ControlID="TextBox1" Name="nome" PropertyName="Text" Type="String" />
</SelectParameters>
</asp:AccessDataSource>

Non sono sicuro che sia la tecnica migliore, ma se esiste una strada più veloce...

Nella stessa categoria

Commenti
Ricciolo scrive:
ControlParameter e masterpage... bug?

Dovrebbe bastare mettere il prefisso del contenitore visto che Content implementa INamingContainer e quindi imbedisce all'esterno di far vedere i suoi contorlli.

ControlID="ContentPlaceHolder1:TextBox1"

Non mi sembra un bug
23/12/2006 ore 19.18 | 4 risposte
andrewz scrive:
Re: ControlParameter e masterpage... bug?

Ciao Cristian,
vuoi dire scritto così?
<asp:ControlParameter ControlID="Content1:TextBox1" Name="nome" PropertyName="Text" Type="String" />

Avevo provato quel formato insieme ad altri, ma ottenevo sempre errori. Hai qualche altra info?

Ti ringrazio e tanti auguri!

Ciao
23/12/2006 ore 20.04 | 1 risposta
andrewz scrive:
Re: ControlParameter e masterpage... bug?

Ah, ok, ho capito, non l'id del content della pagina ma dell'id della masterpage che la contiene:
<asp:ControlParameter ControlID="ContentPlaceHolder1:TextBox1" Name="nome" PropertyName="Text" Type="String" />


C'è solo un problema nel caso remoto non si conosca l'id del contentplaceholder che conterrà il webcontrol interessato.

Ti ringrazio Cristian delle info!

Ciao
23/12/2006 ore 20.10 | 1 risposta
Daniele Bochicchio scrive:
Re: ControlParameter e masterpage... bug?

andrewz wrote:
Ti ringrazio Cristian delle info!

fanne uno o anche due script
27/12/2006 ore 12.48 | 1 risposta
andrewz scrive:
Re: ControlParameter e masterpage... bug?

Daniele Bochicchio <Daniele_Bochicchio> ha scritto:
fanne uno o anche due script

Cristian lo deve scrivere, è lui che sa tutti i trucchi
Ciao
27/12/2006 ore 21.33

Aggiungi un nuovo commento »»»
Per inserire un commento, devi registrarti alla nostra community.

© 1998-2008 - AZ - Il blog di Andrea Zani

TagCloud
BLOG INFO
  • Post: 280
  • Commenti: 186
  • TrackBacks: 35
  • Feed blog e contenuti tecnici: RSS
  • Feed blog: RSS Atom OPML

MVP
CATEGORIE
I PIÙ LETTI DEL MESE
IN EVIDENZA