Not
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
I tidigare självstudier såg vi hur GridView-kontrollen gör det enkelt att redigera och ta bort textdata. I den här självstudien ser vi hur GridView-kontrollen också gör det möjligt att redigera och ta bort binära data, oavsett om dessa binära data sparas i databasen eller lagras i filsystemet.
Inledning
Under de senaste tre handledningarna har vi lagt till en hel del funktioner för att arbeta med binära data. Vi började med att lägga till en BrochurePath kolumn i Categories tabellen och uppdaterade arkitekturen därefter. Vi har också lagt till metoder för dataåtkomstskikt och affärslogiklager för att arbeta med tabellen Categoriess befintliga Picture kolumn, som innehåller binärt innehåll i en bildfil. Vi har skapat webbsidor för att presentera binära data i en GridView-nedladdningslänk för broschyren, med kategorins bild som visas i ett <img> element och har lagt till en DetailsView så att användare kan lägga till en ny kategori och ladda upp dess broschyr- och bilddata.
Allt som återstår att implementera är möjligheten att redigera och ta bort befintliga kategorier, vilket vi kommer att göra i den här självstudien med hjälp av GridViews inbyggda redigerings- och borttagningsfunktioner. När du redigerar en kategori kan användaren ladda upp en ny bild eller låta kategorin fortsätta att använda den befintliga. För broschyren kan de antingen välja att använda den befintliga broschyren, ladda upp en ny broschyr eller ange att kategorin inte längre har en broschyr associerad med den. Låt oss komma igång!
Steg 1: Uppdatera dataåtkomstskiktet
DAL har automatiskt genererade Insert, Updateoch Delete metoder, men dessa metoder genererades baserat på CategoriesTableAdapter huvudfrågan, som inte innehåller Picture kolumnen. Metoderna Insert och Update innehåller därför inte parametrar för att ange binära data för kategorins bild. Precis som i föregående självstudie måste vi skapa en ny TableAdapter-metod för att uppdatera Categories tabellen när du anger binära data.
Öppna Den typerade datauppsättningen och högerklicka på CategoriesTableAdapter s-huvudet från Designer och välj Lägg till fråga på snabbmenyn för att starta konfigurationsguiden för TableAdapter-frågor. Den här guiden börjar med att fråga oss hur TableAdapter-frågan ska komma åt databasen. Välj Använd SQL-instruktioner och klicka på Nästa. Nästa steg frågar efter vilken typ av fråga som ska genereras. Eftersom vi skapar en fråga för att lägga till en ny post i Categories tabellen väljer du UPPDATERA och klickar på Nästa.
Bild 1: Välj alternativet UPPDATERA (Klicka om du vill visa en bild i full storlek)
Nu måste vi specificera SQL-instruktionen UPDATE . Guiden föreslår automatiskt en UPDATE instruktion som motsvarar TableAdapter-huvudfrågan (en som uppdaterar CategoryNamevärdena , Descriptionoch BrochurePath ). Ändra instruktionen så att Picture kolumnen inkluderas tillsammans med en @Picture parameter, så här:
UPDATE [Categories] SET
[CategoryName] = @CategoryName,
[Description] = @Description,
[BrochurePath] = @BrochurePath ,
[Picture] = @Picture
WHERE (([CategoryID] = @Original_CategoryID))
Den sista skärmen i guiden ber oss att namnge den nya TableAdapter-metoden. Ange UpdateWithPicture och klicka på Slutför.
Bild 2: Namnge metoden New TableAdapter UpdateWithPicture (Klicka om du vill visa en bild i full storlek)
Steg 2: Lägga till metoder för affärslogiklagret
Förutom att uppdatera DAL måste vi uppdatera BLL:n så att den innehåller metoder för att uppdatera och ta bort en kategori. Det här är de metoder som ska anropas från presentationsskiktet.
För att ta bort en kategori kan vi använda den automatiskt genererade metoden CategoriesTableAdapterDelete. Lägg till följande metod i klassen 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;
}
I den här självstudien ska vi skapa två metoder för att uppdatera en kategori – en som förväntar sig binära bilddata och anropar den UpdateWithPicture metod som vi just lade till CategoriesTableAdapter i och en annan som bara CategoryNameaccepterar värdena , Descriptionoch BrochurePath och använder CategoriesTableAdapter klassens automatiskt genererade Update instruktion. Logiken bakom att använda två metoder är att en användare i vissa fall kanske vill uppdatera kategorins bild tillsammans med sina andra fält, i vilket fall användaren måste ladda upp den nya bilden. Den uppladdade bildens binära data kan sedan användas i -instruktionen UPDATE . I andra fall kanske användaren bara är intresserad av att uppdatera, till exempel, namnet och beskrivningen. Men om instruktionen UPDATE förväntar sig binära data för Picture kolumnen också, skulle vi behöva ange den informationen också. Detta skulle kräva en extra resa till databasen för att hämta bilddata för den post som redigeras. Därför vill vi ha två UPDATE metoder. Affärslogiklagret avgör vilken som ska användas baserat på om bilddata tillhandahålls när kategorin uppdateras.
För att underlätta detta lägger du till två metoder i CategoriesBLL klassen, båda med namnet UpdateCategory. Den första ska acceptera tre string s, en byte matris och en int som indataparametrar, den andra, bara tre string s och en int. Indataparametrarna string är för kategorins namn, beskrivning och broschyrfilsökväg, matrisen byte är för det binära innehållet i kategorins bild och int identifierar posten CategoryID som ska uppdateras. Observera att den första överlagringen anropar den andra om den införda byte matrisen är 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;
}
Steg 3: Kopiera över funktionerna Infoga och visa
I föregående självstudie skapade vi en sida med namnet UploadInDetailsView.aspx som listade alla kategorier i en GridView och tillhandahöll en DetailsView för att lägga till nya kategorier i systemet. I den här självstudien utökar vi GridView till att omfatta stöd för redigering och borttagning. I stället för att fortsätta arbeta från UploadInDetailsView.aspx, ska vi placera denna självstudiekursens ändringar på sidan UpdatingAndDeleting.aspx från samma mapp, ~/BinaryData. Kopiera och klistra in deklarativ kod och kod från UploadInDetailsView.aspx till UpdatingAndDeleting.aspx.
Börja med att UploadInDetailsView.aspx öppna sidan. Kopiera all deklarativ syntax i elementet <asp:Content> enligt bild 3.
UpdatingAndDeleting.aspx Öppna och klistra sedan in den här markeringen i dess <asp:Content> element. På samma sätt kopierar du koden från kod-bakom-klassen på UploadInDetailsView.aspx-sidan till UpdatingAndDeleting.aspx.
Bild 3: Kopiera deklarativ markering från UploadInDetailsView.aspx (Klicka om du vill visa en bild i full storlek)
När du har kopierat över deklarativ kod och markering går du till UpdatingAndDeleting.aspx. Du ska se samma utdata och ha samma användarupplevelse som sidan UploadInDetailsView.aspx från föregående handledning.
Steg 4: Lägga till stöd för borttagning i ObjectDataSource och GridView
Som vi gick igenom i självstudien En översikt över infogning, uppdatering och borttagning av data , tillhandahåller GridView inbyggda borttagningsfunktioner och dessa funktioner kan aktiveras i krysset i en kryssruta om rutnätets underliggande datakälla stöder borttagning. ObjectDataSource som GridView är bunden till (CategoriesDataSource) stöder för närvarande inte borttagning.
Du kan åtgärda detta genom att klicka på alternativet Konfigurera datakälla från ObjectDataSources smarta tagg för att starta guiden. Den första skärmen visar att ObjectDataSource har konfigurerats för att fungera med CategoriesBLL klassen. Tryck på Nästa. För närvarande anges endast ObjectDataSource s InsertMethod och SelectMethod egenskaper. Guiden fyllde dock automatiskt i listrutorna på flikarna UPDATE med UpdateCategory metoden och DELETE med DeleteCategory metoden. Det beror på att vi i CategoriesBLL klassen markerade dessa metoder med DataObjectMethodAttribute standardmetoderna för uppdatering och borttagning.
För tillfället anger du fliken UPPDATERA listruta till (Ingen), men lämnar fliken TA BORT listruta inställd på DeleteCategory. Vi går tillbaka till det här guiden/programmet i Steg 6 för att lägga till uppdateringsstöd.
Bild 4: Konfigurera ObjectDataSource att använda DeleteCategory metoden (Klicka om du vill visa en bild i full storlek)
Anmärkning
När du har slutfört guiden kan Visual Studio fråga om du vill uppdatera fält och nycklar, vilket återskapar fälten för datawebbkontroller. Välj Nej, eftersom om du väljer Ja skrivs eventuella fältanpassningar över.
ObjectDataSource innehåller nu ett värde för dess DeleteMethod egenskap samt en DeleteParameter. Kom ihåg att när du använder guiden för att ange metoderna anger Visual Studio egenskapen OldValuesParameterFormatStringObjectDataSource till original_{0} , vilket orsakar problem med anropen för uppdaterings- och borttagningsmetoden. Därför rensar du antingen den här egenskapen helt eller återställer den till standardvärdet . {0} Om du behöver fräscha upp minnet om den här ObjectDataSource-egenskapen kan du läsa självstudien En översikt över hur du infogar, uppdaterar och tar bort data.
När du har slutfört guiden och åtgärdat OldValuesParameterFormatStringbör ObjectDataSources deklarativa markering se ut ungefär så här:
<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>
När du har konfigurerat ObjectDataSource lägger du till borttagningsfunktioner i GridView genom att markera kryssrutan Aktivera borttagning från GridViews smarta tagg. Då läggs ett CommandField till i GridView vars ShowDeleteButton egenskap är inställd på true.
Bild 5: Aktivera stöd för att ta bort i GridView (Klicka om du vill visa en bild i full storlek)
Ta en stund att testa borttagningsfunktionen. Det finns en sekundärnyckel mellan Products tabellens CategoryID och Categories tabellens CategoryID, så du får ett undantag om begränsningsöverträdelse för sekundärnyckel om du försöker ta bort någon av de åtta första kategorierna. Om du vill testa den här funktionen lägger du till en ny kategori som ger både en broschyr och en bild. Min testkategori, som visas i bild 6, innehåller en testbroschyrfil med namnet Test.pdf och en testbild. Bild 7 visar GridView när testkategorin har lagts till.
Bild 6: Lägg till en testkategori med en broschyr och bild (Klicka om du vill visa en bild i full storlek)
Bild 7: När du har infogat testkategorin visas den i GridView (Klicka om du vill visa en bild i full storlek)
Uppdatera Solution Explorer i Visual Studio. Nu bör du se en ny fil i ~/Brochures mappen Test.pdf (se bild 8).
Klicka sedan på länken Ta bort på raden Testkategori, vilket gör att sidan skickas tillbaka och CategoriesBLL klassens DeleteCategory metod utlöses. Detta anropar DAL-metoden, Delete vilket gör att lämplig DELETE instruktion skickas till databasen. Sedan binds datan om till GridView och markeringen skickas tillbaka till klienten utan testkategorin.
Även om borttagningsarbetsflödet har tagit bort posten Testkategori från Categories tabellen, tog den inte bort broschyrfilen från webbserverns filsystem. Uppdatera Solution Explorer så ser du att den Test.pdf fortfarande finns i ~/Brochures mappen.
Bild 8: Filen Test.pdf togs inte bort från webbserverns filsystem
Steg 5: Ta bort broschyrfilen för borttagen kategori
En av nackdelarna med att lagra binära data utanför databasen är att du måste vidta extra åtgärder för att rensa filerna när den associerade databasposten tas bort. GridView och ObjectDataSource tillhandahåller händelser som utlöses både före och efter att borttagningskommandot har utförts. Vi behöver faktiskt skapa händelsehanterare för både för- och efteråtgärdshändelserna. Innan posten Categories tas bort måste vi fastställa dess PDF-filsökväg, men vi vill inte ta bort PDF-filen förrän kategorin är borttagen ifall det uppstår något undantag och kategorin inte blir borttagen.
GridView-händelsen RowDeleting utlöses innan kommandot ObjectDataSources borttagning har anropats, medan händelsenRowDeleted utlöses efteråt. Skapa händelsehanterare för dessa två händelser med hjälp av följande kod:
// 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));
}
}
}
RowDeleting I händelsehanteraren CategoryID hämtas raden som tas bort från GridView-samlingenDataKeys, som kan nås i den här händelsehanteraren via e.Keys samlingen. Därefter CategoriesBLL anropas klassen s GetCategoryByCategoryID(categoryID) för att returnera information om posten som tas bort. Om det returnerade CategoriesDataRow objektet har ett icke-värdeNULL``BrochurePath lagras det i sidvariabeln deletedCategorysPdfPath så att filen kan tas bort i RowDeleted händelsehanteraren.
Anmärkning
I stället för att hämta BrochurePath information om Categories-posten som tas bort i RowDeleting-händelsehanteraren, skulle vi kunna lägga till BrochurePath i GridViews DataKeyNames-egenskap och komma åt postvärdet genom e.Keys-samlingen. Detta skulle öka storleken på GridView-vytillståndet något, men minska mängden kod som behövs och spara en resa till databasen.
När ObjectDataSources underliggande borttagningskommando har anropats utlöses GridViews RowDeleted händelsehanterare. Om det inte fanns några undantag vid borttagning av data och det finns ett värde för deletedCategorysPdfPathtas PDF-filen bort från filsystemet. Observera att den här extra koden inte behövs för att rensa kategorins binära data som är associerade med bilden. Det beror på att bilddata lagras direkt i databasen, så om Categories du tar bort raden tas även den kategorins bilddata bort.
När du har lagt till de två händelsehanterarna kör du det här testfallet igen. När du tar bort kategorin tas även dess associerade PDF bort.
Att uppdatera en befintlig posts associerade binära data medför vissa intressanta utmaningar. Resten av den här handledningen utforskar att lägga till funktioner för uppdatering i broschyren och bilden. Steg 6 utforskar tekniker för att uppdatera broschyrinformationen medan steg 7 tittar på att uppdatera bilden.
Steg 6: Uppdatera en kategoribroschyr
Som beskrivs i självstudien En översikt över infogning, uppdatering och borttagning av data erbjuder GridView inbyggt redigeringsstöd på radnivå som kan implementeras med kryssmarkeringen i en kryssruta om dess underliggande datakälla är korrekt konfigurerad.
CategoriesDataSource ObjectDataSource har för närvarande inte konfigurerats för att inkludera uppdateringsstöd, så vi lägger till det.
Klicka på länken Konfigurera datakälla från guiden ObjectDataSource och gå vidare till det andra steget. På grund av den DataObjectMethodAttribute som används i CategoriesBLL bör listrutan UPDATE automatiskt fyllas med den UpdateCategory överbelastning som accepterar fyra ingångsparametrar (för alla kolumner utom Picture). Ändra detta så att överbelastningen används med fem parametrar.
Bild 9: Konfigurera ObjectDataSource att använda en metod UpdateCategory som innehåller en parameter för Picture (Klicka för att visa en bild i full storlek)
ObjectDataSource innehåller nu ett värde för dess UpdateMethod egenskap samt motsvarande UpdateParameter . Som du ser i steg 4 anger Visual Studio egenskapen ObjectDataSource till OldValuesParameterFormatStringoriginal_{0} när du använder guiden Konfigurera datakälla. Detta orsakar problem med anropen för uppdaterings- och borttagningsmetoden. Därför rensar du antingen den här egenskapen helt eller återställer den till standardvärdet . {0}
När du har slutfört guiden och åtgärdat OldValuesParameterFormatStringbör ObjectDataSources deklarativa markering se ut så här:
<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>
Om du vill aktivera GridViews inbyggda redigeringsfunktioner kontrollerar du alternativet Aktivera redigering från GridViews smarta tagg. Då anges egenskapen ShowEditButtonCommandField till true , vilket resulterar i att knappen Redigera läggs till (och knapparna Uppdatera och Avbryt för raden som redigeras).
Bild 10: Konfigurera GridView till Stöd för redigering (Klicka om du vill visa en bild i full storlek)
Gå till sidan via en webbläsare och klicka på någon av radernas Redigera-knappar. Dessa CategoryName och Description BoundFields visas som textrutor.
BrochurePath TemplateField saknar en EditItemTemplate, så den fortsätter att visa sin ItemTemplate länk till broschyren.
Picture ImageField visas som en TextBox vars Text-egenskap tilldelas värdet av ImageField:s DataImageUrlField-värde, i det här fallet CategoryID.
Bild 11: GridView saknar ett redigeringsgränssnitt för (BrochurePath)
AnpassaBrochurePaths-redigeringsgränssnittet
Vi måste skapa ett redigeringsgränssnitt för BrochurePath TemplateField, ett som gör att användaren kan antingen:
- Lämna kategoriens broschyr as-is.
- Uppdatera kategoribroschyren genom att ladda upp en ny broschyr, eller
- Ta bort kategoribroschyren helt och hållet (om kategorin inte längre har en associerad broschyr).
Vi behöver också uppdatera Picture ImageFields redigeringsgränssnitt, men vi kommer till det i steg 7.
Från GridViews smarta tagg klickar du på länken Redigera mallar och väljer BrochurePath TemplateFields EditItemTemplate i listrutan. Lägg till en RadioButtonList-webbkontroll i den här mallen och ange dess ID egenskap till BrochureOptions och dess AutoPostBack egenskap till true. I fönstret Egenskaper klickar du på ellipserna i Items egenskapen, som tar upp ListItem samlingsredigeraren. Lägg till följande tre alternativ med Value s 1, 2 respektive 3:
- Använd aktuell broschyr
- Ta bort aktuell broschyr
- Ladda upp ny broschyr
Ställ in egenskapen för första ListItem s Selected till true.
Bild 12: Lägg till tre ListItem i RadioButtonList
Under RadioButtonList lägger du till en FileUpload-kontroll med namnet BrochureUpload. Ange egenskapen Visible till false.
Bild 13: Lägg till en RadioButtonList- och FileUpload-kontroll i EditItemTemplate (Klicka om du vill visa en bild i full storlek)
Den här RadioButtonList innehåller de tre alternativen för användaren. Tanken är att kontrollen FileUpload endast visas om det sista alternativet, Ladda upp ny broschyr, har valts. För att åstadkomma detta skapar du en händelsehanterare för händelsen RadioButtonList s SelectedIndexChanged och lägger till följande kod:
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");
}
Eftersom kontrollerna RadioButtonList och FileUpload finns i en mall måste vi skriva lite kod för att programmatiskt få åtkomst till dessa kontroller. Händelsehanteraren SelectedIndexChanged får en referens till RadioButtonList via sender-parametern. För att hämta FileUpload-kontrollen måste vi hämta den överordnade kontrollen i RadioButtonList och använda FindControl("controlID") metoden därifrån. När vi har en referens till både kontrollerna RadioButtonList och FileUpload anges kontrollen FileUpload s Visible-egenskap till true endast om RadioButtonList s SelectedValue är lika med 3, vilket är Value för att Ladda upp ny broschyr ListItem.
Med den här koden på plats, ta en stund att testa redigeringsgränssnittet. Klicka på knappen Redigera för en rad. Ursprungligen bör alternativet Använd aktuell broschyr väljas. Om du ändrar det valda indexet får du ett återanrop. Om det tredje alternativet är markerat visas kontrollen FileUpload, annars är det dolt. Bild 14 visar redigeringsgränssnittet när knappen Redigera först klickas. Bild 15 visar gränssnittet när alternativet Ladda upp ny broschyr har valts.
Bild 14: Ursprungligen är alternativet Använd aktuell broschyr valt (Klicka om du vill visa en bild i full storlek)
Bild 15: Om du väljer alternativet Ladda upp ny broschyr visas kontrollen FileUpload (Klicka om du vill visa en bild i full storlek)
Spara broschyrfilen och uppdateraBrochurePathkolumnen
När du klickar på knappen Uppdatera i GridView utlöses händelsen RowUpdating . Kommandot ObjectDataSources uppdatering anropas och sedan utlöses GridView-händelsen RowUpdated . Precis som med arbetsflödet för borttagning måste vi skapa händelsehanterare för båda dessa händelser. I RowUpdating-händelsehanteraren måste vi fastställa vilken åtgärd som ska vidtas baserat på SelectedValue för BrochureOptions RadioButtonList:
- Om
SelectedValueär 1 vill vi fortsätta att använda sammaBrochurePathinställning. Därför måste vi ange parametern ObjectDataSourcesbrochurePathtill det befintligaBrochurePathvärdet för posten som uppdateras. Parametern ObjectDataSourcesbrochurePathkan anges med hjälp ave.NewValues["brochurePath"] = value. - Om
SelectedValueär 2, så vill vi ange postensBrochurePathvärde tillNULL. Detta kan åstadkommas genom att ange parametern ObjectDataSource sbrochurePathtillNothing, vilket resulterar i att en databasNULLanvänds i -instruktionenUPDATE. Om det finns en befintlig broschyrfil som tas bort måste vi ta bort den befintliga filen. Vi vill dock bara göra detta om uppdateringen slutförs utan att skapa ett undantag. - Om
SelectedValueär 3 vill vi se till att användaren har laddat upp en PDF-fil och att spara den i filsystemet och uppdatera registrensBrochurePathkolumnvärde. Om det dessutom finns en befintlig broschyrfil som ersätts måste vi ta bort den tidigare filen. Vi vill dock bara göra detta om uppdateringen slutförs utan att skapa ett undantag.
Stegen måste slutföras när RadioButtonList s SelectedValue är 3 är praktiskt taget identiska med de som används av DetailsView-händelsehanteraren ItemInserting . Den här händelsehanteraren körs när en ny kategoripost läggs till från DetailsView-kontrollen som vi lade till i föregående självstudie. Därför uppmanas vi att omstrukturera den här funktionen till separata metoder. Mer specifikt flyttade jag ut de vanliga funktionerna till två metoder:
-
ProcessBrochureUpload(FileUpload, out bool)accepterar som indata en FileUpload-kontrollinstans och ett booleskt utdatavärde som anger om borttagnings- eller redigeringsåtgärden ska fortsätta eller om den ska avbrytas på grund av ett verifieringsfel. Den här metoden returnerar sökvägen till den sparade filen ellernullom ingen fil har sparats. -
DeleteRememberedBrochurePathtar bort filen som anges av sökvägen i sidvariabelndeletedCategorysPdfPathomdeletedCategorysPdfPathinte ärnull.
Koden för dessa två metoder följer. Observera likheten mellan ProcessBrochureUpload och DetailsView-händelsehanteraren ItemInserting från föregående självstudie. I den här självstudien har jag uppdaterat DetailsViews händelsehanterare för att använda dessa nya metoder. Ladda ned koden som är associerad med den här självstudien för att se ändringarna i DetailsViews händelsehanterare.
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));
}
}
GridViews RowUpdating- och RowUpdated-händelsehanterare använder metoderna ProcessBrochureUpload och DeleteRememberedBrochurePath, som den följande koden visar:
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();
}
}
Observera hur RowUpdating händelsehanteraren använder en serie villkorsstyrda instruktioner för att utföra lämplig åtgärd baserat på BrochureOptions RadioButtonLists egenskapsvärde SelectedValue.
Med den här koden på plats kan du redigera en kategori och låta den använda sin aktuella broschyr, inte använda någon broschyr eller ladda upp en ny. Prova det. Ange brytpunkter i RowUpdating händelsehanterarna och RowUpdated för att få en uppfattning om arbetsflödet.
Steg 7: Ladda upp en ny bild
ImageFields Picture redigeringsgränssnitt återges som en textruta ifylld med värdet från dess DataImageUrlField egenskap. Under redigeringsarbetsflödet skickar GridView en parameter till ObjectDataSource där parameternamn motsvarar egenskapen ImageFields DataImageUrlField och parametervärdet motsvarar värdet som angavs i textrutan i redigeringsgränssnittet. Det här beteendet är lämpligt när bilden sparas som en fil i filsystemet och DataImageUrlField innehåller den fullständiga URL:en för avbildningen. Under sådana omständigheter visar redigeringsgränssnittet bildens URL i textrutan, som användaren kan ändra och har sparat tillbaka till databasen. Det här standardgränssnittet tillåter inte att användaren laddar upp en ny bild, men låter dem ändra url:en för bilden från det aktuella värdet till en annan. I denna självstudie räcker dock inte det vanliga redigeringsgränssnittet för ImageField eftersom den binära Picture-datan lagras direkt i databasen och DataImageUrlField-egenskapen endast innehåller CategoryID.
För att bättre förstå vad som händer i vår självstudiekurs när en användare redigerar en rad med ett ImageField bör du överväga följande exempel: en användare redigerar en rad med CategoryID 10, vilket gör Picture att ImageField återges som en textruta med värdet 10. Anta att användaren ändrar värdet i den här textrutan till 50 och klickar på knappen Uppdatera. En postback inträffar och GridView skapar först en parameter med namnet CategoryID med värdet 50. Men innan GridView skickar den här parametern (och parametrarna CategoryName och Description ) lägger den till värdena från DataKeys samlingen. Därför skriver den över parametern CategoryID med den aktuella radens underliggande CategoryID värde, 10. Kort sagt har ImageFields gränssnitt för redigering ingen inverkan på redigeringsarbetsflödet för den här självstudien eftersom namnen på ImageFields DataImageUrlField egenskap och rutnätets DataKey värde är en och samma.
ImageField gör det enkelt att visa en bild baserat på databasdata, men vi vill inte ange en textruta i redigeringsgränssnittet. I stället vill vi erbjuda en FileUpload-kontroll som slutanvändaren kan använda för att ändra kategorins bild. Till skillnad från värdet BrochurePath, har vi bestämt oss för att för de här självstudierna kräva att varje kategori måste ha en bild. Därför behöver vi inte låta användaren ange att det inte finns någon associerad bild som användaren antingen kan ladda upp en ny bild eller lämna den aktuella bilden as-is.
För att anpassa ImageFields redigeringsgränssnitt måste vi konvertera det till ett TemplateField. Från GridViews smarta tagg klickar du på länken Redigera kolumner, väljer ImageField och klickar på länken Konvertera det här fältet till en TemplateField.
Bild 16: Konvertera ImageField till ett mallfält
Om du konverterar ImageField till ett TemplateField på det här sättet genereras ett TemplateField med två mallar. Som följande deklarativa syntax visar, innehåller ItemTemplate en bildwebbkontroll vars ImageUrl-egenskap tilldelas med hjälp av databindningssyntax baserat på ImageFields DataImageUrlField och DataImageUrlFormatString-egenskaper.
EditItemTemplate Innehåller en TextBox vars Text egenskap är bunden till det värde som anges av DataImageUrlField egenskapen.
<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>
Vi måste uppdatera EditItemTemplate för att använda en FileUpload-kontroll. Klicka på länken Redigera mallar i GridViews smarta tagg och välj Picture sedan TemplateFields EditItemTemplate i listrutan. I mallen bör du se en textruta ta bort detta. Dra sedan en FileUpload-kontroll från verktygslådan till mallen och ange dess ID till PictureUpload. Lägg också till texten Om du vill ändra kategorins bild anger du en ny bild. Om du vill behålla kategorins bild på samma sätt lämnar du även fältet tomt i mallen.
Bild 17: Lägg till en FileUpload-kontroll i EditItemTemplate (Klicka om du vill visa en bild i full storlek)
När du har anpassat redigeringsgränssnittet visar du förloppet i en webbläsare. När du visar en rad i endast läsläge visas kategorins bild som den var tidigare, men om du klickar på knappen Redigera visas bildkolumnen som text med en FileUpload-kontroll.
Bild 18: Redigeringsgränssnittet innehåller en FileUpload-kontroll (Klicka om du vill visa en bild i full storlek)
Kom ihåg att ObjectDataSource har konfigurerats för att anropa CategoriesBLL klassens UpdateCategory metod som accepterar den binära bilddatan som en byte matris som indata. Om den här matrisen har ett null värde anropas dock den alternativa UpdateCategory överlagringen, som utfärdar SQL-instruktionen UPDATEPicture som inte ändrar kolumnen, vilket lämnar kategorins aktuella bild intakt. Därför måste vi i GridViews RowUpdating händelsehanterare programmatiskt referera PictureUpload till FileUpload-kontrollen och avgöra om en fil har laddats upp. Om en inte har laddats upp vill vi inte ange något värde för parametern picture . Om en fil däremot laddades upp i PictureUpload kontrollen FileUpload vill vi se till att den är en JPG-fil. I så fall kan vi skicka dess binära innehåll till ObjectDataSource via parametern picture .
Precis som med koden som används i steg 6 finns mycket av den kod som behövs här redan i DetailsView-händelsehanteraren ItemInserting . Därför har jag omstrukturerat de gemensamma funktionerna till en ny metod, ValidPictureUpload, och uppdaterat händelsehanteraren så att den ItemInserting använder den här metoden.
Lägg till följande kod i början av GridView-händelsehanteraren RowUpdating . Det är viktigt att den här koden kommer före koden som sparar broschyrfilen eftersom vi inte vill spara broschyren i webbserverns filsystem om en ogiltig bildfil laddas upp.
// 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;
}
}
Metoden ValidPictureUpload(FileUpload) tar in en FileUpload-kontroll som den enda indataparametern och kontrollerar filnamnstillägget för uppladdad fil för att säkerställa att den uppladdade filen är en JPG. Den anropas bara om en bildfil laddas upp. Om ingen fil laddas upp anges inte bildparametern och använder därför standardvärdet null. Om en bild laddades upp och ValidPictureUpload returnerar truetilldelas parametern picture binära data för den uppladdade bilden. Om metoden returnerar falseavbryts uppdateringsarbetsflödet och händelsehanteraren avslutas.
Metodkoden ValidPictureUpload(FileUpload) , som omstrukturerades från DetailsViews ItemInserting händelsehanterare, följer:
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;
}
}
Steg 8: Ersätta de ursprungliga kategoribilderna med JPG:er
Kom ihåg att de ursprungliga åtta kategoribilderna är bitmappsfiler som omsluts i en OLE-rubrik. Nu när vi har lagt till funktionen för att redigera en befintlig posts bild, ta en stund och ersätt dessa bitmap-bilder med JPG-bilder. Om du vill fortsätta att använda de aktuella kategoribilderna kan du konvertera dem till JPG:er genom att utföra följande steg:
- Spara bitmappsbilderna på hårddisken. Gå till sidan
UpdatingAndDeleting.aspxi webbläsaren och för var och en av de första åtta kategorierna högerklickar du på bilden och väljer att spara bilden. - Öppna bilden i valfri bildredigerare. Du kan till exempel använda Microsoft Paint.
- Spara bitmappen som en JPG-avbildning.
- Uppdatera kategorins bild via redigeringsgränssnittet med hjälp av JPG-filen.
När du har redigerat en kategori och laddat upp JPG-bilden återges inte bilden i webbläsaren eftersom DisplayCategoryPicture.aspx sidan tar bort de första 78 byteen från bilderna i de första åtta kategorierna. Åtgärda detta genom att ta bort koden som utför OLE-sidhuvudborttagningen. När du har gjort detta DisplayCategoryPicture.aspx``Page_Load bör händelsehanteraren bara ha följande kod:
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);
}
Anmärkning
Sidinfognings UpdatingAndDeleting.aspx - och redigeringsgränssnitt kan behöva lite mer arbete.
CategoryName, Description BoundFields i DetailsView och GridView ska konverteras till TemplateFields. Eftersom CategoryName inte tillåter NULL värden bör en RequiredFieldValidator läggas till. Och textrutan Description bör förmodligen konverteras till en textruta med flera rader. Jag lämnar dessa sista detaljer som en övning för dig.
Sammanfattning
Den här självstudien avslutar vår genomgång av att arbeta med binära data. I den här självstudien och de föregående tre såg vi hur binära data kan lagras i filsystemet eller direkt i databasen. En användare tillhandahåller binära data till systemet genom att välja en fil från hårddisken och ladda upp den till webbservern, där den kan lagras i filsystemet eller infogas i databasen. ASP.NET 2.0 innehåller en FileUpload-kontroll som gör det enkelt att tillhandahålla ett sådant gränssnitt som dra och släpp. Som påpekas i självstudien Ladda upp filer är FileUpload-kontrollen endast väl lämpad för relativt små filuppladdningar, som helst inte bör överstiga en megabyte. Vi har också utforskat hur du associerar uppladdade data med den underliggande datamodellen, samt hur du redigerar och tar bort binära data från befintliga poster.
Nästa uppsättning självstudier utforskar olika cachelagringstekniker. Cachelagring ger ett sätt att förbättra programmets övergripande prestanda genom att ta resultaten från dyra åtgärder och lagra dem på en plats som kan nås snabbare.
Lycka till med programmerandet!
Om författaren
Scott Mitchell, författare till sju ASP/ASP.NET-böcker och grundare av 4GuysFromRolla.com, har arbetat med Microsofts webbtekniker sedan 1998. Scott arbetar som oberoende konsult, tränare och författare. Hans senaste bok är Sams Teach Yourself ASP.NET 2.0 på 24 timmar. Han kan nås på mitchell@4GuysFromRolla.com.
Särskilt tack till
Den här självstudieserien granskades av många användbara granskare. Ansvarig granskare för den här självstudien var Teresa Murphy. Vill du granska mina kommande MSDN-artiklar? Om så är fallet, hör av dig på mitchell@4GuysFromRolla.com.