Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Nelle esercitazioni precedenti è stato illustrato come il controllo GridView semplifica la modifica e l'eliminazione dei dati di testo. In questa esercitazione viene illustrato come il controllo GridView consente anche di modificare ed eliminare dati binari, indipendentemente dal fatto che tali dati binari vengano salvati nel database o archiviati nel file system.
Introduzione
Nelle ultime tre esercitazioni sono state aggiunte alcune funzionalità per l'uso dei dati binari. È stata avviata l'aggiunta di una BrochurePath colonna alla Categories tabella e l'architettura è stata aggiornata di conseguenza. Sono stati aggiunti anche i metodi livello di accesso ai dati e al livello di logica di business per lavorare con la colonna esistente Picture della tabella Categories, che contiene il contenuto binario di un file di immagine. Abbiamo creato pagine Web per presentare i dati binari in un collegamento di download di GridView per la brochure, con l'immagine della categoria mostrata in un <img> elemento e abbiamo aggiunto un controllo DetailsView per consentire agli utenti di aggiungere una nuova categoria e caricare i dati della brochure e dell'immagine.
Tutto ciò che rimane da implementare è la possibilità di modificare ed eliminare le categorie esistenti, che verranno eseguite in questa esercitazione usando la modifica e l'eliminazione predefinite di GridView. Quando si modifica una categoria, l'utente potrà caricare facoltativamente una nuova immagine o fare in modo che la categoria continui a usare quella esistente. Per la brochure, possono scegliere di utilizzare la brochure esistente, di caricare una nuova brochure o di indicare che la categoria non ha più una brochure associata. Iniziamo!
Passaggio 1: Aggiornamento del livello di accesso ai dati
IL dal include metodi , Inserte generati automaticamenteUpdate, ma questi metodi sono stati generati in base alla Delete query principale, che non include la CategoriesTableAdapterPicture colonna. Pertanto, i Insert metodi e Update non includono parametri per specificare i dati binari per l'immagine della categoria. Come nell'esercitazione precedente, è necessario creare un nuovo metodo TableAdapter per aggiornare la Categories tabella quando si specificano dati binari.
Aprire il set di dati tipizzato e, da Progettazione, fare clic con il pulsante destro del mouse sull'intestazione CategoriesTableAdapter s e scegliere Aggiungi query dal menu di scelta rapida per avviare la Configurazione guidata query TableAdapter. Questa procedura guidata inizia chiedendo in che modo la query TableAdapter deve accedere al database. Scegliere Usa istruzioni SQL e fare clic su Avanti. Il passaggio successivo richiede la generazione del tipo di query. Poiché si sta creando una query per aggiungere un nuovo record alla Categories tabella, scegliere AGGIORNA e fare clic su Avanti.
Figura 1: Selezionare l'opzione UPDATE (fare clic per visualizzare l'immagine a dimensione intera)
È ora necessario specificare l'istruzione UPDATE SQL. La procedura guidata suggerisce automaticamente un'istruzione UPDATE corrispondente alla query principale di TableAdapter (che aggiorna i valori CategoryName, Description e BrochurePath). Modificare l'istruzione in modo che la Picture colonna sia inclusa insieme a un @Picture parametro, come illustrato di seguito:
UPDATE [Categories] SET
[CategoryName] = @CategoryName,
[Description] = @Description,
[BrochurePath] = @BrochurePath ,
[Picture] = @Picture
WHERE (([CategoryID] = @Original_CategoryID))
La schermata finale della procedura guidata chiede di assegnare un nome al nuovo metodo TableAdapter. Immettere UpdateWithPicture e fare clic su Fine.
Figura 2: Assegnare un nome al nuovo metodo UpdateWithPicture TableAdapter (fare clic per visualizzare l'immagine a dimensione intera)
Passaggio 2: Aggiunta dei metodi del livello di logica aziendale
Oltre ad aggiornare il DAL, bisogna aggiornare il BLL per includere i metodi per aggiornare ed eliminare una categoria. Questi sono i metodi che verranno richiamati dal livello presentazione.
Per eliminare una categoria, è possibile usare il CategoriesTableAdapter metodo generato automaticamente Delete . Aggiungere il metodo seguente alla classe CategoriesBLL:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteCategory(int categoryID)
{
int rowsAffected = Adapter.Delete(categoryID);
// Return true if precisely one row was deleted, otherwise false
return rowsAffected == 1;
}
Per questa esercitazione, creiamo due metodi per l'aggiornamento di una categoria: uno che prevede i dati dell'immagine binaria e richiama il metodo UpdateWithPicture appena aggiunto a CategoriesTableAdapter, e un altro che accetta solo i valori CategoryName, Description e BrochurePath e usa l'istruzione CategoriesTableAdapter generata automaticamente della classe Update. La logica alla base dell'uso di due metodi è che in alcune circostanze, un utente potrebbe voler aggiornare l'immagine della categoria insieme agli altri campi, nel qual caso l'utente dovrà caricare la nuova immagine. I dati binari dell'immagine caricati possono quindi essere usati nell'istruzione UPDATE . In altri casi, l'utente potrebbe essere interessato solo all'aggiornamento, ad esempio il nome e la descrizione. Tuttavia, se l'istruzione UPDATE prevede anche i dati binari per la Picture colonna, è necessario fornire tali informazioni. Ciò richiederebbe un ulteriore viaggio al database per riportare i dati dell'immagine per il record da modificare. Pertanto, vogliamo due UPDATE metodi. Il livello di logica di business determinerà quale usare in base all'eventuale disponibilità di dati immagine durante l'aggiornamento della categoria.
Per semplificare questa operazione, aggiungere due metodi alla CategoriesBLL classe , entrambi denominati UpdateCategory. Il primo deve accettare tre string s, una byte matrice e un int come parametri di input; il secondo, solo tre string s e un oggetto int. I string parametri di input sono relativi al nome, alla descrizione e al percorso del file della brochure della categoria, alla byte matrice relativa al contenuto binario dell'immagine della categoria e all'identificatore intCategoryID del record da aggiornare. Si noti che il primo overload richiama il secondo se la matrice passata byte è null:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, false)]
public bool UpdateCategory(string categoryName, string description,
string brochurePath, byte[] picture, int categoryID)
{
// If no picture is specified, use other overload
if (picture == null)
return UpdateCategory(categoryName, description, brochurePath, categoryID);
// Update picture, as well
int rowsAffected = Adapter.UpdateWithPicture
(categoryName, description, brochurePath, picture, categoryID);
// Return true if precisely one row was updated, otherwise false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateCategory(string categoryName, string description,
string brochurePath, int categoryID)
{
int rowsAffected = Adapter.Update
(categoryName, description, brochurePath, categoryID);
// Return true if precisely one row was updated, otherwise false
return rowsAffected == 1;
}
Passaggio 3: Copiare la funzionalità di inserimento e visualizzazione
Nell'esercitazione precedente è stata creata una pagina denominata UploadInDetailsView.aspx che elenca tutte le categorie in gridView e ha fornito un controllo DetailsView per aggiungere nuove categorie al sistema. In questa esercitazione estenderemo il GridView per includere la funzionalità di modifica ed eliminazione. Anziché continuare a lavorare da UploadInDetailsView.aspx, inseriamo invece le modifiche di questo tutorial nella pagina UpdatingAndDeleting.aspx dalla stessa cartella, ~/BinaryData. Copiare e incollare il markup dichiarativo e il codice da UploadInDetailsView.aspx a UpdatingAndDeleting.aspx.
Per iniziare, aprire la UploadInDetailsView.aspx pagina. Copiare tutta la sintassi dichiarativa all'interno dell'elemento <asp:Content> , come illustrato nella figura 3.
UpdatingAndDeleting.aspx Aprire e incollare quindi il markup all'interno del relativo <asp:Content> elemento. Analogamente, copia il codice dalla classe code-behind della pagina UploadInDetailsView.aspx a UpdatingAndDeleting.aspx.
Figura 3: Copiare il markup dichiarativo da UploadInDetailsView.aspx (fare clic per visualizzare l'immagine a dimensione intera)
Dopo aver copiato il markup dichiarativo e il codice, vai su UpdatingAndDeleting.aspx. Dovrebbe essere visualizzato lo stesso output e avere la stessa esperienza utente come con la pagina UploadInDetailsView.aspx del tutorial precedente.
Passaggio 4: Aggiunta del supporto per l'eliminazione a ObjectDataSource e GridView
Come illustrato di nuovo nell'esercitazione Panoramica dell'inserimento, dell'aggiornamento e dell'eliminazione di dati , GridView offre funzionalità di eliminazione predefinite e queste funzionalità possono essere abilitate al segno di spunta di una casella di controllo se l'origine dati sottostante della griglia supporta l'eliminazione. L'oggetto ObjectDataSource a cui GridView è associato a (CategoriesDataSource) non supporta l'eliminazione.
Per risolvere questo problema, fare clic sull'opzione Configura origine dati dallo smart tag ObjectDataSource s per avviare la procedura guidata. La prima schermata mostra che ObjectDataSource è configurato per l'uso con la CategoriesBLL classe . Premere Avanti. Attualmente, sono specificate solo le proprietà InsertMethod s e SelectMethod di ObjectDataSource. Tuttavia, la procedura guidata ha popolato automaticamente gli elenchi a discesa nelle schede UPDATE e DELETE con i metodi UpdateCategory e DeleteCategory, rispettivamente. Ciò è dovuto al CategoriesBLL fatto che nella classe sono stati contrassegnati questi metodi usando DataObjectMethodAttribute come metodi predefiniti per l'aggiornamento e l'eliminazione.
Per il momento, impostare l'elenco a discesa della scheda UPDATE su (Nessuno), ma lasciare l'elenco a discesa della scheda DELETE impostato su DeleteCategory. Si tornerà a questa procedura guidata nel passaggio 6 per aggiungere il supporto per l'aggiornamento.
Figura 4: Configurare ObjectDataSource per l'uso del metodo (DeleteCategory a dimensione intera)
Annotazioni
Al termine della procedura guidata, Visual Studio potrebbe chiedere se si desidera aggiornare campi e chiavi, che rigenerano i campi dei controlli Web dati. Scegliere No, perché scegliendo Sì si sovrascriveranno le personalizzazioni dei campi che potrebbero essere state apportate.
ObjectDataSource includerà ora un valore per la relativa DeleteMethod proprietà e un oggetto DeleteParameter. Tenere presente che quando si usa la procedura guidata per specificare i metodi, Visual Studio imposta la proprietà OldValuesParameterFormatStringObjectDataSource su original_{0} , che causa problemi con le chiamate al metodo update ed delete. Pertanto, cancellare completamente questa proprietà o reimpostarla sull'impostazione predefinita, {0}. Se è necessario rinfrescare la memoria su questa proprietà ObjectDataSource, vedere l'esercitazione Panoramica sull'inserimento, l'aggiornamento e l'eliminazione di dati.
Dopo aver corretto OldValuesParameterFormatString e completato la procedura guidata, il markup dichiarativo di ObjectDataSource dovrebbe assomigliare al seguente:
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetCategories"
TypeName="CategoriesBLL" InsertMethod="InsertWithPicture"
DeleteMethod="DeleteCategory">
<InsertParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
</InsertParameters>
<DeleteParameters>
<asp:Parameter Name="categoryID" Type="Int32" />
</DeleteParameters>
</asp:ObjectDataSource>
Dopo aver configurato ObjectDataSource, aggiungere le funzionalità di eliminazione a GridView selezionando la casella di controllo Abilita eliminazione dallo smart tag gridView. Verrà aggiunto un oggetto CommandField a GridView la cui ShowDeleteButton proprietà è impostata su true.
Figura 5: Abilitare il supporto per l'eliminazione in GridView (fare clic per visualizzare l'immagine a dimensione intera)
Prenditi un momento per provare la funzionalità di eliminazione. Esiste una chiave esterna tra la Products tabella s CategoryID e la Categories tabella s CategoryID, pertanto si otterrà un'eccezione di violazione del vincolo di chiave esterna se si tenta di eliminare una delle prime otto categorie. Per testare questa funzionalità, aggiungere una nuova categoria, fornendo sia una brochure che un'immagine. La categoria di test, illustrata nella figura 6, include un file di brochure di test denominato Test.pdf e un'immagine di test. La figura 7 mostra GridView dopo l'aggiunta della categoria di test.
Figura 6: Aggiungere una categoria di test con una brochure e un'immagine (fare clic per visualizzare l'immagine a dimensione intera)
Figura 7: Dopo aver inserito la categoria di test, viene visualizzata in GridView (fare clic per visualizzare l'immagine a dimensione intera)
In Visual Studio, aggiornare l'esplora soluzioni. Ora dovresti vedere un nuovo file nella cartella ~/Brochures, Test.pdf (vedi Figura 8).
Quindi, fai clic sul collegamento Elimina nella riga Categoria di test, provocando il postback della pagina e attivando la classe CategoriesBLL e il metodo DeleteCategory. Verrà richiamato il metodo del DAL Delete, causando l'invio dell'istruzione appropriata DELETE al database. I dati vengono quindi riassociati a GridView e il codice viene inviato nuovamente al client con la Categoria di test non più presente.
Anche se il flusso di lavoro di eliminazione ha rimosso correttamente il record di Categoria di Test dalla Categories tabella, il file della brochure non è stato rimosso dal file system del server web. Aggiorna Esplora soluzioni e noterai che Test.pdf è ancora presente nella cartella ~/Brochures.
Figura 8: Il Test.pdf file non è stato eliminato dal file system del server Web
Passaggio 5: Rimozione del file brochure della categoria eliminata
Uno degli aspetti negativi dell'archiviazione di dati binari esterni al database è che è necessario eseguire ulteriori passaggi per pulire questi file quando il record di database associato viene eliminato. GridView e ObjectDataSource forniscono eventi che vengono attivati sia prima che dopo l'esecuzione del comando delete. In realtà è necessario creare i gestori degli eventi per gli eventi di pre-azione e post-azione. Prima che il Categories record venga eliminato, è necessario determinare il percorso del file PDF, ma non si vuole eliminare il PDF prima che la categoria venga eliminata in caso di eccezione e la categoria non viene eliminata.
L'evento gridView RowDeleting viene generato prima che sia stato richiamato il comando delete di ObjectDataSource, mentre il relativo RowDeleted evento viene generato dopo. Creare gestori eventi per questi due eventi usando il codice seguente:
// A page variable to "remember" the deleted category's BrochurePath value
string deletedCategorysPdfPath = null;
protected void Categories_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
// Determine the PDF path for the category being deleted...
int categoryID = Convert.ToInt32(e.Keys["CategoryID"]);
CategoriesBLL categoryAPI = new CategoriesBLL();
Northwind.CategoriesDataTable categories =
categoryAPI.GetCategoryByCategoryID(categoryID);
Northwind.CategoriesRow category = categories[0];
if (category.IsBrochurePathNull())
deletedCategorysPdfPath = null;
else
deletedCategorysPdfPath = category.BrochurePath;
}
protected void Categories_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
// Delete the brochure file if there were no problems deleting the record
if (e.Exception == null)
{
// Is there a file to delete?
if (deletedCategorysPdfPath != null)
{
System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath));
}
}
}
Nel gestore eventi RowDeleting, l' CategoryID della riga da eliminare viene ottenuto dalla raccolta DataKeys di GridView, accessibile in questo gestore eventi tramite la raccolta e.Keys. Viene quindi richiamata la CategoriesBLL classe s GetCategoryByCategoryID(categoryID) per restituire informazioni sul record da eliminare. Se l'oggetto restituito CategoriesDataRow ha un valore non-NULL``BrochurePath, viene archiviato nella variabile della pagina deletedCategorysPdfPath in modo che il file possa essere eliminato nel gestore eventi RowDeleted.
Annotazioni
Anziché recuperare i BrochurePath dettagli per il Categories record eliminato nel RowDeleting gestore eventi, in alternativa potremmo aggiungere il BrochurePath alla proprietà DataKeyNames di GridView e accedere al valore del record tramite la raccolta e.Keys. In questo modo si aumentano leggermente le dimensioni dello stato di visualizzazione di GridView, ma si riduce la quantità di codice necessaria e si salva un viaggio nel database.
Dopo aver richiamato il comando delete sottostante di ObjectDataSource, viene attivato il gestore eventi di RowDeleted GridView. Se non sono state rilevate eccezioni nell'eliminazione dei dati e esiste un valore per deletedCategorysPdfPath, il PDF viene eliminato dal file system. Si noti che questo codice aggiuntivo non è necessario per pulire i dati binari della categoria associati alla relativa immagine. Poiché i dati dell'immagine vengono archiviati direttamente nel database, l'eliminazione della Categories riga elimina anche i dati dell'immagine della categoria.
Dopo aver aggiunto i due gestori eventi, eseguire di nuovo questo test case. Quando si elimina la categoria, viene eliminato anche il pdf associato.
L'aggiornamento di dati binari associati a un record esistente offre alcune problematiche interessanti. La parte restante di questa esercitazione illustra l'aggiunta di funzionalità di aggiornamento alla brochure e all'immagine. Il passaggio 6 illustra le tecniche per aggiornare le informazioni sulla brochure mentre il passaggio 7 esamina l'aggiornamento dell'immagine.
Passaggio 6: Aggiornamento della brochure di una categoria
Come illustrato nell'esercitazione Panoramica sull'inserimento, l'aggiornamento e l'eliminazione di dati , GridView offre il supporto predefinito per la modifica a livello di riga che può essere implementato dal segno di spunta di una casella di controllo se l'origine dati sottostante è configurata in modo appropriato. Attualmente ObjectDataSource CategoriesDataSource non è ancora configurato per includere il supporto per l'aggiornamento, quindi è possibile aggiungerlo.
Fare clic sul collegamento Configura origine dati dalla procedura guidata ObjectDataSource e procedere con il secondo passaggio. A causa dell'elemento DataObjectMethodAttribute usato in CategoriesBLL, l'elenco a discesa UPDATE deve essere popolato automaticamente con l'overload UpdateCategory che accetta quattro parametri di input (tranne Picture). Modificare questa impostazione in modo che usi l'overload con cinque parametri.
Figura 9: Configurare ObjectDataSource per l'uso del UpdateCategory metodo che include un parametro per Picture (fare clic per visualizzare l'immagine a dimensione intera)
ObjectDataSource includerà ora un valore per la relativa UpdateMethod proprietà, nonché i valori corrispondenti UpdateParameter . Come indicato nel passaggio 4, Visual Studio imposta la proprietà OldValuesParameterFormatString ObjectDataSource su original_{0} quando si usa la procedura guidata Configura origine dati. Ciò causerà problemi con le chiamate al metodo update ed delete. Pertanto, cancellare completamente questa proprietà o reimpostarla sull'impostazione predefinita, {0}.
Dopo aver completato la procedura guidata e corretto OldValuesParameterFormatString, il markup dichiarativo di ObjectDataSource dovrebbe essere simile al seguente:
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetCategories"
TypeName="CategoriesBLL" InsertMethod="InsertWithPicture"
DeleteMethod="DeleteCategory" UpdateMethod="UpdateCategory">
<InsertParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
</InsertParameters>
<DeleteParameters>
<asp:Parameter Name="categoryID" Type="Int32" />
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
<asp:Parameter Name="categoryID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
Per attivare le funzionalità di modifica predefinite di GridView, selezionare l'opzione Abilita modifica dallo smart tag gridView. Verrà impostata la proprietà ShowEditButtonCommandField su true , con conseguente aggiunta di un pulsante Modifica (e pulsanti Aggiorna e Annulla per la riga da modificare).
Figura 10: Configurare GridView per supportare la modifica (fare clic per visualizzare l'immagine a dimensione intera)
Visitare la pagina tramite un browser e fare clic su uno dei pulsanti Modifica della riga. Il CategoryName e il Description BoundFields sono visualizzati come caselle di testo. Il TemplateField BrochurePath manca di un EditItemTemplate, quindi continua a mostrare ItemTemplate un collegamento alla brochure. ImageField Picture esegue il rendering come TextBox la cui proprietà Text viene assegnata al valore della proprietà DataImageUrlField di ImageField, in questo caso CategoryID.
Figura 11: GridView non dispone di un'interfaccia di modifica per BrochurePath (fare clic per visualizzare l'immagine a dimensione intera)
Personalizzazione dell'interfacciaBrochurePathdi modifica
È necessario creare un'interfaccia di modifica per BrochurePath TemplateField, una che consente all'utente di:
- Lascia la brochure della categoria as-is,
- Aggiornare la brochure della categoria caricando una nuova brochure o
- Rimuovere completamente la brochure della categoria (nel caso in cui la categoria non abbia più una brochure associata).
È anche necessario aggiornare l'interfaccia Picture di modifica di ImageField, ma verrà visualizzata nel passaggio 7.
Dallo smart tag di GridView, fai clic sul collegamento Modifica modelli e seleziona il campo Template BrochurePath dal menu a tendina EditItemTemplate. Aggiungere un controllo Web RadioButtonList a questo modello, impostandone la ID proprietà su BrochureOptions e la relativa AutoPostBack proprietà su true. Nella finestra Proprietà, fare clic sul puntino di sospensione nella proprietà Items, che aprirà l'Editor della raccolta ListItem. Aggiungere le tre opzioni seguenti rispettivamente con Value s 1, 2 e 3:
- Utilizzare la brochure corrente
- Rimuovere la brochure corrente
- Caricare una nuova brochure
Impostare la prima ListItem proprietà Selected su true.
Figura 12: Aggiungere tre elementi a RadioButtonList
Sotto RadioButtonList aggiungere un controllo FileUpload denominato BrochureUpload. Impostare la proprietà Visible su false.
Figura 13: Aggiungere un controllo RadioButtonList e un controllo FileUpload al EditItemTemplate (fare clic per visualizzare l'immagine a dimensione intera)
Questo RadioButtonList fornisce tre opzioni per l'utente. L'idea è che il controllo FileUpload verrà visualizzato solo se è selezionata l'ultima opzione Carica nuova brochure. A tale scopo, creare un gestore eventi per l'evento RadioButtonList SelectedIndexChanged e aggiungere il codice seguente:
protected void BrochureOptions_SelectedIndexChanged(object sender, EventArgs e)
{
// Get a reference to the RadioButtonList and its Parent
RadioButtonList BrochureOptions = (RadioButtonList)sender;
Control parent = BrochureOptions.Parent;
// Now use FindControl("controlID") to get a reference of the
// FileUpload control
FileUpload BrochureUpload =
(FileUpload)parent.FindControl("BrochureUpload");
// Only show BrochureUpload if SelectedValue = "3"
BrochureUpload.Visible = (BrochureOptions.SelectedValue == "3");
}
Poiché i controlli RadioButtonList e FileUpload si trovano all'interno di un modello, è necessario scrivere un po' di codice per accedere a questi controlli a livello di codice. Al SelectedIndexChanged gestore eventi viene passato un riferimento di RadioButtonList nel sender parametro di input. Per ottenere il controllo FileUpload, dobbiamo ottenere il controllo padre di RadioButtonList e usare il metodo FindControl("controlID") da questa posizione. Dopo aver ottenuto un riferimento ai controlli RadioButtonList e FileUpload, la proprietà del Visible controllo FileUpload è impostata su true solo se la RadioButtonList è SelectedValue uguale a 3, che è il caso del Value caricamento di una nuova brochure ListItem.
Con questo codice implementato, prenditi un momento per provare l'interfaccia di modifica. Fare clic sul pulsante Modifica per una riga. Inizialmente, è consigliabile selezionare l'opzione Usa brochure corrente. La modifica dell'indice selezionato causa un postback. Se è selezionata la terza opzione, viene visualizzato il controllo FileUpload, altrimenti è nascosto. La figura 14 mostra l'interfaccia di modifica quando si fa clic sul pulsante Modifica; La figura 15 mostra l'interfaccia dopo l'opzione Carica nuova brochure selezionata.
Figura 14: Inizialmente, l'opzione Usa brochure corrente è selezionata (fare clic per visualizzare l'immagine a dimensione intera)
Figura 15: Scelta dell'opzione Carica nuova brochure Visualizza il controllo FileUpload (fare clic per visualizzare l'immagine a dimensione intera)
Salvare il file della brochure e aggiornare laBrochurePathcolonna
Quando si fa clic sul pulsante Update di GridView, si attiva il suo evento RowUpdating. Il comando di aggiornamento di ObjectDataSource viene richiamato e quindi viene generato l'evento gridView.RowUpdated Analogamente al flusso di lavoro di eliminazione, è necessario creare gestori eventi per entrambi questi eventi. Nel gestore eventi RowUpdating, dobbiamo determinare quale azione intraprendere in base allo stato di SelectedValue della lista di pulsanti radio BrochureOptions RadioButtonList.
- Se è
SelectedValue1, si vuole continuare a usare la stessaBrochurePathimpostazione. È quindi necessario impostare il parametro ObjectDataSource sulbrochurePathvalore esistenteBrochurePathdel record da aggiornare. Il parametro sbrochurePathObjectDataSource può essere impostato tramitee.NewValues["brochurePath"] = value. - Se è
SelectedValue2, si vuole impostare il valore delBrochurePathrecord suNULL. Questa operazione può essere eseguita impostando il parametro ObjectDataSource subrochurePath, che comporta l'uso di un databaseNothingnell'istruzioneNULL. Se è presente un file brochure esistente che viene rimosso, è necessario eliminare il file esistente. Tuttavia, si vuole eseguire questa operazione solo se l'aggiornamento viene completato senza generare un'eccezione. - Se è
SelectedValue3, si vuole assicurarsi che l'utente abbia caricato un file PDF e quindi salvarlo nel file system e aggiornare il valore della colonna delBrochurePathrecord. Inoltre, se è presente un file brochure esistente che viene sostituito, è necessario eliminare il file precedente. Tuttavia, si vuole eseguire questa operazione solo se l'aggiornamento viene completato senza generare un'eccezione.
I passaggi da completare quando RadioButtonList s SelectedValue è 3 sono praticamente identici a quelli usati dal gestore eventi detailsView.ItemInserting Questo gestore eventi viene eseguito quando viene aggiunto un nuovo record di categoria dal controllo DetailsView aggiunto nell'esercitazione precedente. Pertanto, è opportuno che noi facciamo il refactoring di questa funzionalità suddividendola in metodi separati. In particolare, ho spostato le funzionalità comuni in due metodi:
-
ProcessBrochureUpload(FileUpload, out bool)accetta come input un'istanza del controllo FileUpload e un valore booleano di output che specifica se l'operazione di eliminazione o modifica deve continuare o se deve essere annullata a causa di un errore di convalida. Questo metodo restituisce il percorso del file salvato onullse non è stato salvato alcun file. -
DeleteRememberedBrochurePathelimina il file specificato dal percorso nella variabiledeletedCategorysPdfPathdella pagina sedeletedCategorysPdfPathnon ènull.
Di seguito è riportato il codice per questi due metodi. Si noti la somiglianza tra ProcessBrochureUpload e il gestore eventi di DetailsView nell'esercitazione precedente ItemInserting. In questa esercitazione sono stati aggiornati i gestori eventi di DetailsView per usare questi nuovi metodi. Scaricare il codice associato a questa esercitazione per visualizzare le modifiche apportate ai gestori eventi di DetailsView.
private string ProcessBrochureUpload
(FileUpload BrochureUpload, out bool CancelOperation)
{
CancelOperation = false; // by default, do not cancel operation
if (BrochureUpload.HasFile)
{
// Make sure that a PDF has been uploaded
if (string.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName),
".pdf", true) != 0)
{
UploadWarning.Text =
"Only PDF documents may be used for a category's brochure.";
UploadWarning.Visible = true;
CancelOperation = true;
return null;
}
const string BrochureDirectory = "~/Brochures/";
string brochurePath = BrochureDirectory + BrochureUpload.FileName;
string fileNameWithoutExtension =
System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
int iteration = 1;
while (System.IO.File.Exists(Server.MapPath(brochurePath)))
{
brochurePath = string.Concat(BrochureDirectory, fileNameWithoutExtension,
"-", iteration, ".pdf");
iteration++;
}
// Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath));
return brochurePath;
}
else
{
// No file uploaded
return null;
}
}
private void DeleteRememberedBrochurePath()
{
// Is there a file to delete?
if (deletedCategorysPdfPath != null)
{
System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath));
}
}
I gestori degli eventi di GridView RowUpdating e RowUpdated usano i metodi ProcessBrochureUpload e DeleteRememberedBrochurePath, come illustrato nel codice seguente.
protected void Categories_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
// Reference the RadioButtonList
RadioButtonList BrochureOptions =
(RadioButtonList)Categories.Rows[e.RowIndex].FindControl("BrochureOptions");
// Get BrochurePath information about the record being updated
int categoryID = Convert.ToInt32(e.Keys["CategoryID"]);
CategoriesBLL categoryAPI = new CategoriesBLL();
Northwind.CategoriesDataTable categories =
categoryAPI.GetCategoryByCategoryID(categoryID);
Northwind.CategoriesRow category = categories[0];
if (BrochureOptions.SelectedValue == "1")
{
// Use current value for BrochurePath
if (category.IsBrochurePathNull())
e.NewValues["brochurePath"] = null;
else
e.NewValues["brochurePath"] = category.BrochurePath;
}
else if (BrochureOptions.SelectedValue == "2")
{
// Remove the current brochure (set it to NULL in the database)
e.NewValues["brochurePath"] = null;
}
else if (BrochureOptions.SelectedValue == "3")
{
// Reference the BrochurePath FileUpload control
FileUpload BrochureUpload =
(FileUpload)Categories.Rows[e.RowIndex].FindControl("BrochureUpload");
// Process the BrochureUpload
bool cancelOperation = false;
e.NewValues["brochurePath"] =
ProcessBrochureUpload(BrochureUpload, out cancelOperation);
e.Cancel = cancelOperation;
}
else
{
// Unknown value!
throw new ApplicationException(
string.Format("Invalid BrochureOptions value, {0}",
BrochureOptions.SelectedValue));
}
if (BrochureOptions.SelectedValue == "2" ||
BrochureOptions.SelectedValue == "3")
{
// "Remember" that we need to delete the old PDF file
if (category.IsBrochurePathNull())
deletedCategorysPdfPath = null;
else
deletedCategorysPdfPath = category.BrochurePath;
}
}
protected void Categories_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
// If there were no problems and we updated the PDF file,
// then delete the existing one
if (e.Exception == null)
{
DeleteRememberedBrochurePath();
}
}
Si noti che il RowUpdating gestore eventi usa una serie di istruzioni condizionali per eseguire l'azione appropriata in base al valore della BrochureOptions proprietà RadioButtonList.SelectedValue
Con questo codice in uso, è possibile modificare una categoria e usare la sua brochure attuale, non utilizzare la brochure o caricarne una nuova. Vai avanti e provalo. Imposta i punti di interruzione nei gestori degli eventi RowUpdating e RowUpdated per ottenere un'idea del flusso di lavoro.
Passaggio 7: Caricamento di una nuova immagine
L'interfaccia di modifica di Picture ImageField viene visualizzata come una casella di testo che appare popolata con il valore della sua proprietà DataImageUrlField. Durante il flusso di lavoro di modifica, GridView passa un parametro a ObjectDataSource con il nome del parametro il valore della proprietà ImageField s DataImageUrlField e il valore del parametro immesso nella casella di testo nell'interfaccia di modifica. Questo comportamento è adatto quando l'immagine viene salvata come file nel file system e DataImageUrlField contiene l'URL completo dell'immagine. In tali circostanze, l'interfaccia di modifica visualizza l'URL dell'immagine nella casella di testo, che l'utente può modificare e aver salvato di nuovo nel database. Concessa, questa interfaccia predefinita non consente all'utente di caricare una nuova immagine, ma consente di modificare l'URL dell'immagine dal valore corrente a un altro. Per questa esercitazione, tuttavia, l'interfaccia di modifica predefinita di ImageField non è sufficiente perché i Picture dati binari vengono archiviati direttamente nel database e la DataImageUrlField proprietà contiene solo .CategoryID
Per comprendere meglio cosa accade nell'esercitazione quando un utente modifica una riga con un oggetto ImageField, si consideri l'esempio seguente: un utente modifica una riga con CategoryID 10, causando il rendering di Picture ImageField come casella di testo con il valore 10. Si supponga che l'utente cambi il valore in questa casella di testo su 50 e faccia clic sul pulsante Aggiorna. Si verifica un postback e GridView crea inizialmente un parametro denominato CategoryID con il valore 50. Tuttavia, prima che GridView invii questo parametro (e i CategoryName parametri e Description ), aggiunge i valori della DataKeys raccolta. Pertanto, sostituisce il valore sottostante CategoryID del parametro CategoryID con quello della riga corrente, ovvero 10. In breve, l'interfaccia di modifica di ImageField non ha alcun effetto sul flusso di lavoro di modifica per questa esercitazione perché i nomi della proprietà ImageField e DataImageUrlField il valore della DataKey griglia sono uno nello stesso.
Anche se ImageField semplifica la visualizzazione di un'immagine basata sui dati del database, non si vuole fornire una casella di testo nell'interfaccia di modifica. Si vuole invece offrire un controllo FileUpload che l'utente finale può usare per modificare l'immagine della categoria. A differenza del BrochurePath valore, per queste esercitazioni abbiamo deciso di richiedere che ogni categoria debba avere un'immagine. Pertanto, non è necessario consentire all'utente di indicare che non esiste un'immagine associata che l'utente può caricare una nuova immagine o lasciare l'immagine corrente as-is.
Per personalizzare l'interfaccia di modifica di ImageField, è necessario convertirla in un oggetto TemplateField. Dallo smart tag di GridView, fare clic sul collegamento Modifica colonne, selezionare ImageField e fare clic sul collegamento Converti questo campo in un campo Template.
Figura 16: Convertire imageField in un campo modello
La conversione di ImageField in un oggetto TemplateField in questo modo genera un oggetto TemplateField con due modelli. Come illustrato nella sintassi dichiarativa seguente, ItemTemplate contiene un controllo Web Image la cui ImageUrl proprietà viene assegnata usando la sintassi di associazione dati in base alle proprietà e DataImageUrlField imageFieldDataImageUrlFormatString.
EditItemTemplate contiene una TextBox il cui Text è associato al valore specificato dalla proprietà DataImageUrlField.
<asp:TemplateField>
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server"
Text='<%# Eval("CategoryID") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Image ID="Image1" runat="server"
ImageUrl='<%# Eval("CategoryID",
"DisplayCategoryPicture.aspx?CategoryID={0}") %>' />
</ItemTemplate>
</asp:TemplateField>
È necessario aggiornare il EditItemTemplate per usare un controllo FileUpload. Dallo smart tag GridView, fare clic sul collegamento Modifica modelli e quindi selezionare Picture dalla lista a discesa TemplateField EditItemTemplate. Nel modello dovrebbe essere visualizzato un controllo TextBox, rimuovilo. Successivamente, trascina un controllo FileUpload dalla casella degli strumenti nel modello e imposta la sua proprietà ID su PictureUpload. Aggiungere anche il testo Per modificare l'immagine della categoria, specificare una nuova immagine. Per mantenere invariata l'immagine della categoria, lascia vuoto anche il campo nel modello.
Figura 17: Aggiungere un controllo FileUpload a EditItemTemplate (fare clic per visualizzare l'immagine a dimensione intera)
Dopo aver personalizzato l'interfaccia di modifica, visualizzare lo stato di avanzamento in un browser. Quando si visualizza una riga in modalità di sola lettura, l'immagine della categoria viene mostrata come prima, ma facendo clic sul pulsante Modifica la colonna dell'immagine viene visualizzata come testo con un controllo di caricamento file.
Figura 18: L'interfaccia di modifica include un controllo FileUpload (fare clic per visualizzare l'immagine a dimensione intera)
Tenere presente che ObjectDataSource è configurato per chiamare il CategoriesBLL metodo della UpdateCategory classe che accetta come input i dati binari per l'immagine come byte matrice. Se questa matrice contiene un valore null, tuttavia, viene chiamato l'overload alternativo UpdateCategory, che emette l'istruzione SQL UPDATE che non modifica la colonna Picture, lasciando intatta l'immagine corrente della categoria. Pertanto, nel gestore eventi di RowUpdating GridView è necessario fare riferimento a livello di codice al PictureUpload controllo FileUpload e determinare se è stato caricato un file. Se non è stato caricato, non desideriamo specificare un valore per il parametro picture. D'altra parte, se un file è stato caricato nel PictureUpload controllo FileUpload, vogliamo assicurarsi che si tratti di un file JPG. In caso affermativo, è possibile inviare il relativo contenuto binario a ObjectDataSource tramite il picture parametro .
Analogamente al codice usato nel passaggio 6, gran parte del codice necessario esiste già nel gestore eventi di ItemInserting DetailsView. Di conseguenza, ho refattorizzato le funzionalità comuni in un nuovo metodo, ValidPictureUpload e ho aggiornato il ItemInserting gestore eventi per usare questo metodo.
Aggiungere il codice seguente all'inizio del gestore eventi di RowUpdating GridView. È importante che questo codice venga prima del codice che salva il file della brochure perché non si vuole salvare la brochure nel file system del server Web se viene caricato un file immagine non valido.
// Reference the PictureUpload FileUpload
FileUpload PictureUpload =
(FileUpload)Categories.Rows[e.RowIndex].FindControl("PictureUpload");
if (PictureUpload.HasFile)
{
// Make sure the picture upload is valid
if (ValidPictureUpload(PictureUpload))
{
e.NewValues["picture"] = PictureUpload.FileBytes;
}
else
{
// Invalid file upload, cancel update and exit event handler
e.Cancel = true;
return;
}
}
Il ValidPictureUpload(FileUpload) metodo accetta un controllo FileUpload come unico parametro di input e controlla l'estensione del file caricato per assicurarsi che il file caricato sia jpg; viene chiamato solo se viene caricato un file immagine. Se non viene caricato alcun file, il parametro picture non è impostato e quindi usa il valore predefinito di null. Se un'immagine è stata caricata e ValidPictureUpload restituisce true, al picture parametro vengono assegnati i dati binari dell'immagine caricata; se il metodo restituisce false, il flusso di lavoro di aggiornamento viene annullato e il gestore eventi è terminato.
Il codice del ValidPictureUpload(FileUpload) metodo, che è stato refactorizzato dal gestore eventi di ItemInserting DetailsView, è il seguente.
private bool ValidPictureUpload(FileUpload PictureUpload)
{
// Make sure that a JPG has been uploaded
if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName),
".jpg", true) != 0 &&
string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName),
".jpeg", true) != 0)
{
UploadWarning.Text =
"Only JPG documents may be used for a category's picture.";
UploadWarning.Visible = true;
return false;
}
else
{
return true;
}
}
Passaggio 8: Sostituzione delle immagini delle categorie originali con JPG
Tenere presente che le immagini delle otto categorie originali sono file bitmap avvolte in un'intestazione OLE. Ora che è stata aggiunta la funzionalità per modificare un'immagine di un record esistente, prenditi un momento per sostituire queste bitmap con JPG. Se si desidera continuare a usare le immagini di categoria correnti, è possibile convertirle in JPG seguendo questa procedura:
- Salvare le immagini bitmap nel disco rigido. Visitare la
UpdatingAndDeleting.aspxpagina nel browser e per ognuna delle prime otto categorie, fare clic con il pulsante destro del mouse sull'immagine e scegliere di salvare l'immagine. - Aprire l'immagine nell'editor di immagini preferito. Ad esempio, è possibile usare Microsoft Paint.
- Salvare la bitmap come immagine JPG.
- Aggiornare l'immagine della categoria tramite l'interfaccia di modifica, usando il file JPG.
Dopo aver modificato una categoria e caricato l'immagine JPG, l'immagine non verrà visualizzata nel browser perché la DisplayCategoryPicture.aspx pagina rimuove i primi 78 byte dalle immagini delle prime otto categorie. Risolvere questo problema rimuovendo il codice che esegue la rimozione dell'intestazione OLE. Dopo aver eseguito questa operazione, il DisplayCategoryPicture.aspx``Page_Load gestore eventi deve avere solo il codice seguente:
protected void Page_Load(object sender, EventArgs e)
{
int categoryID = Convert.ToInt32(Request.QueryString["CategoryID"]);
// Get information about the specified category
CategoriesBLL categoryAPI = new CategoriesBLL();
Northwind.CategoriesDataTable categories = _
categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID);
Northwind.CategoriesRow category = categories[0];
// For new categories, images are JPGs...
// Output HTTP headers providing information about the binary data
Response.ContentType = "image/jpeg";
// Output the binary data
Response.BinaryWrite(category.Picture);
}
Annotazioni
Le interfacce di inserimento e modifica della pagina UpdatingAndDeleting.aspx necessitano di qualche miglioramento. I CategoryName e Description BoundFields in DetailsView e GridView devono essere convertiti in TemplateFields. Poiché CategoryName non consente valori NULL , è necessario aggiungere un oggetto RequiredFieldValidator. E il Description TextBox dovrebbe essere probabilmente convertito in un TextBox a più righe. Lascio questi ritocchi finali come esercizio per te.
Riassunto
Questa esercitazione completa l'analisi dell'uso dei dati binari. In questa esercitazione e nelle tre precedenti è stato illustrato come archiviare i dati binari nel file system o direttamente all'interno del database. Un utente fornisce dati binari al sistema selezionando un file dal disco rigido e caricandolo nel server Web, in cui può essere archiviato nel file system o inserito nel database. ASP.NET 2.0 include un controllo FileUpload che rende facile fornire un'interfaccia del genere tramite trascinamento e rilascio. Tuttavia, come indicato nell'esercitazione Caricamento di file, il controllo FileUpload è adatto solo per caricamenti di file relativamente piccoli, idealmente senza superare un megabyte. È stato anche illustrato come associare i dati caricati al modello di dati sottostante, nonché come modificare ed eliminare i dati binari dai record esistenti.
Il prossimo set di esercitazioni esplora varie tecniche di memorizzazione nella cache. La memorizzazione nella cache consente di migliorare le prestazioni complessive di un'applicazione prendendo i risultati da operazioni costose e archiviandoli in una posizione a cui è possibile accedere più rapidamente.
Buon programmatori!
Informazioni sull'autore
Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, ha lavorato con le tecnologie Web Microsoft dal 1998. Scott lavora come consulente indipendente, formatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 2.0 in 24 ore. Può essere raggiunto a mitchell@4GuysFromRolla.com.
Grazie speciale a
Questa serie di esercitazioni è stata esaminata da molti revisori competenti. Il revisore principale di questo tutorial è stata Teresa Murphy. Si è interessati a esaminare i prossimi articoli MSDN? In tal caso, mandami un messaggio a mitchell@4GuysFromRolla.com.