Microsoft Access2003

Page 9 (2005/01)

一段落、そしていろいろと脱線
前のページで一応一段落しました。ここから先はマルチユーザーとかアクティブXだとか、さらにややこしい世界になるので、そこまで入り込みたくないって感じです(/_;)
ソフト関係の解説書ってもうしばらく買ったことがないので、手元になるAccess97のを使いました。Microsoftが出していた「Microsoft Access アプリケーション開発ガイド」というものですが、今は出てないみたい。旧いものを何冊か持っているのですけど、結局これとオンライン・ヘルプが一番いいみたい。
翻訳物なので文章が読みにくいのが難点ですけど、詳細に書かれているものは他にあまりない。
同じマイクロソフト出版の分厚いオフィシャル・マニュアルみたいなものも持っているのですが、大半が翻訳なので、辞書代わりに見るだけでした。
今回、新しいものも2冊ほど買ったのですが、リファレンスとしては役に立ちそうです。
関数や定数を一覧できるものが手元にないと、調べるのにものすごく時間がかかります。
オンライン・ヘルプなどは、ますます煩雑で分かりにくくなっていて、辟易しました。

ここまででやり残したことというとデバッグとエラー処理なのですけど、これはいつも後回しです(/_;)
思い出しながらいじり回してみると、いろいろ面白いところがあります。以前のように目先の目的がないので、遊び半分で気楽にできます。
前のページの最後のサンプルはパラメータ・クエリの使い方の例なのですが、ちょっと脱線して、上図のようになりました。
とりあえずこのような使い方をする必要は無いのですけど、必要とあればこのようなパターンで使えるかな?ということで改良してみました。
サンプルコードのオリジナルでは、クエリの結果を配列変数に入れるFunctionプロシージャだったのですけど、よく分からないのでSubプロシージャにして、ややこしい配列変数ではなく、文字列にして大きなテキストボックスに結果を表示させましたが、これだと表示行数が多くなると表示しきれない。
それならばリストボックスを・・・使うのはどうやるんだっけ?と調べながら変更した結果です。

クエリのパラメータとして使われる「開始年」「と終了年」も当初のテキストボックスへの手入力からコンボボックスに変更しました。
リストボックスの表示は通常はテーブルかクエリーを使うことが多いですが、値リストで渡してます。
各レコードを文字列の連結でひとつにすると見た目が悪くなってしまうのですが、四苦八苦して上図のように、テーブル/クエリと同じような表示ができるようになりました。
まず「コントロールの種類の変更」でテキストボックスだった「開始年」「終了年」をコンボボックスに変更します。新しくコントロールを作るのとさほど変わらないのですけどね。ここに入る値は基本的にはテーブル「DVD」のフィールド制作年のデータでいいわけですから、値集合ソース/RowSourceにはクエリをセットします。クエリビルダでお手軽に作れるのですが、条件としては、制作年で昇順に並べ替えて表示する、空白は除外する、重複も除外するということですので
以下のようなSQL文が生成されました。DVD.制作年)<>"l" という条件がいまいち飲み込めないのですけど、空白フィールドを除外しているようです。
重複レコードを除外するのはSELECTのあとの DISTINCT です。
コンボボックスのプロパティ、入力チェック/LimimtToListを「はい」にすれば登録された値以外は入らなくなるし、「いいえ」にすれば入力可能になります。

SELECT DISTINCT DVD.制作年 FROM DVD WHERE (((DVD.制作年)<>"l")) ORDER BY DVD.制作年;

「終了年」もほぼ同じですが、開始年より前の年は必要ないということで、DVD.制作年)>=[txtStart]という条件を追加しています。
このほかに、開始年と終了年が逆のケースを考えてこのあとのコマンドボタンでもチェックを入れてます。

SELECT DISTINCT DVD.制作年 FROM DVD WHERE (((DVD.制作年)>=[txtStart])) ORDER BY DVD.制作年;

で・・・リストボックスですが、値集合タイプ/RowSourceType・プロパティを「値リスト/Value List」に設定して、値集合ソース/RowSourceに値リストを設定します。
通常は "ABC;DEF;GHI"といった具合に文字列を ; で区切って渡しますが、列数/ColumnCountプロパティを 3 に設定して、渡す文字列を、
"A,B,C;D,E,F;G,H,I"というふうにカンマで区切ると、テーブル/クエリーと同じようにきちんと整列して表示できます。
Option Compare Database
Option Explicit
Sub ParameterQueryTest(strStart As String, strEnd As String)
  Dim dbs As Database, rst As Recordset, qdf As QueryDef
  Dim strSQL As String, strList As String
  Set dbs = CurrentDb

  strSQL = "PARAMETERS [開始年] string,[終了年] string ;"
  strSQL = strSQL & "SELECT DVD.制作年, DVD.タイトル, 監督名.監督名, 俳優名.俳優名 "
  strSQL = strSQL & "FROM 俳優名 RIGHT JOIN (監督名 RIGHT JOIN DVD ON 監督名.監督名ID = DVD.監督名) "
  strSQL = strSQL & "ON 俳優名.俳優名ID = DVD.出演者1 "
  strSQL = strSQL & "WHERE (((DVD.制作年) Between [開始年] And [終了年])) ORDER BY DVD.制作年;"
  
  Set qdf = dbs.CreateQueryDef("", strSQL)  '新しいクエリを名前を付けずに作成
  qdf.Parameters("開始年") = strStart      'フォームの値をパラメーターに代入する
  qdf.Parameters("終了年") = strEnd
  Set rst = qdf.OpenRecordset(dbOpenSnapshot, dbForwardOnly)     'クエリからレコードセットをオープン
  
  While rst.EOF = False       'クエリのレコードセットのレコードを値リストのための変数に代入する
    strList = strList & rst!制作年 & "," & rst!タイトル & "," & rst!監督名 & "," & rst!俳優名 & ";"
    rst.MoveNext
  Wend
  リストボックス5.RowSourceType = "value list"  '値集合タイプを値リストに設定
  リストボックス5.RowSource = strList        'リストボックスの値集合ソースに変数をセットする
  Me.Repaint                        'これを入れないと画面にリストの値が表示されない
End Sub

Private Sub cmdGO_Click()
  If IsNull(txtStart) Or IsNull(txtEnd) Then
    Beep
    MsgBox "開始日と終了日を入力してください"
    txtStart.SetFocus
  Else
    If txtEnd < txtStart Then
      MsgBox "開始日と終了日の日付が逆です"
      txtEnd = ""
      Me.Requery
    Else
      ParameterQueryTest txtStart, txtEnd
    End If
  End If
End Sub
サブ・フォームを使ってアレンジ
もともとパラメータ・クエリのレコードセットのサンプルなのですが、条件入力でデータにフィルターをかけて表示するほかの方法、ということでサブフォームを使ったものも作ってみました。
サブフォーム frmSubForm_sub は「制作年」の順で並べ替えただけのクエリをレコードソースにしています。
このサブフォームのフィルター・プロパティに値をセットするだけなのでコードは簡単です。
ただ、サブフォームの値を指定する方法がややこしいですね。適当に試行錯誤でやってみましたけど、なぜこのように書くのか?はうまく説明できません。
といいながら・・・
下の二つのコマンドラインはまったく同じです。
Me.frmSubForm_sub.Form.FilterOn = False
Forms!frmsubform!frmSubForm_sub.Form.FilterOn = False

Formsコレクションのなかのフォーム・オブジェクトfrmsubformフォームのなかのfrmSubForm_subオブジェクトのなかのFormオブジェクトのFilterOnプロパティ・・・とうことなのでしょうか・・・
Option Compare Database
Option Explicit
Private Sub cmdFilterOff_Click()
  Me.frmSubForm_sub.Form.FilterOn = False
End Sub
Private Sub cmdGO_Click()
  If IsNull(txtStart) Or IsNull(txtEnd) Then
    Beep
    MsgBox "開始日と終了日を入力してください"
    txtStart.SetFocus
  Else
    If txtEnd < txtStart Then
      MsgBox "開始日と終了日の日付が逆です"
      txtEnd = ""
      Me.Requery
    Else
      Me.frmSubForm_sub.Form.Filter = "[制作年]>=txtStart and [制作年]<=txtEnd"
      Me.frmSubForm_sub.Form.FilterOn = True

      cmdFilterOff.Enabled = True
    End If
  End If
End Sub
Private Sub txtStart_GotFocus()
  cmdFilterOff.Enabled = False
End Sub
リストボックス ListBox
前ページからこのページにかけて、たまたま、縦長のデータ表示コントロールが必要になってテキストボックスの代わりにスクロールバーの付いたコントロールということでリストボックスを使ってみたのですが、もともとは複数選択ができるコンボボックスという機能なので、思い出したり調べたりをやってみました。
下の図がそのフォーム frmListBox の実行画面です。
リストボックスには「複数選択/MultiSelect」プロパティがあって、「しない/None」 「標準/Simple」 「 拡張/Expand」が選べます。
Noneだとコンボボックスと同じ機能になってしまうので、今回は複数選択を試すということでSimpleに設定しました。

コードは実行ボタンcmdGO のクリックイベントだけで短いものですがループ処理で配列の値のリストみたいな処理をしているので読みにくいです。
コメント行とプロパティの解説を付けてみました。
Option Compare Database
Option Explicit
Private Sub cmdGO_Click()
  Dim intCount As Integer, intItem As Integer, strNamae As String
  For intCount = 0 To lstListBox.ItemsSelected.Count - 1
      'ItemSelected.Countは選択レコードの総数を返すがintCountが0から始まるので-1する。
    intItem = lstListBox.ItemsSelected(intCount)
      '選択レコードの整数インデックスを変数intItemに代入する
    strNamae = strNamae & lstListBox.ItemData(intItem) & " " & _
      lstListBox.Column(1, intItem) & vbCrLf
      'ItemDataはテキストボックスの連結列の値を返す。Columm(x,y)は連結列に関係なく指定されたカラムの値を返します。
  Next
  txtTextBox = strNamae
End Sub

Sub RowsSelected() 'Accessヘルプのなかのサンプルをアレンジしたもの
  Dim ctlList As Control, varItem As Variant
  ' リスト ボックスを示す Control オブジェクトを取得します。
  Set ctlList = Forms!frmListBox!lstListBox
  ' 選択項目を列挙します。
  For Each varItem In ctlList.ItemsSelected
    ' 連結列の値を表示します。
    Debug.Print ctlList.ItemData(varItem) & " " & ctlList.Column(1, varItem)
  Next varItem
End Sub

Private Sub cmdPrint_Form_Controls_Click()
  Print_Form_Controls
End Sub

Private Sub cmdRowsSelected_Click()
  RowsSelected
End Sub

Sub Print_Form_Controls() 'AccessヘルプのCountプロパティのサンプルコード
  Dim frm As Form, intI As Integer, intJ As Integer
  Dim intControls As Integer, intForms As Integer
  intForms = Forms.Count    ' 開かれているフォームの数
  If intForms > 0 Then
    For intI = 0 To intForms - 1
      Set frm = Forms(intI)
      Debug.Print frm.Name
      intControls = frm.Count
      If intControls > 0 Then
        For intJ = 0 To intControls - 1
          Debug.Print vbTab; frm(intJ).Name
        Next intJ
      Else
        Debug.Print vbTab; "(コントロールはありません)"
      End If
    Next intI
  Else
    MsgBox "開かれているフォームはありません。", vbExclamation, "フォームとコントロール"
  End If
End Sub

ItemsSelected コレクションは、オブジェクトのコレクションではなく、バリアント型 (Variant) 変数のコレクションであるという点で、他のコレクションとは異なります。各バリアント型変数は、リスト ボックスまたはコンボ ボックスの選択行を参照する整数インデックスです
ItemsSelected コレクションにはメソッドはありません。また、ItemsSelected コレクションに存在するプロパティは、CountItem プロパティの 2 つです

Count プロパティ   指定されたコレクションの中の項目数を示します。値の取得および設定が可能です。整数型 (Integer) の値を使用します。

ItemData プロパティ  コンボ ボックスまたはリスト ボックスの連結列の、指定した行のデータを返します。値の取得のみ可能です。バリアント型 (Variant) の値を使用します。

リストボックスとコンボボックスは視覚的にデータを選択するという機能は似ていますし、プロパティも共通するようです。
コンボボックスのメリットはフォーム上にスペースをとらないことと、設定値以外の値も入力可能なテキストボックスと同じ機能を持っています。
これに対してリストボックスのメリットはというと、複数選択ができるということです。


Page Topへ戻る