次の方法で共有


既存のバイナリ データを更新し、削除する (C#)

スコット・ミッチェル著

PDF をダウンロードする

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

イントロダクション

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

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

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

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

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

UPDATE オプションを選択する

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

次に、 UPDATE SQL ステートメントを指定する必要があります。 ウィザードは、TableAdapter のメイン クエリ (UPDATECategoryName、およびDescriptionの値を更新するクエリ) に対応するBrochurePathステートメントを自動的に提案します。 次のように、 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 bool DeleteCategory(int categoryID)
{
    int rowsAffected = Adapter.Delete(categoryID);
    // Return true if precisely one row was deleted, otherwise false
    return rowsAffected == 1;
}

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

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

[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;
}

手順 3: 挿入および表示機能をコピーする

前の チュートリアル では、GridView のすべてのカテゴリを一覧表示する UploadInDetailsView.aspx という名前のページを作成し、新しいカテゴリをシステムに追加するための DetailsView を提供しました。 このチュートリアルでは、GridView を拡張して、編集と削除のサポートを含めます。 UploadInDetailsView.aspxから作業を続けるのではなく、同じフォルダーのUpdatingAndDeleting.aspx ページにこのチュートリアルの変更を配置~/BinaryData。 宣言型マークアップとコードをコピーし、 UploadInDetailsView.aspx から UpdatingAndDeleting.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 がバインドされている ObjectDataSource (CategoriesDataSource) は削除をサポートしていません。

これを解決するには、ObjectDataSource のスマート タグの [データ ソースの構成] オプションをクリックしてウィザードを起動します。 最初の画面は、ObjectDataSource が CategoriesBLL クラスで動作するように構成されていることを示しています。 [次へ] をクリックします。 現時点では、ObjectDataSource の InsertMethod プロパティと SelectMethod プロパティのみが指定されています。 ただし、ウィザードでは、[UPDATE] タブと [DELETE] タブのドロップダウン リストに、それぞれ UpdateCategory メソッドと DeleteCategory メソッドが自動的に設定されました。 これは、 CategoriesBLL クラスでは、更新と削除の既定のメソッドとして DataObjectMethodAttribute を使用してこれらのメソッドをマークしたためです。

ここでは、[UPDATE] タブのドロップダウン リストを [なし] に設定しますが、[DELETE] タブのドロップダウン リストは [ 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 に削除機能を追加します。 これにより、 ShowDeleteButton プロパティが true に設定されている GridView に CommandField が追加されます。

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

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

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

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

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

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

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

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

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

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

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

図 8: Test.pdf ファイルが Web サーバーのファイル システムから削除されなかった

ステップ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 
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 イベント ハンドラーでは、削除される行のCategoryIDが GridView の DataKeys コレクションから取得されます。これは、e.Keys コレクションを介してこのイベント ハンドラーでアクセスできます。 次に、 CategoriesBLL クラスの GetCategoryByCategoryID(categoryID) が呼び出され、削除されるレコードに関する情報が返されます。 返されたCategoriesDataRow オブジェクトにNULL``BrochurePath以外の値がある場合は、deletedCategorysPdfPath イベント ハンドラーでファイルを削除できるように、RowDeletedページ変数に格納されます。

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

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

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

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

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

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

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

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

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

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

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

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

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

BrochurePath TemplateField の編集インターフェイスを作成する必要があります。このインターフェイスを使用すると、ユーザーは次のいずれかを実行できます。

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

Picture ImageField の編集インターフェイスも更新する必要がありますが、手順 7 で説明します。

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

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

最初の ListItemSelected プロパティを trueに設定します。

RadioButtonList に 3 つの ListItem を追加する

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

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

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

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

この RadioButtonList は、ユーザーに 3 つのオプションを提供します。 FileUpload コントロールは、最後のオプションである [新しいパンフレットのアップロード] が選択されている場合にのみ表示されるということです。 これを実現するには、RadioButtonList の SelectedIndexChanged イベントのイベント ハンドラーを作成し、次のコードを追加します。

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");
}

RadioButtonList コントロールと FileUpload コントロールはテンプレート内に存在するため、これらのコントロールにプログラムでアクセスするためのコードを少し記述する必要があります。 SelectedIndexChanged イベント ハンドラーには、sender入力パラメーターに RadioButtonList の参照が渡されます。 FileUpload コントロールを取得するには、RadioButtonList の親コントロールを取得し、そこから FindControl("controlID") メソッドを使用する必要があります。 RadioButtonList コントロールと FileUpload コントロールの両方への参照がある場合、FileUpload コントロールの Visible プロパティは、RadioButtonList true が 3 に等しい場合にのみSelectedValueに設定されます。これは、新しいパンフレットのアップロード ValueListItemです。

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

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

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

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

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

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

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

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

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

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

これら 2 つのメソッドのコードは次のとおりです。 前のチュートリアルの ProcessBrochureUpload と DetailsView ItemInserting イベント ハンドラーの類似性に注意してください。 このチュートリアルでは、これらの新しいメソッドを使用するように DetailsView のイベント ハンドラーを更新しました。 このチュートリアルに関連付けられているコードをダウンロードして、DetailsView のイベント ハンドラーに対する変更を確認します。

private string ProcessBrochureUpload
    (FileUpload BrochureUpload, out bool CancelOperation)
{
    CancelOperation = false;    // by default, do not cancel operation
    if (BrochureUpload.HasFile)
    {
        // Make sure that a PDF has been uploaded
        if (string.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName), 
            ".pdf", true) != 0)
        {
            UploadWarning.Text = 
                "Only PDF documents may be used for a category's brochure.";
            UploadWarning.Visible = true;
            CancelOperation = true;
            return null;
        }
        const string BrochureDirectory = "~/Brochures/";
        string brochurePath = BrochureDirectory + BrochureUpload.FileName;
        string fileNameWithoutExtension = 
            System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
        int iteration = 1;
        while (System.IO.File.Exists(Server.MapPath(brochurePath)))
        {
            brochurePath = string.Concat(BrochureDirectory, fileNameWithoutExtension, 
                "-", iteration, ".pdf");
            iteration++;
        }
        // Save the file to disk and set the value of the brochurePath parameter
        BrochureUpload.SaveAs(Server.MapPath(brochurePath));
        return brochurePath;
    }
    else
    {
        // No file uploaded
        return null;
    }
}
private void DeleteRememberedBrochurePath()
{
    // Is there a file to delete?
    if (deletedCategorysPdfPath != null)
    {
        System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath));
    }
}

GridView の RowUpdating イベント ハンドラーと RowUpdated イベント ハンドラーでは、次のコードに示すように、 ProcessBrochureUpload メソッドと DeleteRememberedBrochurePath メソッドが使用されます。

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();
    }
}

RowUpdating イベント ハンドラーが一連の条件付きステートメントを使用して、BrochureOptions RadioButtonList のSelectedValueプロパティ値に基づいて適切なアクションを実行する方法に注意してください。

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

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

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

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

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

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

ImageField を TemplateField に変換する

図 16: ImageField を TemplateField に変換する

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

<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 のスマート タグから [テンプレートの編集] リンクをクリックし、ドロップダウン リストから Picture TemplateField の EditItemTemplate を選択します。 テンプレートに TextBox が表示され、これを削除します。 次に、ツールボックスからテンプレートに FileUpload コントロールをドラッグし、その IDPictureUploadに設定します。 また、テキストを追加カテゴリの画像を変更するには、新しい画像を指定します。 カテゴリの画像を同じにするには、テンプレートにもフィールドを空のままにします。

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

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

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

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

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

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

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

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

// 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;
    }
}

ValidPictureUpload(FileUpload) メソッドは、FileUpload コントロールを唯一の入力パラメーターとして受け取り、アップロードされたファイルの拡張子をチェックして、アップロードされたファイルが JPG であることを確認します。画像ファイルがアップロードされた場合にのみ呼び出されます。 ファイルがアップロードされない場合、picture パラメーターは設定されないため、既定値の null が使用されます。 画像がアップロードされ、 ValidPictureUploadtrue返された場合、 picture パラメーターにはアップロードされたイメージのバイナリ データが割り当てられます。メソッドが falseを返すと、更新ワークフローが取り消され、イベント ハンドラーが終了します。

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

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

手順 8: 元のカテゴリの画像を JPG に置き換える

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

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

カテゴリを編集して JPG イメージをアップロードした後、 DisplayCategoryPicture.aspx ページが最初の 8 つのカテゴリの画像から最初の 78 バイトを削除しているため、イメージはブラウザーにレンダリングされません。 これを修正するには、OLE ヘッダーの削除を実行するコードを削除します。 これを行った後、 DisplayCategoryPicture.aspx``Page_Load イベント ハンドラーには次のコードが必要です。

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);
}

UpdatingAndDeleting.aspxページの挿入および編集インターフェイスでは、もう少し作業が必要になる可能性があります。 DetailsView と GridView の CategoryNameDescription BoundFields を TemplateFields に変換する必要があります。 CategoryNameではNULL値が許可されないため、RequiredFieldValidator を追加する必要があります。 Description TextBox は、おそらく複数行の TextBox に変換する必要があります。 私はこれらの仕上げを練習としてあなたに残します。

概要

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

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

プログラミングに満足!

著者について

7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジを使用しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・セルフ ASP.NET 24時間で2.0です。 彼には mitchell@4GuysFromRolla.comで連絡できます。

特別な感謝

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