更新及刪除現有的二進位資料 (VB)
在先前的教學課程中,我們瞭解 GridView 控件如何讓您輕鬆地編輯和刪除文字數據。 在本教學課程中,我們會看到 GridView 控件如何讓編輯和刪除二進位數據,無論是儲存在資料庫中還是儲存在文件系統中。
簡介
在過去三個教學課程中,我們新增了相當多的功能來處理二進位數據。 我們一開始將數據 BrochurePath
行新增至 Categories
數據表,並據以更新架構。 我們也新增了數據存取層和商業規則層方法,以使用 Categories 數據表的現有 Picture
數據行,其中包含圖像檔的二進位內容。 我們已建置網頁,以在 GridView 中呈現折頁簿的二進位數據,其中包含元素中顯示的 <img>
類別圖片,並新增 DetailsView,讓使用者新增類別並上傳其折頁簿和圖片數據。
所有仍要實作的功能都是能夠編輯和刪除現有的類別,我們將在本教學課程中使用 GridView 的內建編輯和刪除功能來完成。 編輯類別時,用戶可以選擇性地上傳新圖片,或讓類別繼續使用現有的圖片。 針對摺頁冊,他們可以選擇使用現有的摺頁冊、上傳新的摺頁冊,或指出該類別不再有與其相關聯的摺頁冊。 讓我們開始吧!
步驟 1:更新數據存取層
DAL 具有自動產生的、 和方法,但這些方法是根據不包含Picture
數據行的主要查詢所產生CategoriesTableAdapter
。Delete
Update
Insert
因此, Insert
和 Update
方法不包含參數來指定類別圖片的二進位數據。 如同我們在上一個 教學課程中所做的一樣,我們需要建立新的 TableAdapter 方法,以在指定二進位數據時更新 Categories
數據表。
開啟 [具類型的數據集],然後從 [Designer],以滑鼠右鍵按兩下 CategoriesTableAdapter
s 標頭,然後從操作功能表中選擇 [新增查詢] 以啟動 [TableAdapter 查詢設定精靈]。 此精靈一開始會詢問我們 TableAdapter 查詢應該如何存取資料庫。 選擇 [使用 SQL 語句],然後按 [下一步]。 下一個步驟會提示產生查詢的類型。 因為我們要建立查詢以將新記錄新增至 Categories
數據表,請選擇 [更新],然後按 [下一步]。
圖 1:選取 UPDATE 選項 (按下即可檢視完整大小的映像)
我們現在需要指定 UPDATE
SQL 語句。 精靈會自動建議 UPDATE
對應至 TableAdapter 主要查詢的語句, (更新 CategoryName
、 Description
和 BrochurePath
值) 。 變更 語句, Picture
讓數據行與 參數一 @Picture
起包含,如下所示:
UPDATE [Categories] SET
[CategoryName] = @CategoryName,
[Description] = @Description,
[BrochurePath] = @BrochurePath ,
[Picture] = @Picture
WHERE (([CategoryID] = @Original_CategoryID))
精靈的最後一個畫面會要求我們命名新的 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
在本教學課程中,讓我們建立兩個方法來更新類別- 一個預期二進位圖片數據,並叫UpdateWithPicture
用CategoriesTableAdapter
我們剛才新增至 CategoriesTableAdapter
的方法,另一個方法只CategoryName
接受 、 Description
和 BrochurePath
值,並使用類別自動產生的Update
語句。 使用兩種方法背後的原理是在某些情況下,使用者可能會想要更新類別的圖片及其其他字段,在此情況下,用戶必須上傳新圖片。 上傳的圖片二進位數據接著可以在語句中使用 UPDATE
。 在其他情況下,使用者可能只想要更新名稱與描述。 但是, UPDATE
如果語句也預期數據行的 Picture
二進位數據,我們也需要提供該資訊。 這需要額外的資料庫車程,才能傳回正在編輯之記錄的圖片數據。 因此,我們想要兩 UPDATE
種方法。 商業規則層會根據更新類別時是否提供圖片數據來決定要使用哪一個。
為了方便進行這項作業,請將兩個方法新增至 CategoriesBLL
類別,這兩個方法皆名為 UpdateCategory
。 第一個應該接受三 String
個 Byte
、陣列和 Integer
做為其輸入參數;第二個,只有三 String
個 和 Integer
。 輸入 String
參數適用於類別的名稱、描述和折頁簿檔案路徑、 Byte
陣列適用於類別圖片的二進位內容,而 Integer
會 CategoryID
識別要更新之記錄的 。 請注意,如果傳入 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
複製到 。
圖 3:從 UploadInDetailsView.aspx
([按兩下] 複製宣告式標記 ,以檢視完整大小的影像)
複製宣告式標記和程式代碼之後,請造訪 UpdatingAndDeleting.aspx
。 您應該會看到相同的輸出,並具有與上一個教學課程頁面相同的用戶體驗 UploadInDetailsView.aspx
。
步驟 4:將刪除支援新增至 ObjectDataSource 和 GridView
如我們在 插入、更新和刪除資料 的概觀教學課程中所討論,GridView 提供內建刪除功能,如果網格線的基礎數據源支援刪除,則可以在複選框刻度啟用這些功能。 GridView 目前系結至 () CategoriesDataSource
不支持刪除的 ObjectDataSource。
若要解決此問題,請按兩下 ObjectDataSource 智慧標記中的 [設定資料源] 選項,以啟動精靈。 第一個畫面顯示 ObjectDataSource 已設定為使用 CategoriesBLL
類別。 點擊 [下一步]。 目前只會指定 ObjectDataSource 和 InsertMethod
SelectMethod
屬性。 不過,精靈會分別使用 UpdateCategory
和 DeleteCategory
方法,自動填入 UPDATE 和 DELETE 索引卷標的下拉式清單。 這是因為在 類別中 CategoriesBLL
,我們會使用 DataObjectMethodAttribute
標記這些方法作為更新和刪除的預設方法。
目前,將 [更新] 索引標籤下拉式清單設定為 ([無) ],但將 [刪除] 索引標籤下拉式清單設定為 DeleteCategory
。 我們將在步驟 6 中返回此精靈,以新增更新支援。
圖 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
。
圖 5:在 GridView 中啟用刪除的支援 (按單擊即可檢視完整大小的影像)
請花點時間測試刪除功能。 數據表與CategoryID
Categories
數據表CategoryID
之間Products
有外鍵,因此,如果您嘗試刪除前八個類別中的任何一個,您將會收到外鍵條件約束違規例外狀況。 若要測試這項功能,請新增類別,同時提供折頁簿和圖片。 如圖 6 所示的測試類別包含名為 Test.pdf
的測試折頁簿檔案和測試圖片。 圖 7 顯示新增測試類別之後的 GridView。
圖 6:新增含有折頁冊和影像的測試類別 (按鍵即可檢視全大小影像)
圖 7:插入測試類別之後,它會顯示在 GridView (按兩下即可檢視大小完整的影像)
在 Visual Studio 中,重新整理 方案總管。 您現在應該會在資料夾中看到新的檔案 ~/Brochures
, Test.pdf
(請參閱圖 8) 。
接下來,按兩下 [測試類別] 資料列中的 [刪除] 連結,導致頁面回傳,並 CategoriesBLL
引發 類別 s DeleteCategory
方法。 這會叫用 DAL s Delete
方法,導致適當的 DELETE
語句傳送至資料庫。 數據接著會重新系結至 GridView,而標記會傳回至用戶端,且測試類別不再存在。
雖然刪除工作流程已成功從 Categories
數據表中移除 [測試類別目錄] 記錄,但並未從網頁伺服器的文件系統中移除其折頁簿檔案。 重新整理 方案總管,您會看到Test.pdf
仍在~/Brochures
資料夾中。
圖 8: Test.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
在事件處理程式中 RowDeleting
, CategoryID
會從 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 精靈按兩下 [設定數據源] 連結,然後繼續進行第二個步驟。 由於 DataObjectMethodAttribute
中 CategoriesBLL
所使用的 ,UPDATE 下拉式清單應該會自動填入 UpdateCategory
可接受四個輸入參數的多載, (所有數據行) Picture
。 變更此選項,使其使用具有五個參數的多載。
圖 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
,導致新增 [編輯] 按鈕 (和 [更新] 和 [取消] 按鈕,以供編輯的數據列) 。
圖 10:將 GridView 設定為支援編輯 (按一下即可檢視完整大小的影像)
透過瀏覽器瀏覽頁面,然後按下其中一個資料列的 [編輯] 按鈕。 CategoryName
和 Description
BoundFields 會轉譯為文字框。 BrochurePath
TemplateField 缺少 EditItemTemplate
,因此它會繼續顯示其ItemTemplate
折頁簿的連結。 ImageField 會 Picture
轉譯為 TextBox,其 Text
屬性會指派 ImageField s DataImageUrlField
值的值,在此案例中為 CategoryID
。
圖 11:GridView 缺少編輯介面, BrochurePath
(按兩下即可檢視完整大小的影像)
BrochurePath
自訂編輯介面
我們需要建立TemplateField的 BrochurePath
編輯介面,讓用戶能夠:
- 依原樣保留類別的摺頁冊
- 上傳新的摺頁冊來更新類別折頁冊,或
- 如果類別不再有相關聯的摺頁冊) ,請完全移除類別的摺頁冊 (。
我們也需要更新 Picture
ImageField 的編輯介面,但我們將在步驟 7 中取得。
從 GridView 智慧標記中,按兩下 [編輯範本] 連結,然後從下拉式清單中選取 BrochurePath
TemplateField s EditItemTemplate
。 將 RadioButtonList Web 控制項新增至此範本,並將其 屬性設定為 BrochureOptions
,並將其 AutoPostBack
屬性設定為 True
ID
。 從 屬性視窗 中,按兩下屬性中的Items
省略號,這會顯示 ListItem
[集合] 編輯器。 分別使用 Value
s 1、2 和 3 新增下列三個選項:
- 使用目前的折頁簿
- 拿掉目前的折頁簿
- 上傳新摺頁冊
將第一個 ListItem
屬性 Selected
設定為 True
。
圖 12:將三 ListItem
個 新增至 RadioButtonList
在 RadioButtonList 底下,新增名為 BrochureUpload
的 FileUpload 控件。 將屬性 Visible
設定為 False
。
圖 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:一開始,已選取 [使用目前的折頁簿選項] (按兩下即可檢視全大小的影像)
圖 15:選擇 [上傳新折頁冊] 選項顯示 [檔案][上傳控件] (按兩下即可檢視完整大小的影像)
儲存摺頁冊檔案並更新欄BrochurePath
按兩下 GridView 的 [更新] 按鈕時,就會引發其 RowUpdating
事件。 會叫用 ObjectDataSource s update 命令,然後引發 GridView s RowUpdated
事件。 如同刪除工作流程,我們需要建立這兩個事件的事件處理程式。 在事件處理程式中RowUpdating
,我們需要根據 RadioButtonList 的 BrochureOptions
判斷要採取的SelectedValue
動作:
SelectedValue
如果 為 1,我們想要繼續使用相同的BrochurePath
設定。 因此,我們需要將 ObjectDataSource sbrochurePath
參數設定為正在更新之記錄的現有BrochurePath
值。 您可以使用 來設定e.NewValues["brochurePath"] = value
ObjectDataSource sbrochurePath
參數。SelectedValue
如果為 2,則我們想要將記錄BrochurePath
的值設定為NULL
。 這可藉由將 ObjectDataSource sbrochurePath
參數設定為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
和 事件處理程式會使用 ProcessBrochureUpload
和 RowUpdated
DeleteRememberedBrochurePath
方法,如下列程式代碼所示:
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 傳送此參數 (和 和 CategoryName
Description
參數) 之前,它會在 DataKeys
集合中的值中新增 。 因此,它會以目前數據列的基礎CategoryID
值 10 覆寫 CategoryID
參數。 簡單地說,ImageField 的編輯介面不會影響本教學課程的編輯工作流程,因為 ImageField s DataImageUrlField
屬性的名稱和方格 DataKey
的值相同。
雖然 ImageField 可讓您輕鬆地根據資料庫數據顯示影像,但我們不想在編輯介面中提供文本框。 相反地,我們想要提供 FileUpload 控件,讓用戶可用來變更類別的圖片。 BrochurePath
不同於值,針對這些教學課程,我們決定要求每個類別都必須有圖片。 因此,我們不需要讓使用者指出沒有相關聯的圖片,使用者可能會上傳新的圖片,或讓目前的圖片保持原狀。
若要自定義 ImageField 的編輯介面,我們需要將其轉換成 TemplateField。 從 GridView 智慧標記中,按兩下 [編輯資料行] 連結,選取 ImageField,然後按兩下 [將此字位轉換成 TemplateField] 連結。
圖 16:將 ImageField 轉換成 TemplateField
以這種方式將 ImageField 轉換成 TemplateField 會產生具有兩個範本的 TemplateField。 如下列宣告式語法所示,包含 Image Web 控件, ItemTemplate
其 ImageUrl
屬性是使用以 ImageField s DataImageUrlField
和 DataImageUrlFormatString
屬性為基礎的數據系結語法來指派。 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
。 同時新增文字 若要變更類別的圖片,請指定新的圖片。 若要讓類別的圖片保持相同,請將字段保留空白給範本。
圖 17:將 FileUpload 控件新增至 EditItemTemplate
(按兩下即可檢視完整大小的影像)
自訂編輯介面之後,請在瀏覽器中檢視您的進度。 以唯讀模式檢視數據列時,類別影像會顯示為之前,但按兩下 [編輯] 按鈕會將圖片列轉譯為具有 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
。 如果上傳並 ValidPictureUpload
傳 True
回圖片,則會 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:
- 將點陣圖影像儲存至硬碟。
UpdatingAndDeleting.aspx
瀏覽瀏覽器中的頁面,並針對前八個類別的每一個類別,以滑鼠右鍵按鍵按鍵,然後選擇儲存圖片。 - 在您選擇的影像編輯器中開啟影像。 例如,您可以使用 Microsoft 小畫家。
- 將點陣圖儲存為 JPG 影像。
- 使用 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。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應