54 messaggi dal 13 settembre 2010
Ciao,
vorrei sottoporvi un quesito che da un pò di giorni mi sta facendo diventare pazzo:

Ho una detailsview modalità edit con all'interno vari campi tra i quali nazione, provincia, comune, via che corrispondono a 4 dropdownlist per la visualizzazione dei relativi dati.
La detailsview è associata ud un sqldatasource CASE che rappresenta la lista di immobili, le dropdownlist sono associate ognuna ad una sqldatasource diverso che pesca da tabelle relative a nazioni, province, comuni, vie(ho inserito chiavi esterne e vincoli pr tenere il territorio ben mappato). Nella proprietà SelectedValue delle ddl ho
'<%#Bind("id_nazione")%>'
'<%#Bind("id_provincia")%>'
'<%#Bind("id_comune")%>'
'<%#Bind("id_via")%>'

che sono gli id presenti nella tabella CASE.

Ho già creato la pagina per la creazione di una nuova casa e sono riuscito a far in modo che le ddl si passino i dati attraverso dei comntrolparamenter, il problema è in visualizzazione/modifica:

se la casa riporta come nazione "italia" (quindi con id_nazione=2) devono essere visualizzate nella ddl province solo le province dell'italia con selezionata quella scelta, se la casa riporta provincia "roma" (con id_provincia=56) vengano visualizzate nella ddl comuni solo i comuni in provincia di roma con selezionato quello scelto, stessa cosa per le vie.
Poi ovviamente se l'utente va a modificare la provincia e cambia da roma a milano allora in automatico si aggiorna la ddl comuni riportando solo i comuni in provincia di roma.

Io ho effettuato varie prove, o mi perde gli id per strada e le ddl non si popolano come dovrebbero oppure utilizzando dei controlparameter nell'sqldatasource di province, comuni e vie ma mi restituisce l'errore
Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control


Non so se mi sono spiegato, nel caso chiedetemi ulteriori spiegazioni o eventuali parti di codice.
Grazie mille per l'eventuale aiuto.
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,
sì, è necessario che mostri del codice.
L'errore che stai ottenendo può verificarsi se non hai messo runat="server" sulle DropDownList o, più probabilmente, c'è qualche problema di sintassi che manda in palla il parser delle pagine.

Posta sia il contenuto della pagina aspx che il suo codefile. Se il codice è tanto, incollalo su PasteBin, così è più facile da leggere.

ciao

Enjoy learning and just keep making
54 messaggi dal 13 settembre 2010
Ciao,
grazie della disponibilità.
Qui il codice base, da qui vorrei fare in modo che la ddl province visualizasse solo le province in base alla nazione scelta, la ddl comuni solo i comuni della provincia scelta, e stessa cosa per la ddl indirizzo.

http://pastebin.com/NUN2wME9

Tutte le prove che avevo fatto le ho cancellate altrimenti non mi funzionava, quindi adesso così funziona ma le varie ddl non sono collegate tra loro.
Fammi sapere se serve altro, grazie ancora!
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,
un bell'incastro degno di un'enigma del professor Layton :) proviamo a risolverlo!

Partiamo da una premessa: le DropDownList della provincia e del comune dovrebbero aggiornarsi quando cambia il valore della DDL da cui dipendono.
Se non si aggiornano è perché il database non viene interrogato di nuovo, dato che gli Items delle DropDownList restano salvati nel Viewstate, tra un postback e l'altro.

Se provi a forzarne l'aggiornamento, ecco che salta fuori questo errore. E' lo stesso che avevi tu?
http://support.microsoft.com/kb/978215/it
Se ci fai caso illustra proprio il tuo esempio: una FormView che contiene una DropDownlist che legge i suoi elementi da un SqlDataSource.

La soluzione di Microsoft è quella di non usare il metodo .DataBind() sulla DropDownList, ma noi ne abbiamo bisogno affinché i suoi elementi si aggiornino.
Siamo in una strada senza uscita? No, perché effettuare il .DataBind non è il solo modo di popolare una DropDownList. Possiamo accedere direttamente alla sua collezione .Items, svuotarla dei vecchi elementi e aggiungere dei nuovi.

Ecco che se ne va il buon proposito di risolvere il problema solo con codice dichiarativo. Il professore forse ci sarebbe riuscito ma la mia soluzione prevede che tu scriva un po' di codice Vb.NET.
Iniziamo dai 3 template fields di nazione, provincia e comune:

<asp:TemplateField headertext="Nazione">
    <ItemTemplate>
    <asp:DropDownList AutoPostBack="true" runat="server" ID="nazione" DataSourceID="nazioniSource" width="340" DataTextField="nazione" DataValueField="id" SelectedValue='<%#Bind("id_nazione")%>'>
    </asp:DropDownList> 
    </ItemTemplate>    
</asp:TemplateField>
<asp:TemplateField headertext="Provincia">
    <ItemTemplate>
    <asp:DropDownList AutoPostBack="true" runat="server" ID="provincia" width="340" DataTextField="provincia" DataValueField="id" SelectedValue='<%#Bind("id_provincia")%>' OnLoad="provincia_Load">
    </asp:DropDownList> 
    <asp:Label ID="id_provincia" runat="server" Text='<%#Eval("id_provincia")%>' visible="false"/>
    </ItemTemplate>        
</asp:TemplateField>  
<asp:TemplateField headertext="Comune">
    <ItemTemplate>
    <asp:DropDownList runat="server" ID="comune" width="340" DataTextField="comune" DataValueField="id" SelectedValue='<%#Bind("id_comune")%>' OnLoad="comune_Load">    
    </asp:DropDownList> 
    <asp:Label ID="id_comune" runat="server" Text='<%#Eval("id_comune")%>' visible="false"/>
    </ItemTemplate>        
</asp:TemplateField>

  • Su nazione e provincia ho aggiunto AutoPostBack="true", che mi serve a causare un postback quando l'utente cambia selezione. Così potrò esegure codice lato server.
    A proposito... un'applicazione web moderna, in questo caso, non dovrebbe causare il ricaricamento della pagina, quindi quella che ti sto dando è la soluzione per te più veloce da implementare. Quella per me migliore prevede che tu legga province e comuni da richieste Ajax, ma ciò ha altre implicazioni.
  • Su provincia e comune ho gestito l'evento server Load. E' lì che cambierò gli elementi di quelle due dropdownlists. Nazione, invece, che non dipende da nessuno, non ha bisogno che i suoi elementi siano modificati.
  • Da provincia e comune ho anche tolto il DataSourceID. Ho dovuto farlo a causa di quell'errore di cui accennavo sopra.

Ora aggiungi queste funzioni al tuo codefile vb.net. Il codice non è per niente elegante ma funziona. Magari puoi fare qualche ottimizzazione.
Protected Sub provincia_Load(sender As Object, e As EventArgs)
    'Ottengo un riferimento alla dropdownlist
    Dim ddl = CType(sender, DropDownList)
    'Memorizzo il valore attualmente selezionato
    Dim valore = ddl.SelectedValue
    'Svuoto tutti gli elementi
    ddl.Items.Clear()
    'Ottengo il valore di id_nazione e lo assegno come primo parametro del SqlDataSource
    provinceSource.SelectParameters(0).DefaultValue = OttieniValore("id_nazione")
    'Seleziono i risultati, li proietto sul tipo ListItem e li metto in una variabile
    Dim province = From prov In provinceSource.Select(New DataSourceSelectArguments()).Cast(Of DataRowView)()
                   Select New ListItem(prov("provincia"), prov("id"))
    'Aggiungo i nuovi elementi alla dropdownlist
    ddl.Items.AddRange(province.ToArray())
    'E se per caso il valore precedente è ancora nell'elenco, lo reimposto come valore selezionato
    If Not ddl.Items.FindByValue(valore) Is Nothing Then ddl.SelectedValue = valore
End Sub

Protected Sub comune_Load(sender As Object, e As EventArgs)
    'Qui faccio lo stesso per il comune
    Dim ddl = CType(sender, DropDownList)
    Dim valore = ddl.SelectedValue
    ddl.Items.Clear()
    comuniSource.SelectParameters(0).DefaultValue = OttieniValore("id_provincia")
    Dim comuni = From prov In comuniSource.Select(New DataSourceSelectArguments()).Cast(Of DataRowView)()
                   Select New ListItem(prov("comune"), prov("id"))
    ddl.Items.AddRange(comuni.ToArray())
    If Not ddl.Items.FindByValue(valore) Is Nothing Then ddl.SelectedValue = valore
End Sub

Protected Function OttieniValore(chiave As String) As Object
    'Vedi tu se trovi un modo di recuperare il valore delle dropdownlist in maniera consistente
    'Qui io ho dovuto distinguere due casi. 
    If (Page.IsPostBack) Then
        Return CType(dettaglioView.FindControl(chiave.Replace("id_", "")), DropDownList).SelectedValue
    Else
        Return CType(dettaglioView.DataItem, DataRowView)(chiave)
    End If
End Function


Infine, i tuoi provinciaSource e comuneSource dovranno avere un SelectParameter di tipo <asp:Parameter per id_nazione e id_provincia, rispettivamente.

Probabilmente avrai notato che i controlli server ti aiutano nei casi più comuni, ma quando vuoi fare qualcosa di più complicato, ecco che iniziano i problemi. Succede quando si baratta il controllo completo sull'applicazione per la rapidità di sviluppo.
Asp.Net WebForms è perfettamente valido ancora oggi, ma ci sono situazioni in cui vuoi spaccare il capello e avere le migliori performance. In quel caso, sviluppare un'applicazione con MVC può essere la scelta da preferire.

ciao
Modificato da BrightSoul il 15 dicembre 2012 20.40 -

Enjoy learning and just keep making
54 messaggi dal 13 settembre 2010
Ciao,
hai ragione, qui il professore si è divertito ad ingarbugliare molto la situazione!
Comunque tutto molto chiaro, in parte la strada che mi hai suggerito l'avevo percorsa ma poi mi son perso come uno scemo...
Quindi ho fatto come indicato ed effettivamente funziona, i dati della ddl (figlia) si aggiornano correttamente!
L'unica cosa è che quando vado a fare l'update mi esce questo errore
http://postimage.org/image/gh82r8db9/
come se non riuscisse a ripopolare la ddl nella detailsview una volta effettuato l'update...

Per quanto riguarda l'evitare il postback della pagina, sono concorde con te per l'utilizzo di ajax. Secondo te se uso il contentemplate con lo scriptmanager è buona cosa? Oppure come dovrei procedere? Creo una pagina .js intermedia che mi va a chiamare un'altra pagina .aspx con funzioni specifiche?

Anche per l'MVC ti do pienamente ragione, è tanto diverso da Asp.Net WebForm?

Grazie infinite!
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao, buona domenica!

gdalbell ha scritto:

come se non riuscisse a ripopolare la ddl nella detailsview una volta effettuato l'update...

già, infatti. Quella funzione OttieniValore non è granché, vedi se riesci a farle recuperare i valori corretti di id_nazione e id_provincia in ogni situazione.
In alternativa, questo problema puoi risolverlo mettendo una pezza: appena l'update è avvenuto, reindirizza l'utente.
Quindi, gestisci l'evento ItemUpdated del DetailsView e fai una ridirezione.
Protected Sub dettaglioView_ItemUpdated(sender As Object, e As DetailsViewUpdatedEventArgs)
    Response.Redirect(Request.Url.PathAndQuery)
End Sub


gdalbell ha scritto:

Secondo te se uso il contentemplate con lo scriptmanager è buona cosa?

Sì e no. Il beneficio sarebbe limitato al fatto che la pagina si aggiornerà solo parzialmente, evitando così un postback completo.
Ma i dati del form vengono inviati tutti comunque, compreso il viewstate che *può* arrivare a raggiungere dimensioni importanti.
Per prendere una decisione bisogna capire come verrà usata la tua applicazione. Se si tratta di un pannello di gestione intranet, allora forse non vale neanche la pena di usare ajax perché la connessione con il server avviene su cavi ethernet da almeno 100 Mb, e la latenza di rete sarà quasi nulla. Potresti lasciare tutto com'è, non bisogna modificare la propria applicazione in nome delle buone pratiche di sviluppo, ma solo quando la modifica produce anche un valore tangibile per l'utente.

Tuttavia, se i tuoi utenti si collegano da internet, specie se da un dispositivo mobile 3G, allora la loro esperienza d'uso potrebbe peggiorare.

In questo caso è molto importante ridurre al minimo i dati che viaggiano tra client e server e usare UpdatePanel + ScriptManager non darà comunque un risultato soddisfacente.

Proverei a fare una richiesta ajax ad un HttpHandler .ashx scritto da me. Nella richiesta gli passerei dei parametri tipo ?id_provincia=2 per farmi restituire tutti i comuni di quella provincia. Poi, via javascript, leggerei i comuni e li aggiungerei come opzioni alla dropdownlist.

Ora, Asp.Net Webforms ha un meccanismo anti-tampering che al postback produrrà un errore, se avevi manipolato la dropdownlist col javascript e avevi selezionato un valore non compreso nell'elenco originale, generato lato server.

Allora forse proverei ad usare l'espressione Bind() su un HiddenField e lascerei che le dropdownlists fossero delle semplici <select> html.
In questo modo, col javascript puoi manipolare la <select> come vuoi, dato che non hanno runat="server" e perciò non sono soggette al meccanismo anti-tampering.
Quando poi l'utente seleziona un comune diverso, risponderai all'evento javascript onchange per copiare il valore dentro il campo hidden.

Questo espediente non sarebbe necessario in MVC perché là i controlli server di Asp.Net non si usano.
Rinunciare all'enorme produttività che quei controlli ti danno, in un primo tempo potrebbe lasciarti spiazzato. Via via si impara ad apprezzare la maggiore vicinanza al codice html, e la migliore testabilità del progetto. Si ha un senso di pulizia ed efficienza.

Comunque, non è che MVC soppianti WebForms. Ci sono situazioni in cui ti chiedono di sviluppare in tempi estremamente rapidi e in questo caso i controlli WebForms ti offrono un incredibile aiuto.

ciao

Enjoy learning and just keep making
54 messaggi dal 13 settembre 2010
Ciao, buona domenica a te!
Il problema utilizzando
Protected Sub dettaglioView_ItemUpdated(sender As Object, e As DetailsViewUpdatedEventArgs)
    Response.Redirect(Request.Url.PathAndQuery)
End Sub

è che dopo l'aggiornamento notifico all'utente l'avvenuta modifica con
  risposta.Visible=true
  risposta.Text=risposta.Text+"Immobile salvato correttamente<br />"

e così facendo la pagina viene ricaricata e non riesco a notificare nulla.

Per migliorare come dici te la funziona OttieniValore come potrei procedere? Da dove me lo tiro fuori il valore degli id corretti nella situazione post update?
Secondo me così come è scritta dovrebbe funzionare perchè quando va a fare (per la ddl provincia ad esempio)
Return CType(dettaglioView.FindControl(chiave.Replace("id_", "")), DropDownList).SelectedValue

la ddl nazione dovrebbe essere già caricata e quindi dovrebbe riuscire a recuperare il selectedvalue...
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,

gdalbell ha scritto:

la pagina viene ricaricata e non riesco a notificare nulla.

Subito prima della ridirezione, valorizza una variabile di sessione. Al ricaricamento di pagina, se questa variabile è presente, mostri l'avviso e subito la svuoti.

gdalbell ha scritto:

la ddl nazione dovrebbe essere già caricata e quindi dovrebbe riuscire a recuperare il selectedvalue...

Mmh, mi sembra che subito dopo l'update il SelectedValue fosse vuoto. Usa il debugger di Visual Studio per capire cosa sta succedendo: metti un breakpoint dentro OttieniValore e poi, quando l'esecuzione si interrompe, ispeziona il contenuto del SelectedValue.

In alternativa, potresti leggere il valore dalla collezione Request.Form.
Return Request.Form("dettaglioView$" & chiave.Replace("id_", ""))

Ispeziona il codice html della tua pagina e verifica che l'attributo name della nazione sia effettivamente dettaglioView$nazione. Se non lo è, bisogna adeguare la riga di codice che ho scritto qui sopra.

ciao

Enjoy learning and just keep making

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.