更新及刪除現有的二進位資料 (VB)

作者 :Scott Mitchell

下載 PDF

在先前的教學課程中,我們瞭解 GridView 控件如何讓您輕鬆地編輯和刪除文字數據。 在本教學課程中,我們會看到 GridView 控件如何讓編輯和刪除二進位數據,無論是儲存在資料庫中還是儲存在文件系統中。

簡介

在過去三個教學課程中,我們新增了相當多的功能來處理二進位數據。 我們一開始將數據 BrochurePath 行新增至 Categories 數據表,並據以更新架構。 我們也新增了數據存取層和商業規則層方法,以使用 Categories 數據表的現有 Picture 數據行,其中包含圖像檔的二進位內容。 我們已建置網頁,以在 GridView 中呈現折頁簿的二進位數據,其中包含元素中顯示的 <img> 類別圖片,並新增 DetailsView,讓使用者新增類別並上傳其折頁簿和圖片數據。

所有仍要實作的功能都是能夠編輯和刪除現有的類別,我們將在本教學課程中使用 GridView 的內建編輯和刪除功能來完成。 編輯類別時,用戶可以選擇性地上傳新圖片,或讓類別繼續使用現有的圖片。 針對摺頁冊,他們可以選擇使用現有的摺頁冊、上傳新的摺頁冊,或指出該類別不再有與其相關聯的摺頁冊。 讓我們開始吧!

步驟 1:更新數據存取層

DAL 具有自動產生的、 和方法,但這些方法是根據不包含Picture數據行的主要查詢所產生CategoriesTableAdapterDeleteUpdateInsert 因此, InsertUpdate 方法不包含參數來指定類別圖片的二進位數據。 如同我們在上一個 教學課程中所做的一樣,我們需要建立新的 TableAdapter 方法,以在指定二進位數據時更新 Categories 數據表。

開啟 [具類型的數據集],然後從 [Designer],以滑鼠右鍵按兩下 CategoriesTableAdapter s 標頭,然後從操作功能表中選擇 [新增查詢] 以啟動 [TableAdapter 查詢設定精靈]。 此精靈一開始會詢問我們 TableAdapter 查詢應該如何存取資料庫。 選擇 [使用 SQL 語句],然後按 [下一步]。 下一個步驟會提示產生查詢的類型。 因為我們要建立查詢以將新記錄新增至 Categories 數據表,請選擇 [更新],然後按 [下一步]。

選取 [更新] 選項

圖 1:選取 UPDATE 選項 (按下即可檢視完整大小的映像)

我們現在需要指定 UPDATE SQL 語句。 精靈會自動建議 UPDATE 對應至 TableAdapter 主要查詢的語句, (更新 CategoryNameDescriptionBrochurePath 值) 。 變更 語句, Picture 讓數據行與 參數一 @Picture 起包含,如下所示:

UPDATE [Categories] SET 
    [CategoryName] = @CategoryName, 
    [Description] = @Description, 
    [BrochurePath] = @BrochurePath ,
    [Picture] = @Picture
WHERE (([CategoryID] = @Original_CategoryID))

精靈的最後一個畫面會要求我們命名新的 TableAdapter 方法。 輸入 UpdateWithPicture 並按兩下 [完成]。

將新的 TableAdapter 方法命名為 UpdateWithPicture

圖 2:將新的 TableAdapter 方法 UpdateWithPicture 命名 (按兩下即可檢視全大小影像)

步驟 2:新增商業規則層方法

除了更新 DAL 之外,我們需要更新 BLL,以包含更新和刪除類別的方法。 這些是從呈現層叫用的方法。

若要刪除類別,我們可以使用 CategoriesTableAdapter 自動產生的 Delete 方法。 將下列方法新增至 CategoriesBLL 類別:

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Delete, True)> _
Public Function DeleteCategory(ByVal categoryID As Integer) As Boolean
    Dim rowsAffected As Integer = Adapter.Delete(categoryID)
    ' Return true if precisely one row was deleted, otherwise false
    Return rowsAffected = 1
End Function

在本教學課程中,讓我們建立兩個方法來更新類別- 一個預期二進位圖片數據,並叫UpdateWithPictureCategoriesTableAdapter我們剛才新增至 CategoriesTableAdapter 的方法,另一個方法只CategoryName接受 、 DescriptionBrochurePath 值,並使用類別自動產生的Update語句。 使用兩種方法背後的原理是在某些情況下,使用者可能會想要更新類別的圖片及其其他字段,在此情況下,用戶必須上傳新圖片。 上傳的圖片二進位數據接著可以在語句中使用 UPDATE 。 在其他情況下,使用者可能只想要更新名稱與描述。 但是, UPDATE 如果語句也預期數據行的 Picture 二進位數據,我們也需要提供該資訊。 這需要額外的資料庫車程,才能傳回正在編輯之記錄的圖片數據。 因此,我們想要兩 UPDATE 種方法。 商業規則層會根據更新類別時是否提供圖片數據來決定要使用哪一個。

為了方便進行這項作業,請將兩個方法新增至 CategoriesBLL 類別,這兩個方法皆名為 UpdateCategory。 第一個應該接受三 StringByte 、陣列和 Integer 做為其輸入參數;第二個,只有三 String 個 和 Integer。 輸入 String 參數適用於類別的名稱、描述和折頁簿檔案路徑、 Byte 陣列適用於類別圖片的二進位內容,而 IntegerCategoryID 識別要更新之記錄的 。 請注意,如果傳入 Byte 的陣列為 ,第一個多載會叫用第二個 Nothing多載:

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Update, False)> _
Public Function UpdateCategory(categoryName As String, description As String, _
    brochurePath As String, picture() As Byte, categoryID As Integer) As Boolean
    
    ' If no picture is specified, use other overload
    If picture Is Nothing Then
        Return UpdateCategory(categoryName, description, brochurePath, categoryID)
    End If
    ' Update picture, as well
    Dim rowsAffected As Integer = Adapter.UpdateWithPicture _
        (categoryName, description, brochurePath, picture, categoryID)
    ' Return true if precisely one row was updated, otherwise false
    Return rowsAffected = 1
End Function
<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateCategory(categoryName As String, description As String, _
    brochurePath As String, categoryID As Integer) As Boolean
    Dim rowsAffected As Integer = Adapter.Update _
        (categoryName, description, brochurePath, categoryID)
    ' Return true if precisely one row was updated, otherwise false
    Return rowsAffected = 1
End Function

步驟 3:透過插入和檢視功能複製

上述教學課程中 ,我們建立了名為 UploadInDetailsView.aspx 的頁面,列出 GridView 中的所有類別,並提供 DetailsView 以將新類別新增至系統。 在本教學課程中,我們將擴充 GridView,以包含編輯和刪除支援。 請改為將本教學課程變更放在UpdatingAndDeleting.aspx相同資料夾的頁面,~/BinaryData而不是繼續從 UploadInDetailsView.aspx中工作。 將宣告式標記與程式代碼從 UploadInDetailsView.aspx 複製並貼到 UpdatingAndDeleting.aspx

從開啟 UploadInDetailsView.aspx 頁面開始。 複製 元素內 <asp:Content> 的所有宣告式語法,如圖 3 所示。 接下來,開啟 UpdatingAndDeleting.aspx 並貼上此標記在其 <asp:Content> 元素中。 同樣地,將程式代碼從 UploadInDetailsView.aspx 頁面的程式代碼後置類別 UpdatingAndDeleting.aspx複製到 。

從UploadInDetailsView.aspx複製宣告式標記

圖 3:從 UploadInDetailsView.aspx ([按兩下] 複製宣告式標記 ,以檢視完整大小的影像)

複製宣告式標記和程式代碼之後,請造訪 UpdatingAndDeleting.aspx。 您應該會看到相同的輸出,並具有與上一個教學課程頁面相同的用戶體驗 UploadInDetailsView.aspx

步驟 4:將刪除支援新增至 ObjectDataSource 和 GridView

如我們在 插入、更新和刪除資料 的概觀教學課程中所討論,GridView 提供內建刪除功能,如果網格線的基礎數據源支援刪除,則可以在複選框刻度啟用這些功能。 GridView 目前系結至 () CategoriesDataSource 不支持刪除的 ObjectDataSource。

若要解決此問題,請按兩下 ObjectDataSource 智慧標記中的 [設定資料源] 選項,以啟動精靈。 第一個畫面顯示 ObjectDataSource 已設定為使用 CategoriesBLL 類別。 點擊 [下一步]。 目前只會指定 ObjectDataSource 和 InsertMethodSelectMethod 屬性。 不過,精靈會分別使用 UpdateCategoryDeleteCategory 方法,自動填入 UPDATE 和 DELETE 索引卷標的下拉式清單。 這是因為在 類別中 CategoriesBLL ,我們會使用 DataObjectMethodAttribute 標記這些方法作為更新和刪除的預設方法。

目前,將 [更新] 索引標籤下拉式清單設定為 ([無) ],但將 [刪除] 索引標籤下拉式清單設定為 DeleteCategory。 我們將在步驟 6 中返回此精靈,以新增更新支援。

將 ObjectDataSource 設定為使用 DeleteCategory 方法

圖 4:將 ObjectDataSource 設定為使用 DeleteCategory 方法 (按兩下即可檢視完整大小的影像)

注意

完成精靈時,Visual Studio 可能會詢問您是否要重新整理字段和索引鍵,這會重新產生數據 Web 控件欄位。 選擇 [否],因為選擇 [是] 會覆寫您可能所做的任何字段自定義。

ObjectDataSource 現在會包含其 DeleteMethod 屬性和 DeleteParameter的值。 回想一下,使用精靈指定方法時,Visual Studio 會將 ObjectDataSource s OldValuesParameterFormatString 屬性設定為 original_{0},這會導致更新和刪除方法調用發生問題。 因此,請完全清除此屬性,或將其重設為預設值 {0}。 如果您需要重新整理此 ObjectDataSource 屬性上的記憶體,請參閱 插入、更新和刪除數據的概觀 教學課程。

完成精靈並修正 OldValuesParameterFormatString之後,ObjectDataSource 的宣告式標記看起來應該如下所示:

<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>

設定 ObjectDataSource 之後,請核取 GridView 智慧標記中的 [啟用刪除] 複選框,將刪除功能新增至 GridView。 這會將 CommandField 新增至 GridView,其 ShowDeleteButton 屬性設定為 True

啟用在 GridView 中刪除的支援

圖 5:在 GridView 中啟用刪除的支援 (按單擊即可檢視完整大小的影像)

請花點時間測試刪除功能。 數據表與CategoryIDCategories數據表CategoryID之間Products有外鍵,因此,如果您嘗試刪除前八個類別中的任何一個,您將會收到外鍵條件約束違規例外狀況。 若要測試這項功能,請新增類別,同時提供折頁簿和圖片。 如圖 6 所示的測試類別包含名為 Test.pdf 的測試折頁簿檔案和測試圖片。 圖 7 顯示新增測試類別之後的 GridView。

使用摺頁冊和影像新增測試類別

圖 6:新增含有折頁冊和影像的測試類別 (按鍵即可檢視全大小影像)

插入測試類別之後,它會顯示在 GridView 中

圖 7:插入測試類別之後,它會顯示在 GridView (按兩下即可檢視大小完整的影像)

在 Visual Studio 中,重新整理 方案總管。 您現在應該會在資料夾中看到新的檔案 ~/BrochuresTest.pdf (請參閱圖 8) 。

接下來,按兩下 [測試類別] 資料列中的 [刪除] 連結,導致頁面回傳,並 CategoriesBLL 引發 類別 s DeleteCategory 方法。 這會叫用 DAL s Delete 方法,導致適當的 DELETE 語句傳送至資料庫。 數據接著會重新系結至 GridView,而標記會傳回至用戶端,且測試類別不再存在。

雖然刪除工作流程已成功從 Categories 數據表中移除 [測試類別目錄] 記錄,但並未從網頁伺服器的文件系統中移除其折頁簿檔案。 重新整理 方案總管,您會看到Test.pdf仍在~/Brochures資料夾中。

Test.pdf 檔案未從網頁伺服器檔案系統中刪除

圖 8Test.pdf 檔案未從網頁伺服器檔案系統中刪除

步驟 5:移除已刪除的類別折頁冊檔案

儲存資料庫外部二進位數據的其中一個缺點是,刪除相關聯的資料庫記錄時,必須採取額外的步驟來清除這些檔案。 GridView 和 ObjectDataSource 提供在刪除命令執行前後引發的事件。 我們實際上需要為前置和後置動作事件建立事件處理程式。 Categories刪除記錄之前,我們需要判斷其 PDF 檔案的路徑,但在刪除類別之前,我們不想要刪除 PDF,以防有一些例外狀況,而且不會刪除該類別。

GridView 的 RowDeleting 事件 會在叫用 ObjectDataSource 的 delete 命令之前引發,而其 RowDeleted 事件 會在之後引發。 使用下列程式代碼建立這兩個事件的事件處理程式:

' A page variable to "remember" the deleted category's BrochurePath value
Private deletedCategorysPdfPath As String = Nothing
Protected Sub Categories_RowDeleting(sender As Object, e As GridViewDeleteEventArgs) _
    Handles Categories.RowDeleting
    
    ' Determine the PDF path for the category being deleted...
    Dim categoryID As Integer = Convert.ToInt32(e.Keys("CategoryID"))
    Dim categoryAPI As New CategoriesBLL()
    Dim categoriesData As Northwind.CategoriesDataTable = _
        categoryAPI.GetCategoryByCategoryID(categoryID)
    Dim category As Northwind.CategoriesRow = categoriesData(0)
    If category.IsBrochurePathNull() Then
        deletedCategorysPdfPath = Nothing
    Else
        deletedCategorysPdfPath = category.BrochurePath
    End If
End Sub
Protected Sub Categories_RowDeleted(sender As Object, e As GridViewDeletedEventArgs) _
    Handles Categories.RowDeleted
    
    ' Delete the brochure file if there were no problems deleting the record
    If e.Exception Is Nothing Then
        DeleteRememberedBrochurePath()
    End If
End Sub

在事件處理程式中 RowDeletingCategoryID 會從 GridView 集合擷取要刪除之資料列的 DataKeys ,此集合可透過 e.Keys 集合存取此事件處理程式。 接下來,會叫用 類別 CategoriesBLL s GetCategoryByCategoryID(categoryID) ,以傳回所刪除記錄的相關信息。 如果傳 CategoriesDataRow 回的物件有非NULL``BrochurePath 值,則會將其儲存在頁面變數 deletedCategorysPdfPath 中,以便在事件處理程式中刪除 RowDeleted 檔案。

注意

除了擷取BrochurePath事件處理程式中刪除RowDeleting之記錄的詳細Categories數據之外,我們也可以將 新增BrochurePath至 GridView 屬性DataKeyNames,並透過e.Keys集合存取記錄的值。 這麼做會稍微增加 GridView 的檢視狀態大小,但會減少所需的程式代碼數量,並儲存資料庫的車程。

叫用 ObjectDataSource 的基礎刪除命令之後,就會引發 GridView 事件處理程式 RowDeleted 。 如果刪除資料時沒有例外狀況,而且 有的值 deletedCategorysPdfPath,則會從文件系統中刪除 PDF。 請注意,不需要這個額外的程式代碼,即可清除與其圖片相關聯的類別二進位數據。 這是因為圖片數據直接儲存在資料庫中,因此刪除 Categories 數據列也會刪除該類別的圖片數據。

新增兩個事件處理程序之後,請再次執行此測試案例。 刪除類別時,也會刪除其相關聯的 PDF。

更新現有記錄相關聯的二進位數據,會提供一些有趣的挑戰。 本教學課程的其餘部分會探討如何將更新功能新增至折頁簿和圖片。 步驟 6 會探索更新折頁簿信息的技術,而步驟 7 則探討如何更新圖片。

步驟 6:更新類別折頁冊

如插入、更新和刪除數據的概觀教學課程所述,GridView 提供內建的數據列層級編輯支援,如果已適當設定其基礎數據源,則可以透過複選框的刻度實作。 CategoriesDataSource目前,ObjectDataSource 尚未設定為包含更新支援,因此讓我們將它新增至 。

從 ObjectDataSource 精靈按兩下 [設定數據源] 連結,然後繼續進行第二個步驟。 由於 DataObjectMethodAttributeCategoriesBLL所使用的 ,UPDATE 下拉式清單應該會自動填入 UpdateCategory 可接受四個輸入參數的多載, (所有數據行) Picture 。 變更此選項,使其使用具有五個參數的多載。

將 ObjectDataSource 設定為使用包含圖片參數的 UpdateCategory 方法

圖 9:將 ObjectDataSource 設定為使用 UpdateCategory 包含參數的方法 Picture (按兩下即可檢視完整大小的影像)

ObjectDataSource 現在會包含其 UpdateMethod 屬性的值,以及對應的 UpdateParameter 。 如步驟 4 所述,Visual Studio 會在使用 [設定數據源精靈] 時,將 ObjectDataSource s OldValuesParameterFormatString 屬性設定為 original_{0} 。 這會導致更新和刪除方法調用發生問題。 因此,請完全清除此屬性,或將其重設為預設值 {0}

完成精靈並修正 OldValuesParameterFormatString之後,ObjectDataSource 的宣告式標記看起來應該如下所示:

<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>

若要開啟 GridView 的內建編輯功能,請檢查 GridView 智慧標記中的 [啟用編輯] 選項。 這會將 CommandField 的 ShowEditButton 屬性設定為 True,導致新增 [編輯] 按鈕 (和 [更新] 和 [取消] 按鈕,以供編輯的數據列) 。

設定 GridView 以支援編輯

圖 10:將 GridView 設定為支援編輯 (按一下即可檢視完整大小的影像)

透過瀏覽器瀏覽頁面,然後按下其中一個資料列的 [編輯] 按鈕。 CategoryNameDescription BoundFields 會轉譯為文字框。 BrochurePath TemplateField 缺少 EditItemTemplate,因此它會繼續顯示其ItemTemplate折頁簿的連結。 ImageField 會 Picture 轉譯為 TextBox,其 Text 屬性會指派 ImageField s DataImageUrlField 值的值,在此案例中為 CategoryID

GridView 缺少 WorkbookPath 的編輯介面

圖 11:GridView 缺少編輯介面, BrochurePath (按兩下即可檢視完整大小的影像)

BrochurePath自訂編輯介面

我們需要建立TemplateField的 BrochurePath 編輯介面,讓用戶能夠:

  • 依原樣保留類別的摺頁冊
  • 上傳新的摺頁冊來更新類別折頁冊,或
  • 如果類別不再有相關聯的摺頁冊) ,請完全移除類別的摺頁冊 (。

我們也需要更新 Picture ImageField 的編輯介面,但我們將在步驟 7 中取得。

從 GridView 智慧標記中,按兩下 [編輯範本] 連結,然後從下拉式清單中選取 BrochurePath TemplateField s EditItemTemplate 。 將 RadioButtonList Web 控制項新增至此範本,並將其 屬性設定為 BrochureOptions ,並將其 AutoPostBack 屬性設定為 TrueID。 從 屬性視窗 中,按兩下屬性中的Items省略號,這會顯示 ListItem [集合] 編輯器。 分別使用 Value s 1、2 和 3 新增下列三個選項:

  • 使用目前的折頁簿
  • 拿掉目前的折頁簿
  • 上傳新摺頁冊

將第一個 ListItem 屬性 Selected 設定為 True

將 Three ListItems 新增至 RadioButtonList

圖 12:將三 ListItem 個 新增至 RadioButtonList

在 RadioButtonList 底下,新增名為 BrochureUpload的 FileUpload 控件。 將屬性 Visible 設定為 False

將 RadioButtonList 和 FileUpload 控件新增至 EditItemTemplate

圖 13:將 RadioButtonList 和 FileUpload 控件新增至 EditItemTemplate (按兩下即可檢視完整大小的影像)

此 RadioButtonList 提供使用者的三個選項。 概念是只有在選取最後一個選項 [上傳新折頁冊] 時,才會顯示 FileUpload 控件。 若要達成此目的,請為 RadioButtonList 事件 SelectedIndexChanged 建立事件處理程式,並新增下列程式代碼:

Protected Sub BrochureOptions_SelectedIndexChanged _
    (sender As Object, e As EventArgs)
    
    ' Get a reference to the RadioButtonList and its Parent
    Dim BrochureOptions As RadioButtonList = _
        CType(sender, RadioButtonList)
    Dim parent As Control = BrochureOptions.Parent
    ' Now use FindControl("controlID") to get a reference of the 
    ' FileUpload control
    Dim BrochureUpload As FileUpload = _
        CType(parent.FindControl("BrochureUpload"), FileUpload)
    ' Only show BrochureUpload if SelectedValue = "3"
    BrochureUpload.Visible = (BrochureOptions.SelectedValue = "3")
End Sub

由於 RadioButtonList 和 FileUpload 控件位於範本內,因此我們必須撰寫一些程式碼,以程式設計方式存取這些控件。 SelectedIndexChanged事件處理程式會在輸入參數中傳遞 RadioButtonList 的sender參考。 若要取得 FileUpload 控件,我們需要取得 RadioButtonList 的父控件,並使用 FindControl("controlID") 方法。 一旦我們有 RadioButtonList 和 FileUpload 控件的參考,則只有在 RadioButtonList 等於 SelectedValue 3 時,FileUpload 控件的 Visible 屬性才會設定True為 ,這是Value上傳新折頁冊 ListItem的 。

有了此程式代碼,請花點時間測試編輯介面。 按兩下資料列的 [編輯] 按鈕。 一開始,應該選取 [使用目前的折頁簿] 選項。 變更選取的索引會導致回傳。 如果選取第三個選項,則會顯示 FileUpload 控件,否則會隱藏它。 圖 14 顯示第一次按兩下 [編輯] 按鈕時的編輯介面;圖 15 顯示選取 [上傳新折頁冊] 選項之後的介面。

一開始,已選取 [使用目前的折頁簿選項]

圖 14:一開始,已選取 [使用目前的折頁簿選項] (按兩下即可檢視全大小的影像)

選擇 [上傳新折頁冊] 選項會顯示 FileUpload 控件

圖 15:選擇 [上傳新折頁冊] 選項顯示 [檔案][上傳控件] (按兩下即可檢視完整大小的影像)

儲存摺頁冊檔案並更新欄BrochurePath

按兩下 GridView 的 [更新] 按鈕時,就會引發其 RowUpdating 事件。 會叫用 ObjectDataSource s update 命令,然後引發 GridView s RowUpdated 事件。 如同刪除工作流程,我們需要建立這兩個事件的事件處理程式。 在事件處理程式中RowUpdating,我們需要根據 RadioButtonList 的 BrochureOptions 判斷要採取的SelectedValue動作:

  • SelectedValue如果 為 1,我們想要繼續使用相同的BrochurePath設定。 因此,我們需要將 ObjectDataSource s brochurePath 參數設定為正在更新之記錄的現有 BrochurePath 值。 您可以使用 來設定 e.NewValues["brochurePath"] = valueObjectDataSource s brochurePath 參數。
  • SelectedValue如果為 2,則我們想要將記錄BrochurePath的值設定為 NULL。 這可藉由將 ObjectDataSource s brochurePath 參數設定為 Nothing來完成,這會導致語句中使用的UPDATE資料庫NULL。 如果有正在移除的現有折頁冊檔案,我們需要刪除現有的檔案。 不過,只有在更新完成但不引發例外狀況時,我們才想要這麼做。
  • SelectedValue如果 為 3,則我們想要確保使用者已上傳 PDF 檔案,然後將它儲存至檔案系統,並更新記錄的數據BrochurePath行值。 此外,如果有正在取代的現有折頁冊檔案,我們需要刪除先前的檔案。 不過,只有在更新完成但不引發例外狀況時,我們才想要這麼做。

當 RadioButtonList s SelectedValue 為 3 時,完成的步驟幾乎與 DetailsView 事件處理程式 ItemInserting 所使用的步驟完全相同。 從我們在 一個教學課程中新增的 DetailsView 控件新增類別記錄時,就會執行這個事件處理程式。 因此,我們將此功能重構成不同的方法。 具體而言,我已將通用功能移出兩種方法:

  • ProcessBrochureUpload(FileUpload, out bool) 接受 做為輸入 FileUpload 控制項實例和輸出布爾值,指定刪除或編輯作業是否應該繼續,或是否應該因為某些驗證錯誤而取消。 這個方法會傳回已儲存盤案的路徑,如果沒有 null 儲存盤案,則傳回路徑。
  • DeleteRememberedBrochurePath如果 deletedCategorysPdfPath 不是 null,則會刪除頁面變數deletedCategorysPdfPath中路徑所指定的檔案。

這兩種方法的程序代碼如下。 請注意上一個教學課程中與 DetailsView 事件處理程式ItemInserting之間的ProcessBrochureUpload相似度。 在本教學課程中,我已更新DetailsView事件處理程式,以使用這些新方法。 下載與本教學課程相關聯的程序代碼,以查看 DetailsView 事件處理程式的修改。

Private Function ProcessBrochureUpload _
    (BrochureUpload As FileUpload, CancelOperation As Boolean) As String
    
    CancelOperation = False    ' by default, do not cancel operation
    If BrochureUpload.HasFile Then
        ' Make sure that a PDF has been uploaded
        If String.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName), _
            ".pdf", True) <> 0 Then
            
            UploadWarning.Text = _
                "Only PDF documents may be used for a category's brochure."
            UploadWarning.Visible = True
            CancelOperation = True
            Return Nothing
        End If
        Const BrochureDirectory As String = "~/Brochures/"
        Dim brochurePath As String = BrochureDirectory + BrochureUpload.FileName
        Dim fileNameWithoutExtension As String = _
            System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName)
        Dim iteration As Integer = 1
        While System.IO.File.Exists(Server.MapPath(brochurePath))
            brochurePath = String.Concat(BrochureDirectory, _
                fileNameWithoutExtension, "-", iteration, ".pdf")
            iteration += 1
        End While
        ' 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 Nothing
    End If
End Function
Private Sub DeleteRememberedBrochurePath()
    ' Is there a file to delete?
    If deletedCategorysPdfPath IsNot Nothing Then
        System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath))
    End If
End Sub

GridView s RowUpdating 和 事件處理程式會使用 ProcessBrochureUploadRowUpdatedDeleteRememberedBrochurePath 方法,如下列程式代碼所示:

Protected Sub Categories_RowUpdating _
    (sender As Object, e As GridViewUpdateEventArgs) _
    Handles Categories.RowUpdating
    
    ' Reference the RadioButtonList
    Dim BrochureOptions As RadioButtonList = _
        CType(Categories.Rows(e.RowIndex).FindControl("BrochureOptions"), _
            RadioButtonList)
    ' Get BrochurePath information about the record being updated
    Dim categoryID As Integer = Convert.ToInt32(e.Keys("CategoryID"))
    Dim categoryAPI As New CategoriesBLL()
    Dim categoriesData As Northwind.CategoriesDataTable = _
        categoryAPI.GetCategoryByCategoryID(categoryID)
    Dim category As Northwind.CategoriesRow = categoriesData(0)
    If BrochureOptions.SelectedValue = "1" Then
        ' Use current value for BrochurePath
        If category.IsBrochurePathNull() Then
            e.NewValues("brochurePath") = Nothing
        Else
            e.NewValues("brochurePath") = category.BrochurePath
        End If
    ElseIf BrochureOptions.SelectedValue = "2" Then
        ' Remove the current brochure (set it to NULL in the database)
        e.NewValues("brochurePath") = Nothing
    ElseIf BrochureOptions.SelectedValue = "3" Then
        ' Reference the BrochurePath FileUpload control
        Dim BrochureUpload As FileUpload = _
            CType(categories.Rows(e.RowIndex).FindControl("BrochureUpload"), _
                FileUpload)
        ' Process the BrochureUpload
        Dim cancelOperation As Boolean = False
        e.NewValues("brochurePath") = _
            ProcessBrochureUpload(BrochureUpload, cancelOperation)
        e.Cancel = cancelOperation
    Else
        ' Unknown value!
        Throw New ApplicationException( _
            String.Format("Invalid BrochureOptions value, {0}", _
                BrochureOptions.SelectedValue))
    End If
    If BrochureOptions.SelectedValue = "2" OrElse _
        BrochureOptions.SelectedValue = "3" Then
        
        ' "Remember" that we need to delete the old PDF file
        If (category.IsBrochurePathNull()) Then
            deletedCategorysPdfPath = Nothing
        Else
            deletedCategorysPdfPath = category.BrochurePath
        End If
    End If
End Sub
Protected Sub Categories_RowUpdated _
    (sender As Object, e As GridViewUpdatedEventArgs) _
    Handles Categories.RowUpdated
    
    ' If there were no problems and we updated the PDF file, 
    ' then delete the existing one
    If e.Exception Is Nothing Then
        DeleteRememberedBrochurePath()
    End If
End Sub

請注意事件處理程式如何使用 RowUpdating 一系列的條件語句,根據 BrochureOptions RadioButtonList 的 SelectedValue 屬性值來執行適當的動作。

有了此程式代碼,您可以編輯類別,並讓它使用其目前的折頁冊、不使用摺頁冊,或上傳新的折頁簿。 請繼續進行並試試看。在和 RowUpdated 事件處理程式中RowUpdating設定斷點,以瞭解工作流程。

步驟 7:上傳新圖片

Picture ImageField 的編輯介面會轉譯為以其 DataImageUrlField 屬性中值填入的文字框。 在編輯工作流程期間,GridView 會將參數傳遞至 ObjectDataSource,參數名稱為 ImageField s DataImageUrlField 屬性的值,以及參數 s 值編輯介面中的文字框中輸入的值。 當影像儲存為文件系統上的檔案,且 DataImageUrlField 包含影像的完整URL時,此行為很適合。 在這種情況下,編輯介面會在文本框中顯示影像的 URL,使用者可以變更並儲存回資料庫。 授與此預設介面不允許用戶上傳新的影像,但會讓他們將影像的URL從目前值變更為另一個。 不過,在本教學課程中,ImageField 的預設編輯介面就已足夠,因為 Picture 二進位數據會直接儲存在資料庫中,而 DataImageUrlField 屬性只 CategoryID保留 。

若要進一步了解當使用者使用 ImageField 編輯數據列時,本教學課程會發生什麼事,請考慮下列範例:用戶編輯具有 CategoryID 10 的數據列,導致 Picture ImageField 轉譯為值為 10 的文本框。 假設使用者將此文字框中的值變更為 50,然後按下 [更新] 按鈕。 發生回傳,GridView 一開始會建立名為 CategoryID 且值為 50 的參數。 不過,在 GridView 傳送此參數 (和 和 CategoryNameDescription 參數) 之前,它會在 DataKeys 集合中的值中新增 。 因此,它會以目前數據列的基礎CategoryID值 10 覆寫 CategoryID 參數。 簡單地說,ImageField 的編輯介面不會影響本教學課程的編輯工作流程,因為 ImageField s DataImageUrlField 屬性的名稱和方格 DataKey 的值相同。

雖然 ImageField 可讓您輕鬆地根據資料庫數據顯示影像,但我們不想在編輯介面中提供文本框。 相反地,我們想要提供 FileUpload 控件,讓用戶可用來變更類別的圖片。 BrochurePath不同於值,針對這些教學課程,我們決定要求每個類別都必須有圖片。 因此,我們不需要讓使用者指出沒有相關聯的圖片,使用者可能會上傳新的圖片,或讓目前的圖片保持原狀。

若要自定義 ImageField 的編輯介面,我們需要將其轉換成 TemplateField。 從 GridView 智慧標記中,按兩下 [編輯資料行] 連結,選取 ImageField,然後按兩下 [將此字位轉換成 TemplateField] 連結。

將 ImageField 轉換成 TemplateField

圖 16:將 ImageField 轉換成 TemplateField

以這種方式將 ImageField 轉換成 TemplateField 會產生具有兩個範本的 TemplateField。 如下列宣告式語法所示,包含 Image Web 控件, ItemTemplateImageUrl 屬性是使用以 ImageField s DataImageUrlFieldDataImageUrlFormatString 屬性為基礎的數據系結語法來指派。 EditItemTemplate包含 TextBox,其 Text 屬性系結至 屬性所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>

我們需要更新 以 EditItemTemplate 使用 FileUpload 控制件。 從 GridView 智慧標記按兩下 [編輯範本] 連結,然後從下拉式清單中選取 Picture TemplateField s EditItemTemplate 。 在範本中,您應該會看到 TextBox 移除此專案。 接下來,將 FileUpload 控件從 [工具箱] 拖曳至範本,並將其 ID 設定為 PictureUpload。 同時新增文字 若要變更類別的圖片,請指定新的圖片。 若要讓類別的圖片保持相同,請將字段保留空白給範本。

將 FileUpload 控件新增至 EditItemTemplate

圖 17:將 FileUpload 控件新增至 EditItemTemplate (按兩下即可檢視完整大小的影像)

自訂編輯介面之後,請在瀏覽器中檢視您的進度。 以唯讀模式檢視數據列時,類別影像會顯示為之前,但按兩下 [編輯] 按鈕會將圖片列轉譯為具有 FileUpload 控制件的文字。

編輯介面包含 FileUpload 控制件

圖 18:編輯介面包含 FileUpload 控件 (按兩下即可檢視完整大小的影像)

回想一下,ObjectDataSource 已設定為呼叫 CategoriesBLL 類別的 UpdateCategory 方法,以接受做為圖片的二進位數據做為 Byte 數位的輸入。 不過,如果這個陣列是 Nothing,則會呼叫替代 UpdateCategory 多載,這會發出 UPDATE 不會修改數據 Picture 行的 SQL 語句,因而讓類別的目前圖片保持不變。 因此,在 GridView 事件處理程式 RowUpdating 中,我們需要以程式設計方式參考 PictureUpload FileUpload 控件,並判斷檔案是否已上傳。 如果未上傳,則我們 不想 指定 參數的值 picture 。 另一方面,如果在 FileUpload 控制件中 PictureUpload 上傳檔案,我們想要確保它是 JPG 檔案。 如果是,我們可以透過 picture 參數將其二進位內容傳送至 ObjectDataSource。

就像步驟 6 中使用的程式代碼一樣,這裡所需的大部分程式代碼都已存在於 DetailsView 事件處理程式 ItemInserting 中。 因此,我已將通用功能重構為新的方法 ValidPictureUpload,並更新 ItemInserting 事件處理程式以使用此方法。

將下列程式代碼新增至 GridView RowUpdating 事件處理程式的開頭。 請務必將此程式碼置於儲存折頁簿檔案的程式代碼之前,因為我們不想在上傳無效的圖片檔案時,將折頁簿儲存到網頁伺服器的文件系統。

' Reference the PictureUpload FileUpload
Dim PictureUpload As FileUpload = _
    CType(categories.Rows(e.RowIndex).FindControl("PictureUpload"), _
        FileUpload)
If PictureUpload.HasFile Then
    ' Make sure the picture upload is valid
    If ValidPictureUpload(PictureUpload) Then
        e.NewValues("picture") = PictureUpload.FileBytes
    Else
        ' Invalid file upload, cancel update and exit event handler
        e.Cancel = True
        Exit Sub
    End If
End If

方法 ValidPictureUpload(FileUpload) 會採用 FileUpload 控件作為唯一的輸入參數,並檢查上傳的擴展名,以確保上傳的檔案是 JPG;只有在上傳圖片檔案時才會呼叫它。 如果未上傳檔案,則不會設定圖片參數,因此會使用預設值 Nothing。 如果上傳並 ValidPictureUploadTrue回圖片,則會 picture 將上傳影像的二進位數據指派給 參數;如果方法傳 False回 ,則會取消更新工作流程並結束事件處理程式。

ValidPictureUpload(FileUpload)從 DetailsView 事件處理程式ItemInserting重構的方法程式代碼如下:

Private Function ValidPictureUpload(ByVal PictureUpload As FileUpload) As Boolean
    ' Make sure that a JPG has been uploaded
    If String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
        ".jpg", True) <> 0 AndAlso _
        String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
        ".jpeg", True) <> 0 Then
        
        UploadWarning.Text = _
            "Only JPG documents may be used for a category's picture."
        UploadWarning.Visible = True
        Return False
    Else
        Return True
    End If
End Function

步驟 8:以 JPG 取代原始類別圖片

回想一下,原始八個類別圖片是包裝在 OLE 標頭中的位圖檔案。 既然我們已新增編輯現有記錄圖片的功能,請花點時間將這些點陣圖取代為 JPG。 如果您想要繼續使用目前的類別圖片,您可以執行下列步驟,將它們轉換成 JPG:

  1. 將點陣圖影像儲存至硬碟。 UpdatingAndDeleting.aspx瀏覽瀏覽器中的頁面,並針對前八個類別的每一個類別,以滑鼠右鍵按鍵按鍵,然後選擇儲存圖片。
  2. 在您選擇的影像編輯器中開啟影像。 例如,您可以使用 Microsoft 小畫家。
  3. 將點陣圖儲存為 JPG 影像。
  4. 使用 JPG 檔案,透過編輯介面更新類別圖片。

編輯類別並上傳 JPG 影像之後,影像將不會在瀏覽器中轉譯,因為 DisplayCategoryPicture.aspx 頁面會從前八個類別的圖片中移除前 78 個字節。 拿掉執行 OLE 標頭等量刪除的程式代碼,以修正此問題。 執行此動作之後, DisplayCategoryPicture.aspx``Page_Load 事件處理程序應該只有下列程序代碼:

Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
    Dim categoryID As Integer = _
        Convert.ToInt32(Request.QueryString("CategoryID"))
    ' Get information about the specified category
    Dim categoryAPI As New CategoriesBLL()
    Dim categories As Northwind.CategoriesDataTable = _
        categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID)
    Dim category As Northwind.CategoriesRow = 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)
End Sub

注意

頁面 UpdatingAndDeleting.aspx 的插入和編輯介面可能會使用更多工作。 CategoryName DetailsView 和 GridView 中的 和 Description BoundField 應該轉換成 TemplateFields。 由於 CategoryName 不允許 NULL 值,因此應該加入 RequiredFieldValidator。 Description而且 TextBox 應該轉換成多行 TextBox。 我將這些最後的觸碰做為您練習。

摘要

本教學課程會完成我們處理二進位數據的外觀。 在本教學課程和前三個教學課程中,我們已瞭解如何將二進位數據儲存在文件系統上,或直接儲存在資料庫中。 使用者從硬碟中選取檔案並將其上傳至網頁伺服器,以便將二進位資料儲存在檔案系統上或插入資料庫,以提供二進位資料給系統。 ASP.NET 2.0 包含 FileUpload 控件,可讓這類介面輕鬆拖放。 不過,如 上傳檔案 教學課程所述,FileUpload 控件僅適用於相對小型的檔案上傳,理想情況下不會超過 MB。 我們也探索了如何將上傳的數據與基礎數據模型產生關聯,以及如何編輯和刪除現有記錄中的二進位數據。

下一組教學課程會探索各種快取技術。 快取提供方法來改善應用程式的整體效能,方法是取得昂貴作業的結果,並將其儲存在可更快速存取的位置。

快樂的程序設計!

關於作者

Scott Mitchell 是 1998 年以來,1998 年與 Microsoft Web 技術合作的 篇 ASP/ASP.NET 書籍和 4GuysFromRolla.com 作者。 Scott 是獨立的顧問、訓練者和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格連到,也可以透過其部落格來存取,網址為 http://ScottOnWriting.NET

特別感謝

本教學課程系列是由許多實用的檢閱者所檢閱。 本教學課程的首席檢閱者是 Teresa Murphy。 想要檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行放在 mitchell@4GuysFromRolla.com。