CodeProject

Stamattina, mentre stavo creando una semplice pagina ASP.NET, ho lottato un paio di ore contro una GridView che apparentemente lavorava in maniera anomala.

Quale argomento preferisci?

 

Descrizione del problema

Il problema che riscontravo sembrava un’errata creazione degli ID dei controlli ClientSide. Generalmente il motore ASP.NET si prende carico di modificare gli ID per evitare la duplicazione di identificativi che dovrebbero essere univoci. Proprio nel caso di una GridView infatti, un controllo che noi inseriamo con ID=”txtSelectedValue”, e che viene ripetuto per ogni riga della griglia, avrà un ClientID simile a questo:

    gridTest1_GridView1_ctl02_txtSelectedValue

Stamattina ho impiegato diverso tempo per capire come mai il controllo veniva invece generato in questo modo:

    <INPUT id="txtSelectedValue" type="text" name="txtSelectedValue" />

 

Descrizione del codice

La GridView prevedeva una colonna con una ComboBox la quale doveva eseguire una funzione javascript alla selezione di un valore.
Per meglio capire vediamo subito in dettaglio i controlli in questione:

<Columns>
    <asp:TemplateField>
        <ItemTemplate>
            TextBox runat="server" ID="txtSelectedValue" Text="" />
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField>
        <ItemTemplate>
            <asp:DropDownList runat="server" ID="ddlChoice">
                <asp:ListItem Text="--" />
                <asp:ListItem Text="text1" />
                <asp:ListItem Text="text2" />
                <asp:ListItem Text="text3" />
                <asp:ListItem Text="text4" />
            </asp:DropDownList>
        </ItemTemplate>
    </asp:TemplateField>
</Columns>

mentre questa è la funzione javascript:

function selectChoice(ddl, textboxID) {
    var chosenoption = ddl.options[ddl.selectedIndex];
    if (chosenoption.value != 'nothing') {
        document.getElementById(textboxID).value = chosenoption.value;
    }
}

Volendo aggiungere la funzione javascript alla DropDownList in maniera programamtica, ho scelto di agire sull’evento OnRowCreated della griglia in questo modo:

protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        DropDownList ddlChoice = e.Row.FindControl("ddlChoice") as DropDownList;
        TextBox txtSelectedValue = e.Row.FindControl("txtSelectedValue") as TextBox;

        string jScript = "selectChoice(this, '{0}');";
        ddlChoice.Attributes.Add("onchange", string.Format(jScript, txtSelectedValue.ClientID));
    }
}

L’esempio quì riportato potrebbe avere poca rilevanza pratica, ma nelle esigenze quotidiane potremmo trovarci in una situazione simile.

Come potete vedere dal codice javascript, al cambiare di valore delle ComboBox, si agisce lato client tramite l’evento onchange del controllo <SELECT> per riportare un valore dentro un altro controllo HTML; in questa maniera si evitano inutili PostBack della pagina aspx, per evitare richieste ed esecuzione di codice da parte del motore ASP.NET.

Avendo una serie di controlli tutti con lo stesso id, naturalmente la funzione javascript restituisce un errore alla seguente riga:

    document.getElementById(textboxID).value = chosenoption.value;

poiché il metodo getElementById restituisce il primo controllo HTML con l’id specificato, vanificando quindi l’utilità dell’intera funzione.
Ed ecco la riga incriminata:

    ddlChoice.Attributes.Add("onchange", string.Format(jScript, txtSelectedValue.ClientID));

Probabilmente, ho pensato io, il motore ASP.NET in questo punto non ha ancora creato gli ID univoci; e facendo riferimento da codice alla proprietà ClientID del controllo, probabilmente, ho di nuovo pensato io, il motore ASP.NET decide di mantenere gli ID statici. Infatti semplicemente commentando la riga sopra indicata, tutto funzionerebbe normalmente.

 

Soluzione

Come passare dunque l’ID del controllo TextBox alla funzione javascript?

Facendo riferimento ai miei pochi neuroni ancora funionanti, mi è sembrato di ricordare che l’evento RowCreated viene lanciato prima di RowDataBound, quindi ho immaginato che posticipando l’utilizzo della proprietà ClientID, forse avrei risolto il problema.

Così facendo infatti, è bastato spostare il codice scritto nel delegate OnRowCreated, in OnRowDataBound, per avere il risultato cercato.
Ecco il codice generato in questo caso:

<tr>
    <td>
        <input name="gridTest1$GridView1$ctl03$txtSelectedValue" type="text" id="gridTest1_GridView1_ctl03_txtSelectedValue" />
    </td>
    <td>
        <select name="gridTest1$GridView1$ctl03$ddlChoice" id="gridTest1_GridView1_ctl03_ddlChoice" onchange="selectChoice(this, 'gridTest1_GridView1_ctl03_txtSelectedValue');">
            <option value="--">--</option>
            <option value="text1">text1</option>
            <option value="text2">text2</option>
            <option value="text3">text3</option>
            <option value="text4">text4</option>
        </select>
    </td>
</tr>

 

Conclusione

Non ho approfondito gli studi per comprendere in dettaglio questo comportamento, ma come si sa… il tempo stringe e l’importante è che…… tutti vissero felici e contenti!!!!!