Microsoft Access2003

Page 4 (2005/01)

画像ファイルをテーブルに登録する
テーブルの各レコードに登録された画像ファイルをフォームに表示する方法は前のページでやってみましたが、ここでは登録する方法です。
単純な方法としては、基本的にテキスト型フィールドに画像ファイル名を書き込むだけのことですから、テキストボックスに手入力すればいいことなのですけど、Access付属のサンプル・データベース Northwind.mdb の社員フォームのなかでは、Windowsで頻繁に使用するファイル選択ダイアログを使って目的の画像ファイルを選択登録する方法が使われています。
ちょっと煩雑な手順なので、ここではそのコードを解読してみます。
フォームは右図の通りシンプルです。
右半分の写真とふたつのボタンがターゲットです。
上のフォームを編集状態にしたのが左の図です。
「写真」と表示されているのが写真フィールドに連結されたテキストボックス。コントロールの名前はImagePath

その下の「写真を追加・・」というのはメッセージとして使われるラベルコントロール。名前はerrormsg。
このふたつは不可視状態です。

外側の四角の枠がイメージコントロール。名前はImageFrameです。

「追加変更」コマンドボタンをクリックすると、ファイル選択ダイアログを表示し、選択されたファイル名をテキストボックス「写真」の中に代入して、その画像をイメージコントロールに表示する・・・というプロセスです。
コマンドボタン「追加/変更」をクリックすると、以下のような汎用プロシージャgetFileNameを実行します。

Sub getFileName()
  ' Office の [ファイルを開く] ダイアログ ボックス使って、
  ' 現在の社員レコードのファイル名を選択します。ファイルを選択した場合、
  ' イメージ コントロール中にそのファイルを表示します。
  Dim fileName As String   ←ファイル名を受け取る文字型変数を宣言
  Dim result As Integer     ←ファイル選択ダイアログでどのボタンを押したかを受け取る数値型変数を宣言
  With Application.FileDialog(msoFileDialogFilePicker)
    .Title = "Select Employee Picture"    ←ダイアログボックスのタイトルを指定する
    .Filters.Add "すべてのファイル", "*.*"  ←ダイアログの「ファイルの種類」コンボボックスに表示するフィルターを指定。
    .Filters.Add "JPEG ファイル", "*.jpg"
    .Filters.Add "ビットマップ", "*.bmp"
    .FilterIndex = 3                 ←3個目のビットマップをデフォルトに指定する
    .AllowMultiSelect = False           ←ファイルの複数は不可にする
    .InitialFileName = CurrentProject.path   ←ファイルのダイアログ ボックスに初期表示されるパスやファイル名を設定します。
    result = .Show   ← アクション ボタン (-1) とキャンセル ボタン (0) のどちらがクリックされたかを示す長整数型 (Long) の値を返します
    If (result <> 0) Then
      fileName = Trim(.SelectedItems.Item(1)) ←ダイアログで選択したファイル名を変数に渡す
      Me![ImagePath].Visible = True       ←ここから下の処理でフォームのテキストボックスにファイル名を渡す
      Me![ImagePath].SetFocus            値を代入するときだけ不可視状態のテキストボックスを可視状態にしています。
      Me![ImagePath].Text = fileName
      Me![フリガナ].SetFocus
      Me![ImagePath].Visible = False
    End If
  End With
End Sub

このコードのハイライトはWithとEnd Withで挟まれた部分です。
オブジェクトとコレクションなどの定義、関係についてはとても分かりにくいので、FileDialogオブジェクトを使うことによって、標準的なダイアログボックスを表示するとでも理解してしまいましょう。
引数は四種類あって、定数msoFileDialogFilePickerはそのなかのひとつでファイルを参照するものです。中身はというと数値の3が入っているだけなのですけど。
左の図が、オープンするダイアログです。
result=.Show のところで一時停止してユーザーの選択を待ちます。
OKボタンを押した時だけ、以降の登録処理をするわけです。

ところで、このサンプル・プログラムを実際に動かしてみると、右図のような結果になります。
写真を入れ替える操作をしたところ、一番下のレコードのように、画像ファイル名がフルパスで書き込まれています。

テーブルの構造自体は長い文字列が入るように作られているので問題はないのですし、必要とあれば長い文字列からファイル名を切り出してテーブルに保存する処理を入れれば済むことですが、トレースしてみて、そのような処理はされていないのでした。
SelectedItems.Item(1)が返すのはフルパス名のようです。
このような場合、普通はフィールドに入る値の形式を統一するものでしょうけど、フルパス名でもファイル名だけでも支障がないというのは、それなりの処理をしているからで、その部分はというと・・・・。

If Not IsNull(Me!写真) Then
   res = IsRelative(Me!写真)
   fName = Me![ImagePath]
   If (res = True) Then
      fName = path & "\" & fName
   End If

このように、必要とあればパス名をファイル名に追加しているからです。
ユーザー定義関数 IsRelative() でパス名の有る無しを判断してTrue/Falseを受け取っています。

Function IsRelative(fName As String) As Boolean
   ' ファイル名にドライブまたは UNC パスを含む場合、false を返します。
   IsRelative = (InStr(1, fName, ":") = 0) And (InStr(1, fName, "\\") = 0)
End Function

たった一行だけの関数ですが、要は InStr()関数でファイル名のなかに”:”と”\\”が含まれていなければTrue、含まれていればFalseがIsRelativeに代入されるというだけのものです。
 テーブルからリンクで呼び出される画像ファイルはmdbと同じフォルダにあればパス名がなくてもOKなのか?とやってみたところ、やはりフルパス名で指定しないとダメなようです
上の1行は間違えていました。というか正確な表現ではなかった。
なぜ、サンプルデータベースの「写真」フィールドにファイル名だけが入っていて、ユーザーがデータを追加修正するとフルパス名が入るのか?と考えると、ユーザーがサンプルデータベースをインストールするフォルダはいろいろ違う訳ですから、どのフォルダにあっても問題なく表示するためには、フィールドの値はファイル名のみとし、表示するときにパス名を接続して表示しているのでした。
一方、ユーザーが追加登録なり修正をするときはフルパス(絶対パス)で登録しても問題はない、ということです。
このプログラムは両方に対応しています。
もちろん、フルパスで画像ファイルを指定する場合は、そまファイルがどこのフォルダにあってもいいのですが、ファイル名のみをフィールドに登録する場合は、mdbのあるカレント・ディレクトリに画像ファイルを置かないとダメ、ということです。

\\Dell8300\my documents of 8300\Access_Data\troiscouleurs_blue.jpg (例1)

これはフルパス名を含む画像ファイルの例ですが、この文字列からファイル名のみを取り出す手順を考えてみました。

fileName = Mid(.SelectedItems.Item(1), InStrRev(Trim(.SelectedItems.Item(1)), "\") + 1, 50)

文字列を扱う関数、Mid()InStrRev()Trim()を使っているので読みにくいです。
InStrRevは文字列の右側から、指定された文字("\")が先頭からいくつ目にあるかを数値で返す関数です。これに1を加算すると、ファイル名の最初の文字の位置が分かります。
このあと、Mid関数でここから50文字を取り出して、Trim関数で右側の空白を取り除きます。
このようにすれば、画像ファイルの名前を保存するテキストフィールドは、フルパス名で保存するよりは小さなサイズで済みます。
シンプルに作り始めたフォームですが、今まで使ったことのないコントロールを使ってみたので賑やかになりました。

まだこれでも完成と言うわけではないのですが、これ以上のデータを追加する必要は無さそうです。

データの検索にタイトル検索を追加して画面を整えれば完成と言えるかもしれません。
最後の仕上げとして、「タイトル検索」のコンボボックスを追加して、検索部分を四角の破線で囲みました。
この非連結コンボボックスはウィザ−ドで作りました。フォームで特定のデータを検索して表示するのにはどうするんだったっけ?と後回しにしてしまったのですが、ウィザ−ドでを使えば簡単でした。以下のようなコードが自動で生成されます。

Private Sub cboタイトル検索_AfterUpdate()
  ' コントロールの値と一致するレコードを検索する
  Dim rs As Object
  Set rs = Me.Recordset.Clone
  rs.FindFirst "[DVD_ID] = " & Str(Nz(Me![cboタイトル検索], 0))
  If Not rs.EOF Then Me.Bookmark = rs.Bookmark
End Sub

コンボボックスで選択したり入力するのは「タイトル」の文字列ですが、不可視状態になっているコンボボックスの先頭の列は数値型の「DVD_ID」です。
rs.FindFirst "[DVD_ID] = " & Str(Nz(Me![cboタイトル検索], 0))

この部分は以下のように単純に書いても問題はないようですが、ウィザ−ドは二つの関数Str()Nz()を使って丁寧な処理をしています。
rs.FindFirst "[DVD_ID] = " & Me![cboタイトル検索]

これに続く1行でBookmarkを使ってフォームの表示レコードを移動しています。
登録されていない値を入力した場合は、Page 1 で紹介した「リストにデータが無い」というエラーメッセージが出ます。

タイトル検索した後、フォームのレコードを移動すると、タイトル検索コンボボックスの値とフォームのデータがずれてしまうのが不自然な感じがするので、以下の1行を Form_Current() プロシージャに追加して、タイトル検索・コンボボックスの値を連動させるようにしました。

Me!cboタイトル検索 = Me!DVD_ID
frmDVDのプログラム


Page Topへ戻る