Share via


既存のバイナリ データの更新と削除 (VB)

作成者: Scott Mitchell

PDF のダウンロード

以前のチュートリアルでは、GridView コントロールを使用してテキスト データを簡単に編集および削除する方法について説明しました。 このチュートリアルでは、GridView コントロールを使用して、バイナリ データがデータベースに保存されているか、ファイル システムに格納されているかに関係なく、バイナリ データを編集および削除する方法についても説明します。

はじめに

過去 3 つのチュートリアルで、バイナリ データを操作するための機能をかなり追加しました。 まず、テーブルにCategories列をBrochurePath追加し、それに応じてアーキテクチャを更新しました。 また、画像ファイルのバイナリ コンテンツを保持する Categories テーブルの既存 Picture の列を操作するために、データ アクセス層とビジネス ロジック層メソッドも追加しました。 バイナリ データを GridView に表示する Web ページを作成し、パンフレットのダウンロード リンクを作成し、カテゴリの画像を <img> 要素に表示し、ユーザーが新しいカテゴリを追加してそのパンフレットと画像データをアップロードできるように DetailsView を追加しました。

実装が残っているのは、既存のカテゴリを編集および削除する機能です。このチュートリアルでは、GridView の組み込みの編集および削除機能を使用して実行します。 カテゴリを編集すると、ユーザーは必要に応じて新しい画像をアップロードしたり、カテゴリで既存の画像を引き続き使用したりできます。 パンフレットの場合、既存のパンフレットを使用するか、新しいパンフレットをアップロードするか、カテゴリにパンフレットが関連付けられていないことを示すことができます。 始めましょう。

手順 1: データ アクセス層を更新する

DAL には自動生成された Insert、、および Delete メソッドがありますが、これらのメソッドは、列をCategoriesTableAdapterPictureまない メイン クエリに基Updateづいて生成されました。 したがって、 メソッドと Update メソッドには、Insertカテゴリの画像のバイナリ データを指定するためのパラメーターは含まれません。 前のチュートリアルで行ったように、バイナリ データを指定するときにテーブルを更新Categoriesするための新しい TableAdapter メソッドを作成する必要があります。

型指定された DataSet を開き、Designerから s ヘッダーをCategoriesTableAdapter右クリックし、コンテキスト メニューから [クエリの追加] を選択して TableAdapter クエリ構成ウィザードを起動します。 このウィザードは、TableAdapter クエリがデータベースにアクセスする方法を確認することから始まります。 [SQL ステートメントの使用] を選択し、[次へ] をクリックします。 次の手順では、生成するクエリの種類を確認するメッセージが表示されます。 テーブルに新しいレコード Categories を追加するクエリを作成しているので、[更新] を選択して [次へ] をクリックします。

UPDATE オプションを選択する

図 1: UPDATE オプションを選択します (クリックするとフルサイズの画像が表示されます)

次に、SQL ステートメントを指定する UPDATE 必要があります。 このウィザードでは、TableAdapter の メイン クエリ (、Description、および BrochurePath の値を更新するもの) に対応するステートメントが自動的にCategoryName提案UPDATEされます。 ステートメントを変更して、 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 を更新する必要があります。 これらは、プレゼンテーション レイヤーから呼び出されるメソッドです。

カテゴリを削除する場合は、 の自動生成メソッドをCategoriesTableAdapterDelete使用できます。 次のメソッドを 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

このチュートリアルでは、カテゴリを更新するための 2 つのメソッドを作成します。1 つはバイナリ画像データを想定し、 にCategoriesTableAdapter追加したメソッドを呼び出UpdateWithPictureし、もう 1 つは 、DescriptionBrochurePath の値のみをCategoryName受け入れ、クラスの自動生成Updateステートメントを使用CategoriesTableAdapterします。 2 つの方法を使用する理由は、状況によっては、ユーザーがカテゴリの画像を他のフィールドと共に更新したい場合があり、その場合、ユーザーは新しい画像をアップロードする必要があるということです。 アップロードされた画像のバイナリ データは、 ステートメントで UPDATE 使用できます。 それ以外の場合、ユーザーは名前と説明などの更新にのみ関心を持つ場合があります。 ただし、 ステートメントで UPDATE 列のバイナリ データ Picture も必要な場合は、その情報も提供する必要があります。 編集中のレコードの画像データを取り戻すには、データベースへの追加のトリップが必要になります。 したがって、2 つの UPDATE メソッドが必要です。 ビジネス ロジック レイヤーは、カテゴリの更新時に画像データが提供されるかどうかに基づいて、使用するデータを決定します。

これを容易にするために、 という名前UpdateCategoryの 2 つのメソッドを クラスにCategoriesBLL追加します。 最初の 1 つは、入力パラメーターとして 3 つの String s、 Byte 配列、および を Integer 受け取る必要があります。2 つ目は、3 つの String s と を受け取るだけです Integer。 入力パラメーターは String 、カテゴリの名前、説明、パンフレット のファイル パス用であり、 Byte 配列はカテゴリの画像のバイナリ コンテンツ用であり Integer 、 は更新するレコードの を CategoryID 識別します。 渡された配列Nothingが の場合、最初のオーバーロードは 2 番目の を呼び出すことにByte注意してください。

<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: 挿入および表示機能をコピーする

前の チュートリアル では、GridView 内のすべてのカテゴリを一覧表示する という名前 UploadInDetailsView.aspx のページを作成し、システムに新しいカテゴリを追加するための DetailsView を提供しました。 このチュートリアルでは、GridView を拡張して、サポートの編集と削除を含めます。 からUploadInDetailsView.aspx作業を続けるのではなく、~/BinaryData同じフォルダーの ページにこのチュートリアルの変更をUpdatingAndDeleting.aspx配置してみましょう。 宣言型マークアップとコードを コピーして から UploadInDetailsView.aspxUpdatingAndDeleting.aspx貼り付けます。

まず、ページを UploadInDetailsView.aspx 開きます。 図 3 に示すように、 要素内 <asp:Content> のすべての宣言構文をコピーします。 次に、このマークアップを開 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 プロパティのみが指定されています。 ただし、ウィザードでは、UPDATE タブと DELETE タブ UpdateCategory のドロップダウン リストに、それぞれ メソッドと DeleteCategory メソッドが自動的に設定されました。 これは、 クラスで、 を CategoriesBLL 更新および削除するための既定のメソッドとして を使用して DataObjectMethodAttribute これらのメソッドをマークしたためです。

ここでは、[更新] タブのドロップダウン リストを [(なし)] に設定しますが、[削除] タブのドロップダウン リストは に設定したままにします DeleteCategory。 手順 6 でこのウィザードに戻り、更新サポートを追加します。

DeleteCategory メソッドを使用するように ObjectDataSource を構成する

図 4: メソッドを使用 DeleteCategory するように ObjectDataSource を構成する (フルサイズの画像を表示するにはクリックします)

注意

ウィザードを完了すると、Visual Studio でフィールドとキーを更新するかどうかを確認するメッセージが表示されることがあります。これにより、データ Web コントロールフィールドが再生成されます。 [はい] を選択すると、行った可能性のあるフィールドのカスタマイズが上書きされるため、[いいえ] を選択します。

ObjectDataSource には、その DeleteMethod プロパティの値と と が DeleteParameter含まれるようになりました。 ウィザードを使用してメソッドを指定すると、Visual Studio によって ObjectDataSource の 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 に削除機能を追加します。 これにより、プロパティが に設定Trueされている ShowDeleteButton GridView に CommandField が追加されます。

GridView での削除のサポートを有効にする

図 5: GridView での削除のサポートを有効にする (フルサイズの画像を表示するをクリックします)

少し時間を取って、削除機能をテストしてください。 テーブルとテーブルの CategoryIDCategoriesCategoryIDProductsには外部キーがあるため、最初の 8 つのカテゴリのいずれかを削除しようとすると、外部キー制約違反の例外が発生します。 この機能をテストするには、パンフレットと画像の両方を提供する新しいカテゴリを追加します。 図 6 に示すテスト カテゴリには、 という名前 Test.pdf のテスト パンフレット ファイルとテスト画像が含まれています。 図 7 は、テスト カテゴリが追加された後の GridView を示しています。

パンフレットと画像を含むテスト カテゴリを追加する

図 6: パンフレットと画像を含むテスト カテゴリを追加する (フルサイズの画像を表示する をクリックします)

テスト カテゴリを挿入すると、GridView に表示されます

図 7: テスト カテゴリを挿入した後、GridView に表示されます (フルサイズの画像を表示する をクリックします)

Visual Studio で、ソリューション エクスプローラーを更新します。 フォルダーに新しいファイルが ~/BrochuresTest.pdf 表示されます (図 8 を参照)。

次に、[テスト カテゴリ] 行の [削除] リンクをクリックすると、ページがポストバックされ、クラスの DeleteCategory メソッドがCategoriesBLL起動します。 これにより DAL の Delete メソッドが呼び出され、適切な DELETE ステートメントがデータベースに送信されます。 その後、データは GridView にリバウンドされ、マークアップはテスト カテゴリが存在しない状態でクライアントに送り返されます。

削除ワークフローでは、テーブルから Categories テスト カテゴリ レコードが正常に削除されましたが、Web サーバーのファイル システムからパンフレット ファイルは削除されませんでした。 ソリューション エクスプローラーを更新すると、フォルダーに~/Brochuresまだ残っていることがわかりますTest.pdf

Test.pdf ファイルが Web サーバーのファイル システムから削除されませんでした

図 8: Web サーバーの Test.pdf ファイル システムからファイルが削除されませんでした

手順 5: 削除されたカテゴリのパンフレット ファイルを削除する

データベースの外部にバイナリ データを格納する欠点の 1 つは、関連するデータベース レコードが削除されたときに、これらのファイルをクリーンするために追加の手順を実行する必要があるということです。 GridView と ObjectDataSource は、delete コマンドが実行される前と後の両方で発生するイベントを提供します。 実際には、アクション前イベントと事後イベントの両方のイベント ハンドラーを作成する必要があります。 レコードを削除する Categories 前に、その PDF ファイルのパスを確認する必要がありますが、何らかの例外があり、カテゴリが削除されない場合に備えて、カテゴリが削除される前に PDF を削除する必要はありません。

GridView の RowDeleting イベント は、ObjectDataSource の delete コマンドが呼び出される前に発生し、その RowDeleted 後にイベント が発生します。 次のコードを使用して、これら 2 つのイベントのイベント ハンドラーを作成します。

' 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 値がある場合は、イベント ハンドラーでファイルを削除できるように、ページ変数 deletedCategorysPdfPathRowDeleted 格納されます。

注意

イベント ハンドラーでRowDeleting削除されるレコードのCategories詳細をBrochurePath取得するのではなく、 を GridView の DataKeyNames プロパティに追加BrochurePathし、コレクションを通じてレコードの値にe.Keysアクセスすることもできます。 これにより、GridView のビュー ステート サイズが若干大きくなりますが、必要なコードの量が減り、データベースへの移動が保存されます。

ObjectDataSource の基になる delete コマンドが呼び出されると、GridView の RowDeleted イベント ハンドラーが起動します。 データの削除に例外がなく、 の値 deletedCategorysPdfPathがある場合、PDF はファイル システムから削除されます。 この追加のコードは、画像に関連付けられているカテゴリのバイナリ データをクリーンするために必要ではないことに注意してください。 これは、画像データがデータベースに直接格納されるため、行を Categories 削除すると、そのカテゴリの画像データも削除されるためです。

2 つのイベント ハンドラーを追加した後、このテスト ケースをもう一度実行します。 カテゴリを削除すると、関連付けられている PDF も削除されます。

既存のレコードに関連付けられているバイナリ データを更新すると、いくつかの興味深い課題が発生します。 このチュートリアルの残りの部分では、パンフレットと画像に更新機能を追加する方法について説明します。 手順 6 では、パンフレット情報を更新するための手法について説明します。手順 7 では、画像の更新について説明します。

手順 6: カテゴリのパンフレットを更新する

「データの挿入、更新、および削除の概要」チュートリアルで説明したように、GridView には、基になるデータ ソースが適切に構成されている場合にチェック ボックスのチェック ボックスをオンにして実装できる、組み込みの行レベルの編集サポートが用意されています。 現在、 CategoriesDataSource ObjectDataSource はまだ更新サポートを含むように構成されていないため、追加してみましょう。

ObjectDataSource ウィザードの [データ ソースの構成] リンクをクリックし、2 番目の手順に進みます。 DataObjectMethodAttributeCategoriesBLLを使用しているため、UPDATE ドロップダウン リストには、4 つの入力パラメーターをUpdateCategory受け入れるオーバーロードが自動的に設定されます (ただし、Pictureすべての列に対して )。 これを変更して、5 つのパラメーターを持つオーバーロードを使用するようにします。

Picture のパラメーターを含む UpdateCategory メソッドを使用するように ObjectDataSource を構成する

図 9: のパラメーター Picture を含むメソッドをUpdateCategory使用するように ObjectDataSource を構成する (フルサイズの画像を表示するにはクリックします)

ObjectDataSource には、その UpdateMethod プロパティと対応する UpdateParameter の値が含まれるようになりました。 手順 4 で説明したように、Visual Studio では、データ ソースの構成ウィザードを使用するときに、ObjectDataSource の 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 を構成する (フルサイズの画像を表示する をクリックします)

ブラウザーからページにアクセスし、いずれかの行の [編集] ボタンをクリックします。 CategoryNameおよび Description BoundFields はテキスト ボックスとしてレンダリングされます。 BrochurePath TemplateField には EditItemTemplateが不足しているため、パンフレットへのリンクが引き続き表示ItemTemplateされます。 ImageField は Picture TextBox としてレンダリングされ、このText場合CategoryIDは ImageField のDataImageUrlField値の値がプロパティに割り当てられます。

GridView には、BrochurePath の編集インターフェイスがありません

図 11: の GridView には編集インターフェイス BrochurePath がありません (フルサイズの画像を表示する場合はクリックします)

編集インターフェイスのBrochurePathカスタマイズ

ユーザーが次のいずれかを可能にする TemplateField の BrochurePath 編集インターフェイスを作成する必要があります。

  • カテゴリのパンフレットをそのままにしておきます。
  • 新しいパンフレットをアップロードしてカテゴリのパンフレットを更新するか、
  • カテゴリのパンフレットを完全に削除します (カテゴリに関連するパンフレットがなくなった場合)。

また、ImageField の Picture 編集インターフェイスを更新する必要がありますが、手順 7 でこれを行います。

GridView のスマート タグで、[テンプレートの編集] リンクをクリックし、ドロップダウン リストから [TemplateFieldsEditItemTemplate] を選択BrochurePathします。 RadioButtonList Web コントロールをこのテンプレートに追加し、そのプロパティを IDBrochureOptions 設定し、その AutoPostBack プロパティを に True設定します。 プロパティ ウィンドウから、 プロパティの省略記号をItemsクリックすると、Collection エディターがListItem表示されます。 それぞれ 1、2、3 の 3 つのオプション Value を追加します。

  • 現在のパンフレットを使用する
  • 現在のパンフレットを削除する
  • 新しいパンフレットをアップロードする

最初 ListItem の s プロパティを Selected に設定します True

RadioButtonList に 3 つの ListItems を追加する

図 12: RadioButtonList に 3 つの ListItem s を追加する

RadioButtonList の下に、 という名前 BrochureUploadの FileUpload コントロールを追加します。 Visible プロパティを False に設定します。

RadioButtonList コントロールと FileUpload コントロールを EditItemTemplate に追加する

図 13: RadioButtonList コントロールと FileUpload コントロールを に EditItemTemplate 追加します (フルサイズの画像を表示するには、ここをクリックします)

この RadioButtonList には、ユーザー用の 3 つのオプションが用意されています。 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 コントロールの両方への参照を取得すると、 FileUpload コントロールの Visible プロパティは、RadioButtonList が SelectedValue 3 に等しい場合にのみに設定Trueされます。これはValue、新しいパンフレットListItemをアップロードするための です。

このコードを使用して、編集インターフェイスをテストします。 行の [編集] ボタンをクリックします。 最初に、[現在のパンフレットを使用する] オプションを選択する必要があります。 選択したインデックスを変更すると、ポストバックが発生します。 3 番目のオプションを選択すると、FileUpload コントロールが表示され、それ以外の場合は非表示になります。 図 14 は、[編集] ボタンが最初にクリックされたときの編集インターフェイスを示しています。図 15 は、[新しいパンフレットのアップロード] オプションが選択された後のインターフェイスを示しています。

最初は、[現在のパンフレットを使用する] オプションが選択されています

図 14: 最初は、[現在のパンフレットを使用する] オプションが選択されています (フルサイズの画像を表示するには、ここをクリックします)

[新しいパンフレットのアップロード] オプションを選択すると、FileUpload コントロールが表示されます

図 15: [新しいパンフレットのアップロード] オプションを選択すると、FileUpload コントロールが表示されます (フルサイズの画像を表示する場合はクリックします)

パンフレット ファイルの保存と列のBrochurePath更新

GridView の [更新] ボタンをクリックすると、その RowUpdating イベントが発生します。 ObjectDataSource の update コマンドが呼び出され、GridView イベント RowUpdated が発生します。 削除ワークフローと同様に、これらの両方のイベントのイベント ハンドラーを作成する必要があります。 RowUpdatingイベント ハンドラーでは、RadioButtonList の に基づいてSelectedValue実行するアクションを決定するBrochureOptions必要があります。

  • SelectedValueが 1 の場合は、同じBrochurePath設定を引き続き使用します。 したがって、ObjectDataSource の brochurePath パラメーターを更新するレコードの既存 BrochurePath の値に設定する必要があります。 ObjectDataSource の brochurePath パラメーターは、 を使用して e.NewValues["brochurePath"] = value設定できます。
  • SelectedValueが 2 の場合は、レコードのBrochurePath値を に設定しますNULL。 これは、ObjectDataSource の brochurePath パラメーターを にNothing設定することで実現できます。これにより、 ステートメントでUPDATEデータベースNULLが使用されます。 削除中の既存のパンフレット ファイルがある場合は、既存のファイルを削除する必要があります。 ただし、例外を発生させずに更新が完了した場合にのみ、これを行います。
  • SelectedValueが 3 の場合は、ユーザーが PDF ファイルをアップロードし、ファイル システムに保存し、レコードのBrochurePath列値を更新する必要があります。 また、置き換えられる既存のパンフレット ファイルがある場合は、前のファイルを削除する必要があります。 ただし、例外を発生させずに更新が完了した場合にのみ、これを行います。

RadioButtonList が SelectedValue 3 の場合に完了する必要がある手順は、DetailsView の ItemInserting イベント ハンドラーで使用されるものと実質的に同じです。 このイベント ハンドラーは、 前のチュートリアルで追加した DetailsView コントロールから新しいカテゴリ レコードが追加されたときに実行されます。 したがって、この機能を別のメソッドにリファクタリングする必要があります。 具体的には、一般的な機能を次の 2 つの方法に移行しました。

  • ProcessBrochureUpload(FileUpload, out bool) は、FileUpload コントロール インスタンスと、削除操作または編集操作を続行するか、何らかの検証エラーのために取り消す必要があるかを指定する出力ブール値として受け取ります。 このメソッドは、保存されたファイルへのパスを返します。または null 、ファイルが保存されていない場合は を返します。
  • DeleteRememberedBrochurePathが でないnull場合deletedCategorysPdfPathは、ページ変数deletedCategorysPdfPathのパスで指定されたファイルを削除します。

これら 2 つのメソッドのコードを次に示します。 前のチュートリアルの 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 と RowUpdatingRowUpdated イベント ハンドラーでは、 メソッドと DeleteRememberedBrochurePath メソッドをProcessBrochureUpload使用します。次のコードを示します。

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一連の条件付きステートメントを使用して RadioButtonList のSelectedValueプロパティ値に基づいて適切なアクションをBrochureOptions実行する方法に注意してください。

このコードを設定すると、カテゴリを編集し、現在のパンフレットを使用したり、パンフレットを使用したり、新しいパンフレットをアップロードしたりできます。 先に進み、試してみてください。イベント ハンドラーと RowUpdated イベント ハンドラーにRowUpdatingブレークポイントを設定して、ワークフローを理解します。

手順 7: 新しい画像をアップロードする

ImageField の編集インターフェイスは Picture 、プロパティの DataImageUrlField 値が設定されたテキスト ボックスとしてレンダリングされます。 編集ワークフロー中に、GridView はパラメーターを ObjectDataSource に渡し、パラメーターには ImageField のプロパティの値を、パラメーターの DataImageUrlField 値は編集インターフェイスのテキスト ボックスに入力した値を指定します。 この動作は、イメージがファイル システムにファイルとして保存され DataImageUrlField 、 にイメージの完全な URL が含まれている場合に適しています。 このような状況では、編集インターフェイスによってテキスト ボックスにイメージの URL が表示され、ユーザーは変更してデータベースに保存し直すことができます。 この既定のインターフェイスでは、ユーザーは新しいイメージをアップロードできませんが、イメージの URL を現在の値から別の値に変更できます。 ただし、このチュートリアルでは、バイナリ データがデータベースDataImageUrlFieldに直接格納され、 プロパティが のみをCategoryID保持しているためPicture、ImageField の既定の編集インターフェイスでは十分ではありません。

ユーザーが ImageField を使用して行を編集すると、チュートリアルで何が起こるかを理解するには、ユーザーが 10 の行 CategoryID を編集し Picture 、ImageField が値 10 のテキスト ボックスとしてレンダリングされる例を考えてみましょう。 ユーザーがこのテキストボックスの値を 50 に変更し、[更新] ボタンをクリックするとします。 ポストバックが発生し、GridView は最初に値 50 を持つ という名前 CategoryID のパラメーターを作成します。 ただし、GridView がこのパラメーター (および パラメーターと Description パラメーター) を送信するCategoryName前に、コレクションの値に をDataKeys追加します。 したがって、パラメーターは現在の CategoryID 行の基になる CategoryID 値 10 で上書きされます。 つまり、ImageField の編集インターフェイスは、ImageField DataImageUrlField の プロパティとグリッド DataKey の値の名前が同じであるため、このチュートリアルの編集ワークフローには影響しません。

ImageField を使用すると、データベース データに基づいてイメージを簡単に表示することができますが、編集インターフェイスにテキスト ボックスを指定する必要はありません。 むしろ、エンド ユーザーがカテゴリの画像を変更するために使用できる FileUpload コントロールを提供したいと考えています。 BrochurePath値とは異なり、これらのチュートリアルでは、各カテゴリに画像が必要であることを決定しました。 そのため、ユーザーが新しい画像をアップロードするか、現在の画像をそのままにしておくことができる、関連付けられた画像がないことをユーザーに示す必要はありません。

ImageField の編集インターフェイスをカスタマイズするには、それを TemplateField に変換する必要があります。 GridView のスマート タグで、[列の編集] リンクをクリックし、[ImageField] を選択して、[このフィールドを TemplateField に変換] リンクをクリックします。

ImageField を TemplateField に変換する

図 16: ImageField を TemplateField に変換する

この方法で ImageField を TemplateField に変換すると、2 つのテンプレートを含む TemplateField が生成されます。 次の宣言構文に示すように、 ItemTemplate には ImageField と DataImageUrlFieldDataImageUrlFormatString プロパティに基づくデータバインド構文を使用してプロパティが割り当てられている Image Web コントロールImageUrlが含まれています。 には EditItemTemplate 、 プロパティで指定された値にバインドされたプロパティを持つ Text TextBox が 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>

FileUpload コントロールを使用するように を EditItemTemplate 更新する必要があります。 GridView のスマート タグから [テンプレートの編集] リンクをクリックし、ドロップダウン リストから [TemplateFieldsEditItemTemplate] を選択Pictureします。 テンプレートに TextBox が表示され、これを削除します。 次に、FileUpload コントロールをツールボックスからテンプレートにドラッグし、その を ID に設定します PictureUpload。 また、テキストを追加する カテゴリの図を変更するには、新しい図を指定します。 カテゴリの画像を同じにするには、テンプレートにもフィールドを空のままにします。

FileUpload コントロールを EditItemTemplate に追加する

図 17: に FileUpload コントロールを追加する EditItemTemplate (フルサイズの画像を表示する場合はクリックします)

編集インターフェイスをカスタマイズしたら、ブラウザーで進行状況を表示します。 読み取り専用モードで行を表示すると、カテゴリの画像は以前と同じように表示されますが、[編集] ボタンをクリックすると、画像列が FileUpload コントロールを持つテキストとして表示されます。

編集インターフェイスには FileUpload コントロールが含まれています

図 18: 編集インターフェイスには、FileUpload コントロールが含まれています (フルサイズの画像を表示する をクリックします)

ObjectDataSource は、画像のバイナリ データを CategoriesBLL 配列として入力として受け入れるクラス s UpdateCategory メソッドを呼び出すように構成されていることを思い Byte 出してください。 ただし、この配列が の場合は Nothing、代替 UpdateCategory オーバーロードが呼び出され、列を UPDATE 変更 Picture しない SQL ステートメントが発行されるため、カテゴリの現在の図はそのまま残ります。 そのため、GridView の RowUpdating イベント ハンドラーでは、プログラムによって FileUpload コントロールを PictureUpload 参照し、ファイルがアップロードされたかどうかを判断する必要があります。 アップロードされていない場合は、 パラメーターの値pictureを指定しません。 一方、ファイルが FileUpload コントロールに PictureUpload アップロードされた場合は、JPG ファイルであることを確認する必要があります。 その場合は、 パラメーターを使用して、バイナリ コンテンツを ObjectDataSource に picture 送信できます。

手順 6 で使用したコードと同様に、ここで必要なコードの多くは DetailsView の ItemInserting イベント ハンドラーに既に存在します。 したがって、共通の機能を新しいメソッド にリファクタリングし、 ValidPictureUploadこのメソッドを使用するようにイベント ハンドラーを ItemInserting 更新しました。

GridView のイベント ハンドラーの先頭に次のコードを RowUpdating 追加します。 このコードは、無効な画像ファイルがアップロードされた場合に Web サーバーのファイル システムにパンフレットを保存しないため、パンフレット ファイルを保存するコードの前に記述することが重要です。

' 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 であることを確認します。画像ファイルがアップロードされた場合にのみ呼び出されます。 ファイルがアップロードされない場合、picture パラメーターは設定されないため、既定値の Nothingを使用します。 画像がアップロードされ、 ValidPictureUpload が返Truepictureされた場合、パラメーターにはアップロードされたイメージのバイナリ データが割り当てられます。メソッドから が返Falseされた場合、更新ワークフローは取り消され、イベント ハンドラーは終了します。

DetailsView のItemInsertingイベント ハンドラーからリファクタリングされたメソッド コードはValidPictureUpload(FileUpload)次のとおりです。

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 に置き換える

元の 8 つのカテゴリの図は、OLE ヘッダーでラップされたビットマップ ファイルであることを思い出してください。 既存のレコードの画像を編集する機能を追加したので、少し時間を取ってこれらのビットマップを JPG に置き換えます。 現在のカテゴリ図を引き続き使用する場合は、次の手順を実行して、それらを JPG に変換できます。

  1. ビットマップ イメージをハード ドライブに保存します。 ブラウザーの UpdatingAndDeleting.aspx ページにアクセスし、最初の 8 つのカテゴリごとに画像を右クリックし、画像を保存することを選択します。
  2. 選択したイメージ エディターでイメージを開きます。 たとえば、Microsoft ペイントを使用できます。
  3. ビットマップを JPG イメージとして保存します。
  4. JPG ファイルを使用して、編集インターフェイスを使用してカテゴリの画像を更新します。

カテゴリを編集して JPG イメージをアップロードした後、ページが最初の 8 つのカテゴリの画像から最初の 78 バイトを削除しているため DisplayCategoryPicture.aspx 、画像はブラウザーでレンダリングされません。 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 と Description GridView の および BoundFields を TemplateFields に変換する必要があります。 CategoryName値は許可NULLされないため、RequiredFieldValidator を追加する必要があります。 Description TextBox は、おそらく複数行の TextBox に変換する必要があります。 私はこれらの仕上げの仕上げをあなたのための練習として残します。

まとめ

このチュートリアルでは、バイナリ データの操作について説明します。 このチュートリアルと前の 3 つのチュートリアルでは、バイナリ データをファイル システムまたはデータベース内に直接格納する方法について説明しました。 ユーザーは、ハード ドライブからファイルを選択して Web サーバーにアップロードすることで、バイナリ データをシステムに提供します。ここで、ファイル システムに格納したり、データベースに挿入したりできます。 ASP.NET 2.0 には FileUpload コントロールが含まれており、ドラッグ アンド ドロップのように簡単にインターフェイスを提供できます。 ただし、ファイルのアップロードに関 する チュートリアルで説明したように、FileUpload コントロールは比較的小さいファイルのアップロードにのみ適しています。理想的にはメガバイトを超えるものではありません。 また、アップロードされたデータを基になるデータ モデルに関連付ける方法と、既存のレコードからバイナリ データを編集および削除する方法についても説明しました。

次の一連のチュートリアルでは、さまざまなキャッシュ手法について説明します。 キャッシュは、負荷の高い操作の結果を取得し、より迅速にアクセスできる場所に格納することで、アプリケーションの全体的なパフォーマンスを向上させる手段を提供します。

プログラミングに満足!

著者について

7 冊の ASP/ASP.NET 書籍の著者であり、 4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジと協力しています。 Scott は独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・自分自身 ASP.NET 24時間で2.0です。 にアクセスmitchell@4GuysFromRolla.comすることも、ブログを介して アクセスすることもできます。これは でhttp://ScottOnWriting.NET確認できます。

特別な感謝

このチュートリアル シリーズは、多くの役立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、テレサ マーフィーでした。 今後の MSDN 記事の確認に関心がありますか? その場合は、 に行mitchell@4GuysFromRolla.comをドロップしてください。