ControlParameter e masterpage... bug?

di Andrea Zani, in .NET2,

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; 

/// <summary />
/// ControlParameter per masterpage
/// </summary />
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...

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese