Private Sub コマンド15_Click()
Dim str出身地 As String '出身地の管理
Dim n As Integer 'サーチ文字列の発見場所
Dim rs As New ADODB.Recordset 'ADOのレコードセット
Dim strSQL As String 'SQL文を作成するため
'T_メンバーテーブルから出身地をバンドIDがフォームの値と一緒
strSQL = "Select 出身地 From T_メンバー " _
& " Where バンドID = " & Me![バンドID]
'レコードセットを開く
rs.Open strSQL, CurrentProject.Connection, _
adOpenKeyset, adLockOptimistic
'ループ処理
str出身地 = "" '空文字で初期化
While rs.EOF = False 'いつものEOFが偽の間
'バッファの中に同じ出身地があるか場所をチェックする
n = InStr(str出身地, rs.Fields("出身地"))
If n = 0 Then '出身地が見つからなかったら(重複してない時)
'出身地と" "スペース1つを+する
str出身地 = str出身地 & rs.Fields("出身地") & " "
End If
rs.MoveNext '次のレコードに移動しないと、とんでもないことに(笑)
Wend
rs.Close '開いたら閉じろ、ドアを開けたら閉めるってしつけられたでしょ(笑)
Set rs = Nothing '変数も後始末しますか。使った器はキレイにしろって?
'データのセットと確認メッセージ表示
Me![txt出身地] = str出身地 '非連結のテキストボックスにデータセット
MsgBox "作成した文字列は" & str出身地 & "です"
End Sub
ポイントは、
^^^^^^^^^^^^
'ループ処理
str出身地 = "" '空文字で初期化
まずは、バッファを空にする。
While rs.EOF = False 'いつものEOFが偽の間
'バッファの中に同じ出身地があるか場所をチェックする
n = InStr(str出身地, rs.Fields("出身地"))
作成している出身地の変数内に、読み込んだ出身地があるかチェックする。
InStrで見つからない場合、0が返ります、これを判断し、
If n = 0 Then '出身地が見つからなかったら(重複してない時)
'出身地と" "スペース1つを+する
str出身地 = str出身地 & rs.Fields("出身地") & " "
End If
出身地が見つからなかった場合のみ、出身地を+してます。
rs.MoveNext '次のレコードに移動しないと、とんでもないことに(笑)
Wend
↑テスト結果です。
さてと、完成したし、いっかな。
なんて工夫がないと、
ヤッパ三流プログラマーってSQL文知らないんだぁ・・・
と、一言メッセージをモラッテシマウノデ、SQL文で重複をハジク。
Group Byでグループ化するんダロ?簡単ジャンと思った読者の声を聞きつつ、
ギクっ読まれてるよ行動が。
ホカナイカナァ・・・
DISTINCTキーワード知らないんだぁ?
なにそのキーワード?
騙されたと思って、
Select DISTINCT 〜で、SQL文作ってみなよ
InStrを使う前のプログラムにDISTINCTキーワードを追加してみます。
'T_メンバーテーブルから出身地をバンドIDがフォームの値と一緒
strSQL = "Select DISTINCT 出身地 From T_メンバー " _
& " Where バンドID = " & Me![バンドID]
と、Selectの後にキーワードを+してみます。
Private Sub コマンド14_Click()
Dim str出身地 As String '出身地の管理
Dim rs As New ADODB.Recordset 'ADOのレコードセット
Dim strSQL As String 'SQL文を作成するため
'T_メンバーテーブルから出身地をバンドIDがフォームの値と一緒
strSQL = "Select DISTINCT 出身地 From T_メンバー " _
& " Where バンドID = " & Me![バンドID]
'レコードセットを開く
rs.Open strSQL, CurrentProject.Connection, _
adOpenKeyset, adLockOptimistic
'ループ処理
str出身地 = "" '空文字で初期化
While rs.EOF = False 'いつものEOFが偽の間
'出身地と" "スペース1つを+する
str出身地 = str出身地 & rs.Fields("出身地") & " "
rs.MoveNext '次のレコードに移動しないと、とんでもないことに(笑)
Wend
rs.Close '開いたら閉じろ、ドアを開けたら閉めるってしつけられたでしょ(笑)
Set rs = Nothing '変数も後始末しますか。使った器はキレイにしろって?
'データのセットと確認メッセージ表示
Me![txt出身地] = str出身地 '非連結のテキストボックスにデータセット
MsgBox "作成した文字列は" & str出身地 & "です"
End Sub
テストすると、オッ、できましたね。
SQLの結果で重複をハジクタメだけにグループ化してたけど、
物によっては、
Select DISTINCT
と、重複結果を取り除くキーワードを使ってみるのも面白そうですね。
手前味噌解説に酔ってないで、今、ボタンクリックしないと、
txt出身地にデータセットされないよ。
普通はグループが変わったタイミングで出身地も自動で変えたいよ。
そうでした。どちらの処理でもいいので、
グループが変わったタイミング、
親のフォームのレコード移動時に出身地の取得モジュールを書き込みます。
よし、動作したよと安心したら、
新規のデータを入力しようと、レコードを移動したら、
あらら、エラーだよ。
↑エラーメッセージ
strSQL = "Select DISTINCT 出身地 From T_メンバー " _
& " Where バンドID = " & Me![バンドID]
と、Me![バンドID]を参照しようとするが、
新規のデータなので、番号が無かったみたいです。
チェックを入れないとダメなのかぁ。
なんか、無いかなぁ・・新規レコードを判断するプロパティ。
探すと、そのまんまの、.NewRecordってプロパティがあった(笑)
'新規のデータ時、下の処理を走らせない
If Me.NewRecord = True Then '.NewRecordで新規かチッェクする
Me![txt出身地] = "" '空文字でクリア
Exit Sub '関数を途中で抜ける
End If
とチェックを入れて、新規データ追加時は、
出身地のデータはチェックしないことにしました。
下記、作成したレコード移動時のイベントです。
Private Sub Form_Current()
Dim str出身地 As String '出身地の管理
Dim rs As New ADODB.Recordset 'ADOのレコードセット
Dim strSQL As String 'SQL文を作成するため
'新規のデータ時、下の処理を走らせない
If Me.NewRecord = True Then '.NewRecordで新規かチッェクする
Me![txt出身地] = "" '空文字でクリア
Exit Sub '関数を途中で抜ける
End If
'T_メンバーテーブルから出身地をバンドIDがフォームの値と一緒
'DISTINCTキーワードで重複をハジク
strSQL = "Select DISTINCT 出身地 From T_メンバー " _
& " Where バンドID = " & Me![バンドID]
'レコードセットを開く
rs.Open strSQL, CurrentProject.Connection, _
adOpenKeyset, adLockOptimistic
'ループ処理
str出身地 = "" '空文字で初期化
While rs.EOF = False 'いつものEOFが偽の間
'出身地と" "スペース1つを+する
str出身地 = str出身地 & rs.Fields("出身地") & " "
rs.MoveNext '次のレコードに移動しないと、とんでもないことに(笑)
Wend
rs.Close '開いたら閉じろ、ドアを開けたら閉めるってしつけられたでしょ(笑)
Set rs = Nothing '変数も後始末しますか。使った器はキレイにしろって?
'データのセット
Me![txt出身地] = str出身地 '非連結のテキストボックスにデータセット
End Sub
Private Sub コマンド19_Click()
Dim str検索されたID As String
'メンバー名を条件にバンドIDをT_メンバーテーブルから検索する
str検索されたID = "" & DLookup("バンドID", "T_メンバー", "名前 = '布袋'")
'テスト表示
MsgBox str検索されたID
End Sub
上記、固定の条件ですが、ID番号を検索できました。
これを元にして、フォームの値を使用してみます。
Private Sub コマンド19_Click()
Dim str検索条件 As String
Dim str検索されたID As String
'フォームの値を元に検索条件を作成する
str検索条件 = "名前 = '" & Me!txt検索 & "'"
'str検索条件を条件にバンドIDをT_メンバーテーブルから検索する
str検索されたID = "" & DLookup("バンドID", "T_メンバー", str検索条件)
'結果のテスト表示
If str検索されたID = "" Then '検索できなかった?
MsgBox Me!txt検索 & "は、見つかりませんでした"
Else
MsgBox "検索されたのは" & str検索されたID & "です"
End If
End Sub
Private Sub コマンド20_Click()
On Error GoTo Err_コマンド20_Click
Screen.PreviousControl.SetFocus
DoCmd.FindNext
Exit_コマンド20_Click:
Exit Sub
Err_コマンド20_Click:
MsgBox Err.Description
Resume Exit_コマンド20_Click
End Sub
Private Sub btn検索_Click()
Dim str検索条件 As String
Dim str検索されたID As String
'フォームの値を元に検索条件を作成する
str検索条件 = "名前 = '" & Me!txt検索 & "'"
'str検索条件を条件にバンドIDをT_メンバーテーブルから検索する
str検索されたID = "" & DLookup("バンドID", "T_メンバー", str検索条件)
'結果のテスト表示
If str検索されたID = "" Then '検索できなかった?
MsgBox Me!txt検索 & "は、見つかりませんでした"
Else
'データを移動させる
DoCmd.GoToControl "バンドID" 'コントロールをIDへ移動
DoCmd.FindRecord str検索されたID 'データを検索する
End If
End Sub
Sub ie_get_itext() 'VBA052で解説
Dim objIE As Object 'IEオブジェクト参照用
'インターネットエクスプローラーのオブジェクトを作る
Set objIE = CreateObject("InternetExplorer.application")
objIE.Visible = True '見えるようにする(お約束)
'ランキングのページに飛ぶ
objIE.Navigate "http://www.ken3.org/cgi-bin/lime/limemgr.cgi"
'表示されるまで待つ、10秒後にエラーを判断する
Dim time10 As Date '時刻格納用
time10 = DateAdd("s", 10, Now()) '現在から10秒後を計算
Do While objIE.Busy = True 'ビジー、読み込み中の間
DoEvents
If time10 < Now() Then '10秒経過したか?
MsgBox "タイムアウトです"
Exit Sub
End If
Loop
'innerTextを取出す
Dim strTEXT As String
strTEXT = objIE.document.body.innerText '変数に代入
Debug.Print strTEXT 'イミディエイトにも表示
'UserFormを開く(確認用)
frmINFO.txtINFO.Value = strTEXT 'HTMLを代入
frmINFO.Show 'フォームを開く
End Sub
ポイントは、
~~~~~~~~~~~
Set objIE = CreateObject("InternetExplorer.application")
objIE.Visible = True '見えるようにする(お約束)
を作って、
'表示されるまで待つ、10秒後にエラーを判断する
Dim time10 As Date '時刻格納用
time10 = DateAdd("s", 10, Now()) '現在から10秒後を計算
Do While objIE.Busy = True 'ビジー、読み込み中の間
DoEvents
If time10 < Now() Then '10秒経過したか?
MsgBox "タイムアウトです"
Exit Sub
End If
Loop
と
表示されるまで、じっと、.Busyをストーカーのように観察して
彼女が読み込み待ちじゃなくなったら(笑)or10秒であきらめ(笑)
つかさず、声をかけ(オイオイ)
strTEXT = objIE.document.body.innerText '変数に代入
で、変数に表示結果を代入してます。
これは、プログラムは上から下への基本通りなんで、
プロパティなど、難しいけど、理解すれば簡単です。
流れは、
・IEのオープン --------- CreateObject("InternetExplorer.application")
・IEで指定ページを開く - objIE.Navigate "URLのとび先を指定"
・読み込み待ち ---------- objIE.Busy = True
・データを代入 ---------- strTEXT = objIE.document.body.innerText
と上から下にチェックしながら流れます。
今回の処理は、たぶん、
IE(HTML)のフォームで条件を入力、検索を行う、
検索結果をAccessのテーブルに代入する。
そんな、流れだと思います。
問題ないじゃん?なにか?問題あるの?
条件を入力して、終了したタイミングが取れないんですよ。
条件入力後、SUBMIT後、データを受け取ったら・・・がよくわからない。
※IEで入力して、結果が得られる、それを横取りしたいので。
Private Sub StartIE
Set IE = CreateObject("InternetExplorer.Application.1")
IE.Visible = True
End Sub
You have to create the Internet Explorer object with the WithEvents
keyword, which lets you hook its events in your code. Next,
you can hook this object's NewWindow2 event:
Private Sub IE_NewWindow2(ppDisp As Object, Cancel As Boolean)
Set IEx = CreateObject("InternetExplorer.Application.1")
Set ppDisp = IEx
IEx.Visible = False
End Sub
Private Sub IEx_BeforeNavigate2(ByVal pDisp As Object, URL As Variant, _
Flags As Variant, TargetFrameName As Variant, PostData As Variant, _
Headers As Variant, Cancel As Boolean)
' Figure out whether the URL requested is on the banned list
' If it is, never show the window or navigate to the site.
' Otherwise, let it through normally.
End Sub
なんだこれ?の
Dim WithEvents IE As InternetExplorer
を探る、
http://www.microsoft.com/japan/msdn/library/ja/vblr7/html/vakeyWithEvents.asp
WithEvents を 見るが、イマイチ。。。
宣言されるオブジェクト変数がイベントを発生させることのできる
クラス インスタンスを参照することを示すキーワードです。
へっ?なに?まぁ、わかんないから書いてみよう。
さてと、どうしましょうかねぇ。
テストで新しいウインドウを開くイベントを横取りしてみます。
'忘れずに、MicroSoft Internet Controls を参照設定してください
Dim WithEvents IE As InternetExplorer
Private Sub コマンド1_Click()
'普通にIEのオブジェクトを作成する
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True '可視をON
IE.GoHome '初期ページに移動する
End Sub
'こんなんで、横取りできるの?
Private Sub IE_NewWindow2(ppDisp As Object, Cancel As Boolean)
MsgBox "新しいウインドウを開こうとしてます"
End Sub
で、無事、右ボタンを押して新規ウインドウを起動すると、
新しいウインドウを開こうとしてます
とメッセージが表示されました。
Dim WithEvents IE As InternetExplorer
と
WithEventsキーワードを使って、
IE_NewWindow2(ppDisp As Object, Cancel As Boolean)
と書くだけでOKです。
おっと、これは、
Set IE = CreateObject("InternetExplorer.Application")
と、IEのアプリを外側に起動した場合です。
挿入・ActiveXコントロールで
Microsoft Web Browser コントロールをフォームに貼った場合は、
もっと簡単で、
フォームのロードイベントで表示して、
Private Sub Form_Load()
'フォームのロードイベントでGoHomeしてみる
Me![WebBrowser0].GoHome '初期ページへ移動
End Sub
Private Sub WebBrowser0_NewWindow2(ppDisp As Object, Cancel As Boolean)
MsgBox "新しいウインドウを開こうとしてます"
End Sub
で、同じ処理できました。
新規ウインドウを開いたテスト結果↑
WithEventsのキーワード無くて、
~~~~~~~~~~
WebBrowser0_NewWindow2(ppDisp As Object, Cancel As Boolean)
で動いたのは、意外だったけど。
あっ、当たり前なのかも?
だって、ボタンだったら、
ボタンのコントロール名_Click
で、クリック処理ですよね。
フォームに貼ったMicrosoft Web Browser コントロールのイベントだからか。
ってことは、他のイベントも選択可能かも。
VBAの編集画面で選択してみる。
↑コントロールの選択
↑イベントの選択
/*
* 4.読み込み完了のイベントを横取りしてみる
*/
話を戻してと、
やりたいのは、IE上で検索したデータ結果を取り出したい?
なので、IEで検索まではユーザーに勝手に操作してもらう。
ユーザーが検索すると、画面の更新(データの読み込み)が発生して、
その後、読み込みが完了する。
読み込み完了、そんなイベントを探ってみます。
DocumentCompleteってイベントがあるらしい。
チッまた英語かよ・・なんか疲れた(笑)
http://msdn.microsoft.com/workshop/browser/webbrowser/reference/events/documentcomplete.asp
Syntax
Private Sub object_DocumentComplete( _
ByVal pDisp As Object, _
ByVal URL As Variant)
Parameters
object
Object expression that resolves to the objects in the Applies To list.
pDisp
Object that specifies the top-level or frame WebBrowser object
corresponding to the event.
URL
String that specifies the URL, Universal Naming Convention (UNC) file name,
or pointer to an item identifier list (PIDL) of the loaded document
まぁ、読めないものはショウガナイ、まずはテストしてみます。
_DocumentComplete
のイベントが発生したら、.TitleとBodyの中身をテキストボックスに代入してみます。
Private Sub WebBrowser0_DocumentComplete(ByVal pDisp As Object, URL As Variant)
'ダウンロード終了時に発生すると仮定して
'タイトルの代入
Me![txtTITLE] = Me.WebBrowser0.Document.Title
'HTMLの代入
Me![txtHTML] = Me.WebBrowser0.Document.body.innerhtml
End Sub
範囲指定した部分だけマクロの実行。
一番簡単なのは、プログラムの中でタイミングを取れると楽ですよね。
タイミングを取る?
イベント起動型のプログラムとはいっても、
~~~~~~~~~~~~~~~~~~~~~~~~~~
個々のプログラムは上から下へ、
分岐したり、ループしたり、途中で抜ける、エラー処理に飛ぶ、
と
プログラムを組んでます。
簡単なのは、範囲指定をプログラム中に組み込んでしまう方法。
No.102 Excel 範囲を選択して、カンマ区切りのファイルを作る
http://www.ken3.org/backno/backno_vba21.html#102
で、作りかけなんだけど(最後まで出さないと体に悪いのに・・・?)
プログラムの頭で(頭のタイミングで)、
Dim objTARGET As Range '選択されたセルの集合
Set objTARGET = Application.InputBox(prompt:="セルを選択", Type:=8)
と、
Application.InputBox(prompt:="セルを選択", Type:=8)
を使用して、セル範囲を選択させる。
で、
選択された範囲を
'テーブルデータを作成する
Call MAKE_CSV_FILE(strFNAME, objTARGET)
と、関数へ渡す。
For y = 1 To objHANI.Rows.Count '行のループ
For x = 1 To objHANI.Columns.Count '列のループ
で、ループを作り、データの書き出しを行っています。
処理の流れは簡単で、
1.プログラム Sub Mainが起動される
2.Application.InputBoxでセルを選択させる
3.選択範囲に対して、処理を行う
と、ワンセットになってます。
Sub Main()
'Application.InputBoxでセルを選択させる
Dim objTARGET As Range '選択されたセルの集合
Set objTARGET = Application.InputBox(prompt:="セルを選択", Type:=8)
If IsEmpty(objTARGET) Then 'キャンセルが押されたかチェックする
MsgBox "キャンセルが押されました"
Exit Sub
End If
'ファイル名を作成 ファイル名は自分のパス+\test.html
Dim strFNAME As String 'ファイル名保存用
strFNAME = ThisWorkbook.Path & "\test.csv" 'ファイル名を作る
'テーブルデータを作成する
Call MAKE_CSV_FILE(strFNAME, objTARGET)
'できたファイルをメモ帳で表示して確認する
Shell "notepad.exe " & strFNAME '手抜きでShellで起動
'終わりの挨拶
MsgBox strFNAME & "を作成しました"
End Sub
Sub MAKE_CSV_FILE(strFNAME As String, objHANI As Range)
'ファイルをオープンする
Dim FNO As Integer 'ファイル番号
FNO = FreeFile '空いてるファイル番号を取出す
Open strFNAME For Output As #FNO 'テキストファイルを新規作成
'行、列でループを作る
Dim y As Integer
Dim x As Integer
For y = 1 To objHANI.Rows.Count '行のループ
For x = 1 To objHANI.Columns.Count '列のループ
Print #FNO, objHANI.Cells(y, x).Value;
Print #FNO, ",";
Next x
Print #FNO, "" '改行のみ出力
Next y
'ファイルをクローズする
Close #FNO
End Sub
さてと、方針が見えたら、あとは、Selection.に対して処理を行います。
今日は、少し丁寧に作ってみます。
普段は、
If TypeName(Selection) <> "Range" Then
なんてチェック入れないけど(オイオイ)
TypeName(Selection)で選択されたオブジェクトのタイプをチェックします。
グラフオブジェクトを選択して、処理が走るのを防止してます。
※逆に、グラフを選択させて、処理を実行する、
そんな処理も作れるんだなぁと思いました。
で、Range型だったら、
For Each 〜 IN で、
For Each objRANGE In Selection.Cells
と、選択されたセルを取り出しながら処理してます。
洒落た例題思いつかなかったので、
100以下なら赤、その他は青にフォントのカラーを変えてみました。
Sub ボタン1_Click()
'選択範囲がRange型かチェックする
'グラフを選択されていたりするのを防止したかったため
If TypeName(Selection) <> "Range" Then
MsgBox "変なとこ触るなよ、セルを選択してよ"
Exit Sub 'Range以外は抜ける
End If
'テストで範囲の表示
MsgBox "選択範囲は、" & Selection.Address
'ループさせて処理を行う
Dim objRANGE As Range
For Each objRANGE In Selection.Cells
If objRANGE.Value < 100 Then '数値が100以下なら
objRANGE.Font.Color = RGB(&HFF, 0, 0) '赤にする
Else
objRANGE.Font.Color = RGB(0, 0, &HFF) '青にする
End If
Next
End Sub