Q&A-GUI編

Window の表示や入力に関する質問をまとめました。

フォームを常に手前に表示したい
Enter を押したら、次の項目に進みたい
コマンドボタンのキャプションを左詰にする
コモンダイアログを画面中央に表示する
NumLock キーを制御するには
Tabキーを判別するには
解像度に依存しない画面を作成するにはどうしたら良いでしょうか?
Esc キーや キャンセルボタンで長い処理を中断するには
テキストボックスをスムーズにスクロールさせるには
コマンドボタンで矢印キーを制御するには?
GIFファイルのアニメーションを動かすには?
リストビューコントロールで1行全体を選択させるには?
キー入力をクリアするには
コモンダイアログで [キャンセル] ボタンが押されたか判別するには
PopupMenu の外で右ボタンを押しても PopupMenu が閉じないのですが?
MDIフォームの最大化・最小化ボタンを表示させない方法は?
VB5.0 で実行時にコントロールを追加するには?
マウスカーソルがフォームやコントロールから離れた時を知るには?
コモンダイアログのボタンのキャプションを変更するには?


Q フォームを常に手前に表示したい

Office のアシスタントのように、フォームのウィンドウを全てのアプリケーションの手前に表示するにはどうするのですか?

A APIの SetWindowPos を使用します

まず、標準モジュールに以下の SetWindowPos の定数と宣言を入れます。

Public Const HWND_TOP = 0
Public Const HWND_BOTTOM = 1
Public Const HWND_TOPMOST = (-1)
Public Const HWND_NOTOPMOST = (-2)
Public Const SWP_NOSIZE = &H1&
Public Const SWP_NOMOVE = &H2&
Public Const SWP_NOZORDER = &H4&
Public Const SWP_NOREDRAW = &H8&
Public Const SWP_NOACTIVATE = &H10&
Public Const SWP_FRAMECHANGED = &H20&
Public Const SWP_SHOWWINDOW = &H40&
Public Const SWP_HIDEWINDOW = &H80&
Public Const SWP_NOCOPYBITS = &H100&
Public Const SWP_NOOWNERZORDER = &H200&
Public Const SWP_DRAWFRAME = SWP_FRAMECHANGED
Public Const SWP_NOREPOSITION = SWP_NOOWNERZORDER

Declare Function SetWindowPos Lib "user32" Alias "SetWindowPos" (  _
    ByVal hwnd As Long, _
    ByVal hWndInsertAfter As Long, _
    ByVal x As Long, ByVal y As Long, _
    ByVal cx As Long, ByVal cy As Long, _
    ByVal wFlags As Long) As Long

次にフォームを手前に表示するところで、以下のように、SetWindowPos を呼びます。

lngRet  = SetWindowPos( _
    Form1.hWnd, _
     HWND_TOPMOST, _
     0,  0, _
     0,  0, _
     SWP_SHOWWINDOW Or SWP_NOMOVE Or SWP_NOSIZE)

ここで、最初の引数 Form1.hWnd の Form1 は、手前に表示したいフォームのオブジェクト名です。 元に戻す場合は、2番目の HWND_TOPMOSTHWND_NOTOPMOST にして、呼び出します。
ちなみに、最後のパラメータを以下のようにすれば、ウィンドウをアクティブにしないで表示することができます。
SWP_NOACTIVATE Or SWP_SHOWWINDOW Or SWP_NOMOVE Or SWP_NOSIZE

(注意)
このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q Enter を押したら、次の項目に進みたい

複数のテキストボックスやコンボボックスが並んでいるアプリケーションで、Enter キーを押したら次の TabIndex の項目にフォーカスが移動するようにするにはどうしたら良いでしょうか?

A SendKeys でできますが、ちょっと注意してください

一部のサイトでは親フォームの KeyPreview True にして、KeyPress イベントに次のようなコードを記述するように紹介しています。

Private Sub Form_KeyPress(KeyAscii As Integer)
   If KeyAscii = vbKeyReturn Then
        SendKeys "{Tab}"
        KeyAscii = 0
    End If
End Sub

ただし、この方法では幾つかのトラブルが報告されています(Visual Basic5.0での報告です)。代表的なものとして

・IMEのモードが正しく切り替わらない。
・Num Lock が ON、OFF する。

このトラブルを回避する為に、以下のようなAPIを使用することをお勧めします。

'標準モジュールに以下の定数と宣言を入れ
Public Declare Function PostMessage Lib "user32" _
    Alias "PostMessageA" _
    (
ByVal hwnd As Long, _
    ByVal wMsg As Long, _
    ByVal wParam As Long, _
    ByVal lParam As Long) As Long

Public Const WM_KEYDOWN = &H100
Public Const WM_KEYUP = &H101
Public Const VK_TAB = &H9

'親フォームの KeyPreview True にして、
'KeyPress イベントに次のようなコードを記述
Private Sub Form_KeyPress(KeyAscii As Integer)
    If KeyAscii = vbKeyReturn Then
        PostMessage Me.hwnd, WM_KEYDOWN, VK_TAB, 1
        PostMessage Me.hwnd, WM_KEYUP, VK_TAB, 1
        KeyAscii = 0
    End If
End Sub

(注意) Enter キーによる項目の移動については、Microsoft では、SetFocus による移動を推奨しています。
(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q コマンドボタンのキャプションを左詰にする

通常、コマンドボタンのキャプションはセンタリングされていますが、このキャプションを右詰、左詰にすることはできるでしょうか?

A APIの SetWindowLong でできます。

プロパティでの変更はできませんが、APIの SetWindowLongGetWindowLong を組み合わせてできます。
まず、標準モジュールに以下の定義を行います。

Option Explicit

Public Const GWL_STYLE = (-16)
Public Const BS_LEFT = &H100&
Public Const BS_RIGHT = &H200&
Public Const BS_CENTER = &H300&

Declare Function GetWindowLong Lib "user32" _
Alias "GetWindowLongA" ( _
ByVal hwnd As Long, _
ByVal nIndex As Long) As Long

Declare Function SetWindowLong Lib "user32" _
Alias "SetWindowLongA" ( _
ByVal hwnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long

左詰を行いたい場合は、以下のコードを記述します。

Dim lngStyle As Long

lngStyle = GetWindowLong(cmdTest.hwnd, GWL_STYLE)
lngStyle = SetWindowLong(cmdTest.hwnd, GWL_STYLE, (lngStyle And Not BS_CENTER) Or BS_LEFT)

cmdTest.Refresh

ここで、cmdTest は左詰を行いたい、コマンドボタンです。また、最後に Refresh が必要です。
右詰を行いたい場合は、以下のコードを記述します。

Dim lngStyle As Long

lngStyle = GetWindowLong(cmdTest.hwnd, GWL_STYLE)
lngStyle = SetWindowLong(cmdTest.hwnd, GWL_STYLE, (lngStyle And Not BS_CENTER) Or BS_RIGHT)

cmdTest.Refresh

ここで、cmdTest は右詰を行いたい、コマンドボタンです。最後に Refresh が必要です。
同様に、元のセンタリングに戻したい場合、最後のパラメータを BS_CENTER にすれば、センタリングになります。

(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q コモンダイアログを画面中央に表示する

VB 標準のコントロールを使用してコモンダイアログを表示すると、画面の左上に表示されますが、画面の中央に表示するにはどうしたら良いでしょうか?

A VB のバージョンにより異なります

コモンダイアログを画面中央に表示するには、VB6.0 より コモンダイアログのオートメーションオブジェクトが添付されて、プロパティの設定でできるようになりましが、VB5.0 では、APIを使用しないと画面中央に表示できません。

■ VB5.0 の場合

APIを使用する場合、コモンダイアログは親となる Window を指定できます。親の Windows 指定したダイアログは、親のウィンドウの左上に表示されます。標準のコモンダイアログコントロールは、この親となるウィンドウがディスクトップなので、画面の左上に表示されてしまいます。
ここでは、親のウィンドウの左上に表示されることを利用して、コモンダイアログを画面中央に表示します。

まず、標準モジュールに以下の定義を行います。

Option Explicit

' API で使用する定数の宣言(VB 標準の定義と同じなので、
' VB 標準の定義を使用してもかまいません)
Public Const OFN_READONLY = &H1
Public Const OFN_OVERWRITEPROMPT = &H2
Public Const OFN_HIDEREADONLY = &H4
Public Const OFN_NOCHANGEDIR = &H8
Public Const OFN_SHOWHELP = &H10
Public Const OFN_NOVALIDATE = &H100
Public Const OFN_ALLOWMULTISELECT = &H200
Public Const OFN_EXTENSIONDIFFERENT = &H400
Public Const OFN_PATHMUSTEXIST = &H800
Public Const OFN_FILEMUSTEXIST = &H1000
Public Const OFN_CREATEPROMPT = &H2000
Public Const OFN_SHAREAWARE = &H4000
Public Const OFN_NOREADONLYRETURN = &H8000
Public Const OFN_NOTESTFILECREATE = &H10000

Type OPENFILENAME
    lStructSize As Long
    hwndOwner As Long
    hInstance As Long
    lpstrFilter As String
    lpstrCustomFilter As String
    nMaxCustFilter As Long
    nFilterIndex As Long
    lpstrFile As String
    nMaxFile As Long
    lpstrFileTitle As String
    nMaxFileTitle As Long
    lpstrInitialDir As String
    lpstrTitle As String
    flags As Long
    nFileOffset As Integer
    nFileExtension As Integer
    lpstrDefExt As String
    lCustData As Long
    lpfnHook As Long
    lpTemplateName As String
End Type

Declare Function GetOpenFileName Lib "comdlg32.dll" Alias "GetOpenFileNameA" ( _
    pOpenfilename As OPENFILENAME) As Long

ここで、実際にコモンダイアログを表示する、コードを記述するのですが、その前に、コモンダイアログを表示したい位置にダミーのフォームを作成しておきます。仮にこのフォームのオブジェクト名を frmDummy とします。

コモンダイアログを表示するタイミングで以下のコードを記述します。

Dim ofn As OPENFILENAME

With ofn
    .lStructSize = LenB(ofn)
    ' hwndOwner に ダミーのフォームhWnd を指定します。
    .hwndOwner = frmDummy.hWnd
    .lpstrFilter = "すべてのファイル(*.*)" & vbNullChar & "*.*" & vbNullChar & _
                            "テキストファイル(*.txt)" & vbNullChar & "*.txt" & vbNullChar & vbNullChar
    .nFilterIndex = 1
    .lpstrFile = Space(255)  & vbNullChar
    .nMaxFile = 256
    .lpstrInitialDir = "C:\"
    .lpstrTitle = "Win32 API ファイルを選択"
    .flags = OFN_READONLY
End With

' 親フォームを一時的に Enable = False
Me.Enabled = False

If GetOpenFileName(ofn) Then
  ' OKが押された場合
    Dim i As Integer

  ' ファイル名の後ろの Null をカットする
    i = InStr(ofn.lpstrFile, vbNullChar)
    Debug.Print Left$(ofn.lpstrFile, i - 1)
Else
    Debug.Print "Cancel"
End If

' 親フォームを  Enable = True に戻す
Me.Enabled = True

ここで、注意点が幾つかあります。

hwndOwner に ダミーのフォームhWnd を指定してください。

lpstrFilter には、フィルタ文字列を設定してますが、コモンダイアログコントロールのように "|" で区切る代わりに、vbNullChar で区切ってください。また、最後の文字は  vbNullChar & vbNullChar としてください。

GetOpenFileName で、コモンダイアログを表示する前に、親フォームの Enable を False としてください。これを行わないと、コモンダイアログ表示中に親フォームを操作できてしまいます。GetOpenFileName から戻ってきたら  Enable を True に戻してください。

取得したファイル名の最後は vbNullChar で終了しますので、以降の文字はカットしてください。

lpstrFile の最後は、vbNullChar で終了してください。

同様に、「フォントの設定」「色の設定」等でも、コモンダイアログを中央に表示することができます。

Update 1999/06/17 : API の GetOpenFileName を呼ぶ場合に、lpstrFile のバッファの最後に vbNullChar をセットするように修正。これを行わないと、NT4.0のバージョンによっては動作しません。
(MSの以下のサイトを参考 -- 文書番号: J048273 --  http://www.asia.microsoft.com/japan/support/kb/articles/j048/2/73.htm


■ VB6.0 の場合

最初に、この情報は Microsoft のサポート対象外の情報であるので注意してください。でも、サポート内の情報もサポートしてもらった覚えはありませんが(^^;

VB6.0 からは、ダイアログオートメーションオブジェクトというオブジェクトが使用できるようになりました。このオブジェクトを使用できるようにするには、以下の手順を行ってください。

1. Visual Studio 6.0 の Disk3 の \Common\Tools\Vb\UnSupprt\DlgObj から DLGOBJS.REG をシステムディレクトリにコピーしてください。(一般的に Win95 は C:\Windows\System、 WinNT は、C:\WinNT\System32)

  (注意)DLGOBJS.REGは、Visual Studio 6.0 を購入した人だけ再配布可能です。

2. エクスプローラでコピーした DLGOBJS.REG を、右クリックして[統合]を選択してください。

3. VBを起動して、コモンダイアログを組込むプロジェクトを読込んでください。

4. メニューの [プロジェクト]-[参照設定]を選択して、「参照設定ダイアログ」から「Microsoft Dialog Automation Object」をチェックにしてください。

あとは、以下のようなコーディングを記述してください。

Dim objChooseFile As New ChooseFile

With objChooseFile
    .hWnd = Me.hWnd
    .Filters.Add ("すべてのファイル (*.*):*.*")
    .Filters.Add ("テキスト ファイル (*.txt):*.txt")
    .Directory = "C:\"
    .Title = "DlgObj ファイルを選択"
    .HideReadOnly = True
    .Save = False
    .Center = True
End With

If objChooseFile.Show Then
    Debug.Print objChooseFile.filename
Else
    Debug.Print "Cancel"
End If

コーディングを見て分かるように、.Center = True だけで、ダイアログを画面の中央に表示する事ができます。
他のコモンダイアログにについて調べる場合、残念ながら ダイアログオートメーションオブジェクのHELPファイルはありませんが、プロパティ名はほぼ 標準のコモンダイアログコントロールと同じですので、オブジェクトブラウザで見れば容易に理解できるでしょう。

(参考)ソフトバンク発行「Inside Windows 1999年2月号」の「VBブレイクスルー」

(注意)この項目の内容は、Visual Basic5.0(SP3)Visual Basic6.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q NumLock キーを制御するには

Numlock キーの ON/OFF を制御するには、どうしたら良いでしょうか?

A GetKeyState と keybd_event のAPIでできます

Numlock キーの ON/OFF を制御と言っても、単純に ON/OFF を繰り返すだけでなく、「常に ON にしたいか、常に OFF にしたい」といった要望が多いようです。これを行うには、現在のキーの状態を調べる GetKeyState と  キーの状態を変更する keybd_event を組み合わせます。

まず、標準モジュールに以下の定義を行います。

Option Explicit

Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer

Declare Sub keybd_event Lib "user32" ( _
    ByVal bVk As Byte, _
    ByVal bScan As Byte, _
    ByVal dwFlags As Long, _
    ByVal dwExtraInfo As Long)

Public Const VK_NUMLOCK = &H90
Public Const VK_CAPITAL = &H14

Public Const KEYEVENTF_KEYUP = &H2

Numlock キーを ON にしたい場合、以下のコードを作成します。

If GetKeyState(VK_NUMLOCK) = 0 Then
    keybd_event VK_NUMLOCK, 0, 0, 0
    keybd_event VK_NUMLOCK, 0, KEYEVENTF_KEYUP, 0
End If

Numlock キーを OFF にしたい場合、以下のコードを作成します。

If GetKeyState(VK_NUMLOCK) <> 0 Then
    keybd_event VK_NUMLOCK, 0, 0, 0
    keybd_event VK_NUMLOCK, 0, KEYEVENTF_KEYUP, 0
End If

GetKeyState(VK_NUMLOCK) の戻り値をチェック方法を変更しているのが、わかると思います。
また、CapsLock の状態を変更する場合には、VK_NUMLOCK の代わりに、VK_CAPITAL を指定するだけで変更できます。

(注意)この項目の内容は、Visual Basic5.0(SP3)Visual Basic6.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q Tabキーを判別するには

フォームの KeyPress イベント等で、Tab キーが押されたかどうかを判別するには、どうしたら良いでしょうか?

A TabStop を全て False か GetKeyState のAPIでできます

デフォルトで Tab キーは、VBによって処理される為に、KeyPress イベント等では判別する事ができません。これを判別するには、以下のような方法があります。

■ TabStop を全て False にする場合

まず、フォーム上にある全ての TabStop を False にしてください。ボタンなどを含めフォーム上の TabStop が1つでも True だと、判別はできません。
あとは、各コントロールやフォームの KeyPress イベント で Tab キーが判別できます。
試しに、フォームオブジェクトに以下のコードを入力してみてください。

'フォームの KeyPreview True にして、
'KeyPress イベントに次のようなコードを記述

Private Sub Form_KeyPress(KeyAscii As Integer)
    If KeyAscii = vbKeyTab Then
       
Debug.Print "Tab"
        KeyAscii = 0
    End If
End Sub

上記のコードを実行すると、Tabキーを押すごとに、イミディエート・ウィンドウに "Tab"と表示されるはずです。

■ GetKeyState のAPIを使用する場合

まず、標準モジュールに以下の定義を行います。

Option Explicit

Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer

Public Const  VK_TAB = &H9

フォーム上の TabStop を False にしない場合は、 KeyPress イベント で Tab キーを判別することはできません。そこで、タイマーコントロールや各コントロールのイベント(LostFocus 等)が発生した時点で、以下のAPIを呼びます。

Dim intState As Integer

intState = GetKeyState(VK_TAB) And &HFE

ここで、GetKeyState の戻り値は、上位1ビットがキーが押されている場合には1、押されていない場合0となり、下位1ビットがキーが押されるごとにトグルします。従って And &HFE の演算で intState が 0 の場合は、Tabが押されてない状態、 <>0 の場合は押されている状態となります。
他のキーを判別する場合は、APIビューワにて、VK_ の定数を検索して GetKeyState に検索した定数を指定してください。

(注意)この項目の内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q 解像度に依存しない画面を作成するにはどうしたら良いでしょうか?

VBでアプリケーションを作成したら、インストールしたPCの解像度によって、画面の表示が異なります、どうしたら良いでしょうか?

A ScaleMode によって異なります

基本的に、Visual Basic ではコントロールのサイズや位置に、デフォルトで Twip という ScaleMode を使用しています。この単位は、画面解像度に依存しない実寸の大きさをあらわす単位で、1センチは567twip、1インチは1,440twip になります。この単位を使用した場合、 画面の解像度により、1ドットの画素の大きさが異なります。例えば、ある解像度で1ドットが15twip の場合や、12twip の場合があります。従って、同じ3000twip でも 200ドットの場合や250ドット場合がありまが、画面に表示される実寸は同じになります。
また、 ScaleMode には、Pixcel という単位もあります。この単位は、1ドットが1pixcel に対応しているので、解像度が異なってもドット数では同じ大きさとなりますが、実寸は異なります。

つまり解像度に依存しない為には、このどちらかの ScaleMode に統一する必要があります。(その他の ScaleMode でも同様に統一する必要があります)
それぞれの  ScaleMode で作成する場合には、幾つかの注意が必要です。

■ ScaleMode が Twip の場合
1. Twip で異なった解像度アプリケーションを作成する場合、全てのフォームやコントロールの ScaleMode を Twip に統一してください。

2. 解像度によって同じ Twip でも微妙に大きさが異なる場合があります。例えば、1ドットが15twip の場合、36twip も 30Twip も2ドットですが、1ドットが12twip の場合、36twip は3ドット、  30Twip は2ドットとなります。このため、大きさを揃えたいコントロールの幅や高さは、twip数を完全に同じにしてください。
(例えば、テキストとコンボボックスは、デフォルトで並べると同じ大きさに見えますが、Height のTwip数が異なる場合があります。)

3. フォントはシステムフォントを使用すると、解像度の指定で「大きいフォント」や「小さいフォント」を使用した場合に違った大きさになります。「MSゴシック」等を指定するとほぼ同じ大きさになりますが、やはり解像度により異なりますので、フォントを使用しているコントロールは、余裕を見てやや大きめに作成してください。

4. コントロールによっては、フォントに依存したサイズになる場合や、解像度によってフォームのサイズを変更したい場合は、以下の用なコーディングで解像度ごとに処理を分けてください。

Private Sub Form_Load()
    Select Case Screen.TwipsPerPixelX
        Case 12
            Form1.Width = 360
            Form1.Height = 240
       
        Case 15
            Form1.Width = 450
            Form1.Height = 300
    End Select
End Sub

Screen.TwipsPerPixelXScreen.TwipsPerPixelY は、解像度によって変更される1ドットあたりの Twip の幅と高さを返してくれます。

■ ScaleMode が Pixcel の場合

1. Pixcel で異なった解像度アプリケーションを作成する場合、全てのフォームやコントロールの ScaleMode を Pixcel に統一してください。

2. フォームに貼り付けられたコントロールは Pixcel 単位で幅や高さを指定できますが、フォームの場合は、Pixcel で指定することができないので、以下のようなコーディングでフォームの幅や高さのドット数を固定にしてください。

Private Sub Form_Load()
    ' 360X240ドットに固定
    Form1.Width = Screen.TwipsPerPixelX * 360
    Form1.Height = Screen.TwipsPerPixelY * 240
End Sub

3. Twip の場合と同様、フォントはシステムフォントを使用すると、解像度の指定で「大きいフォント」や「小さいフォント」を使用した場合に違った大きさになります。「MSゴシック」等を指定するとほぼ同じ大きさになりますが、やはり解像度により異なりますので、フォントを使用しているコントロールは、余裕を見てやや大きめに作成してください。

■ 共通の注意事項

● 画面の大きさは、一番低い解像度の画面サイズ(640X480)を想定すれば全ての画面サイズに入ります。

● カラーは Windows 標準の 16 色 (vbBlack、vbBlue、vbCyan など) を使用してください。

● ダイアログなどは、位置を固定せずに、以下のような関数を作成して、画面の中央や左上に表示してください。(VB5.0 からは、StartUpPosition プロパティで指定できる)

'フォームを画面の中央に表示する
Public Sub ShowFormToCenter(frmTarget As Form)
    frmTarget.Left = (Screen.Width - frmTarget.Width) / 2
    frmTarget.Top = (Screen.Height - frmTarget.Height) / 2
End Sub

● 複数の解像度に対応した市販のコントロールもあります。それらを利用するもの良いでしょう。


(注意)この項目の内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q Esc キーや キャンセルボタンで長い処理を中断するには

印刷や技術計算で、長い処理を行っているの時に ESCキーや キャンセルのボタンを押して、中断させるには?

A フラグと DoEvents で中断を判定します

まず、標準モジュールなどに、フラグを1つ宣言します。

Public m_bAbort As Boolean

次に、長い処理の中でフラグを判定して、True なら処理を終了するようにします。

Private Sub Start_Click()
    m_bAbort = False
    Do
        If m_bAbort = True Then Exit Sub
      :
        '処理を記述
      :
        DoEvents
   Loop
End Sub

ここで、ループ中に DoEvents を入れるのがこつです。
最後に、ESCキーや キャンセルのボタンが押された時に フラグを True にすれば完了です。

Private Sub Form_KeyPress(KeyAscii As Integer)
   If KeyAscii = vbKeyEscape Then
      m_bAbort = True
      KeyAscii = 0
   End If
End Sub

Private Sub Stop_Click()
   m_bAbort = True
End Sub


(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q テキストボックスをスムーズにスクロールさせるには

テキストボックスにログデータのようなデータを追加して、最後の行を表示させる為に SelStart を行っていますが、テキストボックスへの書きこみのたびにスクロールバーがちらついたりして、あまり見栄えがよくありません。スムーズにスクロールさせる方法はないでしょうか?

A API の SendMessage でスクロールさせます

スムーズにスクロールさせるには、SendMessage という API で EM_LINESCROLL を指定すれば、スムーズにスクロールできます。

まず、標準モジュールに以下の定義を行います。

Option Explicit

Const EM_LINESCROLL = &HB6
Declare Function SendMessage Lib "User32" Alias _
    "SendMessageA" _
    (ByVal hWnd As Long, _
    ByVal wMsg As Integer, _
    ByVal wParam As Integer, _
    ByVal lParam As Long) As Long


次に、スクロールさせたい時に以下の様に API を使用します。

Private Sub Command1_Click()
    Dim i As Integer

    For i = 0 To 10000
        Text1.Text = Text1.Text & Str$(i) & vbCrLf
        SendMessage Text1.hWnd, EM_LINESCROLL, 0, 1&
        DoEvents
    Next

End Sub

この時の Text1.hWnd は、スクロールさせたいテキストボックスの hWnd プロパティを指定してください。また、ループに DoEvents が必要な事も注意してください。

ちなみに、MSのサイトの以下の情報を参考にしました。(1999/4/27 のアドレスです、見つからない場合は、検索で文書番号の J043517 を検索してください)

http://www.asia.microsoft.com/japan/support/kb/articles/j043/5/17.htm

(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q コマンドボタンで矢印キーを制御するには?

コマンドボタンでは、矢印キーを押しても、KeyDown、KeyPress のイベントが発生しません。どうしたら矢印キーを判別できるでしょうか?

A LostFocus で API の GetKeyState で判別できます

コマンドボタンのイベントは発生しませんが、コマンドボタンから抜ける時にキーボードの状態を判別することができます。
まず、標準モジュールに以下の定義を行います。

Option Explicit

Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer

Public Const VK_LEFT = &H25
Public Const VK_UP = &H26
Public Const VK_RIGHT = &H27
Public Const VK_DOWN = &H28

コマンドボタンの LostFocus で以下のような方法で、キーを判別できます。

Dim intState As Integer

intState = GetKeyState(VK_LEFT) And &HFE

ここで、GetKeyState の戻り値は、上位1ビットがキーが押されている場合には1、押されていない場合0となり、下位1ビットがキーが押されるごとにトグルします。従って And &HFE の演算で intState が 0 の場合は LEFT が押されてない状態、 <>0 の場合は押されている状態となります。
サンプルとして、5つのボタンを作成して、それぞれ、cmdCenter、cmdLeft、cmdRight、cmdUp、cmdDown とします。cmdCenter で左、右、上、下の矢印キーが押された場合に、cmdLeft、cmdRight、cmdUp、cmdDown に移動させるには、次のようにします。

Private Sub cmdCenter_LostFocus()
    If GetKeyState(VK_LEFT) And &HFE Then
        cmdLeft.SetFocus
    ElseIf GetKeyState(VK_UP) And &HFE Then
        cmdUp.SetFocus
    ElseIf GetKeyState(VK_RIGHT) And &HFE Then
        cmdRight.SetFocus
    ElseIf GetKeyState(VK_DOWN) And &HFE Then
        cmdDown.SetFocus
    End If
End Sub

他のキーを判別する場合は、APIビューワにて、VK_ の定数を検索して GetKeyState に検索した定数を指定してください。

(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q GIFファイルのアニメーションを動かすには

アニメーションの入ったGIFファイルをピクチャーボックスに表示させても、アニメーションが動きません。アニメーションが動くようにするにはどうしたらよいでしょうか?

A WebBrowserコントロールを使います

WebBrowser コントロールは、GIFを再生させる機能を持っています。
まず、VBのメニューから [プロジェクト]-[コンポーネント]を選択して、[Microsoft Internet Control] をチェックすると、WebBrowser コントロールがツールボックスに表示されます。
あとは、WebBrowser コントロールをフォームに貼りつけて、Form_Load で以下のような命令を実行します。

WebBrowser1.Navigate "D:\Temp\Anime.Gif"

ここで、WebBrowser1は、WebBrowser コントロールのオブジェクト名、 "D:\Temp\Anime.Gif" は、動かしたいGIFのファイル名です。
スクロールバーがちょっと邪魔になりますが、フレームなどを貼りつけて隠せばOKです(^^

(注意)WebBrowserコントロールは Microsoft、GIFファイルの圧縮技術は、米国UNISYS がライセンスを取得しています。作成したコントロールを配布する場合は、ライセンス情報に気をつけてください。
(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q リストビューコントロールで1行全体を選択させるには?

通常、リストビューコントロールの項目を選択すると、先頭の項目しか選択されません。これを1行全体を選択させるには、どうしたらよいでしょうか?

A API の SendMessage を使用します

一行全体を選択させるには、まず、標準モジュールに以下の定義を行います。

Option Explicit

Public Declare Function
SendMessage Lib "user32" Alias "SendMessageA" ( _
    ByVal hWnd As Long, _
    ByVal wMsg As Long, _
    ByVal wParam As Long, _
    ByVal lParam As Any) As Long

Public Const LVM_FIRST = &H1000
Public Const LVM_SETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 54
Public Const LVM_GETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 55
Public Const LVS_EX_FULLROWSELECT = &H20

後は、リストビューを表示する前に、以下のプログラムを実行します。

Dim r As Long

r = SendMessage(ListView1.hWnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0&, ByVal 0&)
r = r Or LVS_EX_FULLROWSELECT
r = SendMessage(ListView1.hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0&, ByVal r)

ここで、ListView1 はリストビューのオブジェクト名です。
以降、リストビューの項目が選択されると、1行全体が選択されます。

(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q キー入力をクリアするには

キー入力の先行入力をクリアするにはどうしたら良いでしょうか?

A API の PeekMessage を使用します

キー入力の先行入力をクリアするには、Windows のキー入力メッセージをクリアすれば実現できます。
このメッセージを削除するには、 PeekMessage という API を使用します。
PeekMessage を使用するには、まず、以下の宣言を標準モジュールに入れます。

Type POINTAPI
    X As Long
    Y As Long
End Type

Type MSG
    hWnd As Long
    message As Long
    wParam As Long
    lParam As Long
    time As Long
    pt As POINTAPI
End Type

Public Const PM_REMOVE = &H1
Public Const PM_NOREMOVE = &H0
Public Const WM_KEYFIRST = &H100
Public Const WM_KEYLAST = &H108
Public Const WM_MOUSEFIRST = &H200
Public Const WM_MOUSELAST = &H20A

Declare Function PeekMessage Lib "user32" Alias "PeekMessageA" ( _
    lpMsg As MSG, _
    ByVal hWnd As Long, _
    ByVal wMsgFilterMin As Long, _
    ByVal wMsgFilterMax As Long, _
    ByVal wRemoveMsg As Long) As Long

これで、PeekMessage を使用する準備ができました。
後は、キー入力をクリアしたい所に、以下のコーディングを追加するだけです。

Dim m As MSG

Do While
PeekMessage(m, Me.hWnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)
Loop

ここで、Me.hWnd の Me はキー入力を行うフォームのオブジェクト名です。また、WM_KEYFIRST, WM_KEYLAST は、メッセージの範囲を指定しています。PM_REMOVE は取得したメッセージを削除します。
また、WM_KEYFIRST, WM_KEYLASTの代わりに、WM_MOUSEFIRSTWM_MOUSELAST を使用すれば、マウスでの入力を削除することもできます。
さらに、PM_REMOVE の代わりに PM_NOREMOVE を指定すれば、メッセージを削除せずに、入力があったかどうかを判別することもできます。

(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q コモンダイアログで [キャンセル] ボタンが押されたか判別するには

コモンダイアログで [キャンセル] ボタンが押された場合、ShowOpen の戻り値では判別できません。どうしたらよいでしょうか?

A プロパティの CancelError を True に設定してください。

まずは、コモンダイアログコントロールのプロパティ CancelErrorTrue に設定してください。これを True に設定すると、ShowOpen 等を実行して コモンダイアログで [キャンセル] ボタンが押されるとエラーが発生します。エラーの状態を Err.Number で判別するか On Error Goto で分岐すれば判別できます。
以下はサンプルです。

   On Error GoTo ErrCancel

    With CommonDialog1
        .Flags = cdlOFNFileMustExist Or cdlOFNHideReadOnly
        .DialogTitle = "ファイルを選択"
        .Filter = "TEXT ファイル(*.txt)|*.txt|全てのファイル(*.*)|*.*"
        .DefaultExt = ".txt"
        .ShowOpen

        MsgBox .filename & "が選択されました"
        Exit Sub
    End With


ErrCancel:
    MsgBox "キャンセル が押されました"
    Exit Sub

(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q PopupMenu の外で右ボタンを押しても PopupMenu が閉じないのですが?

PopupMenu メソッドでポップアップメニューを表示した後に、ポップアップメニューの範囲外を右ボタンで押しても、ポップアップメニューが閉じません。このままだと、右ボタンを使用して連続してポップアップメニューを表示することができません。どうすれば良いでしょうか?

A PopupMenu メソッドの flags に vbPopupMenuRightButton を指定します

flags  に vbPopupMenuRightButton を指定すると、ポップアップメニューの項目をマウスの左または右のボタンをクリックしたときに反応するようになります。また、ポップアップメニューの範囲外を右ボタンでクリックすると、ポップアップメニューが閉じます。

Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    PopupMenu mnuEdit, vbPopupMenuRightButton
End Sub


(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q MDIフォームの最大化・最小化ボタンを表示させない方法は?

MDIフォームの最大化・最小化ボタンを表示させないには、なにか良い方法がありますか?

A SetWindowLong という API でできます

コマンドボタンのキャプションを左詰にするでも紹介したように、プロパティに無い設定を行う場合に、この SetWindowLongGetWindowLong を使うことで実現できることがよくあります。
詳細に付いては、Microsoft の以下のサイトを参考にしてください。(1999/7/16 のアドレスです、見つからない場合は、検索で文書番号のJ042454 を検索してください)

http://www.asia.microsoft.com/japan/support/kb/articles/j042/4/54.htm


(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q VB5.0 で実行時にコントロールを追加するには?

VBの6.0では、Control.Add を使用して、実行時にフォームにコントロールを追加できますが、VB5.0で同様なことができるのでしょうか?

A コントロール配列を Load で追加することができます

VBの6.0では、Controls.Add を使用して、動的にコントロールを追加することができますが、VB5.0で同様なことを行いたい場合には、追加したいコントロールを予め配列にしておく必要があります。後は、Load ステートメントで要素を追加することができます。

Load Label1(2)
Label1(2).Caption = "Label2"
Label1(2).Left = 100
Label1(2).Top = 100
Label1(2).Visible = True

また、VBの6.0の Controls.Add の使い方は、MSDN等で検索してみてください。Dr. GUI が教えてくれますよ(^^

(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q マウスカーソルがフォームやコントロールから離れた時を知るには?

マウスカーソルが、フォームやコントロールから離れたのを知るにはどうしたらよいのでしょうか?

A SetCapture ReleaseCapture で知ることができます

通常、マウスの動きは MouseMove イベントで知ることができますが、マウスが離れるとイベントが発生しなくなり、マウスの位置を知ることができません。そこで、APIの SetCapture を使用すると、マウスカーソルがフォームやコントロールを離れた場合も、イベントが取得できるようになります。ただし、マウスカーソルが離れても他のコントロールにイベントが発生しなくなります。そこで、必要な処理が終わったならば、ReleaseCapture というAPIを実行して、マウスカーソルを開放します。
以下は、フォーム上にマウスカーソルが移動した場合に、フォームの背景を黒に設定し、マウスカーソルが離れた場合に、元の色に戻すサンプルです。

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If 0 <= X And X <= Me.ScaleWidth And 0 <= Y And Y <= Me.ScaleHeight Then
        Me.BackColor = vbBlack
        SetCapture Me.hwnd
    Else
        Me.BackColor = vbButtonFace
        ReleaseCapture
   End If
End Sub


以下は、宣言部です。標準モジュールに入れてください。

Option Explicit

Declare Function
SetCapture Lib "user32" (ByVal hwnd As Long) As Long
Declare Function ReleaseCapture Lib "user32" () As Long

(注意)このページの内容は、Visual Basic5.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。


Q コモンダイアログのボタンのキャプションを変更するには?

コモンダイアログの[OK]ボタンや[キャンセル]ボタンのキャプションを「開く」や「取消」といった文字にするにはどうしたら良いでしょうか?

A API を使用します。

コモンダイアログのキャプションの文字を変更するには、APIのフック関数を使用します。
早速サンプルを見てみましょう!

まず、標準モジュールに以下の定義を行います。

Option Explicit

' API で使用する定数の宣言(VB 標準の定義と同じなので、
' VB 標準の定義を使用してもかまいません)
Public Const OFN_READONLY = &H1
Public Const OFN_OVERWRITEPROMPT = &H2
Public Const OFN_HIDEREADONLY = &H4
Public Const OFN_NOCHANGEDIR = &H8
Public Const OFN_SHOWHELP = &H10
Public Const OFN_NOVALIDATE = &H100
Public Const OFN_ALLOWMULTISELECT = &H200
Public Const OFN_EXTENSIONDIFFERENT = &H400
Public Const OFN_PATHMUSTEXIST = &H800
Public Const OFN_FILEMUSTEXIST = &H1000
Public Const OFN_CREATEPROMPT = &H2000
Public Const OFN_SHAREAWARE = &H4000
Public Const OFN_NOREADONLYRETURN = &H8000
Public Const OFN_NOTESTFILECREATE = &H10000
Public Const OFN_ENABLEHOOK = &H20


Public Const
WM_INITDIALOG = &H110

Public Const
IDOK = 1
Public Const
IDCANCEL = 2

Type
OPENFILENAME
    lStructSize As Long
    hwndOwner As Long
    hInstance As Long
    lpstrFilter As String
    lpstrCustomFilter As String
    nMaxCustFilter As Long
    nFilterIndex As Long
    lpstrFile As String
    nMaxFile As Long
    lpstrFileTitle As String
    nMaxFileTitle As Long
    lpstrInitialDir As String
    lpstrTitle As String
    flags As Long
    nFileOffset As Integer
    nFileExtension As Integer
    lpstrDefExt As String
    lCustData As Long
    lpfnHook As Long
    lpTemplateName As String
End Type

Declare Function GetOpenFileName Lib "comdlg32.dll" Alias "GetOpenFileNameA" ( _
    pOpenfilename As OPENFILENAME) As Long

Declare Function
SetWindowText Lib "user32" Alias "SetWindowTextA" ( _
    ByVal hWnd As Long, _
    ByVal lpString As String _
) As Long

Declare Function
GetDlgItem Lib "user32" ( _
    ByVal hDlg As Long, _
    ByVal nIDDlgItem As Long _
) As Long

' フック関数
Public Function
fnHook(ByVal hWnd As Long, _
    ByVal uMsg As Long, _
    ByVal wParam As Long, _
    ByVal lParam As Long) As Long

     If uMsg = WM_INITDIALOG Then
     
' OKボタンを変更
         SetWindowText GetDlgItem(hWnd, IDOK), "読込み"
      ' キャンセルボタンを変更
         SetWindowText GetDlgItem(hWnd, IDCANCEL), "取消"
     End If

    fnHook = 0
End Function

Public Function
GetAddress(ByVal lngAddress As Long) As Long
    GetAddress = lngAddress
End Function

コモンダイアログを表示するタイミングで以下のコードを記述します。

Dim ofn As OPENFILENAME

With ofn
    .lStructSize = LenB(ofn)
    .hwndOwner = Me.hWnd
    .lpstrFilter = "すべてのファイル(*.*)" & vbNullChar & "*.*" & vbNullChar & _
                            "テキストファイル(*.txt)" & vbNullChar & "*.txt" & vbNullChar & vbNullChar
    .nFilterIndex = 1
    .lpstrFile = Space(255)  & vbNullChar
    .nMaxFile = 256
    .lpstrInitialDir = "C:\"
    .lpstrTitle = "Win32 API ファイルを選択"
    ' フック関数のアドレスを設定しています。
    .lpfnHook = GetAddress(AddressOf fnHook)
    .flags = OFN_READONLY Or OFN_ENABLEHOOK
End With

If GetOpenFileName(ofn) Then
     ' OKが押された場合
    Dim i As Integer

     ' ファイル名の後ろの Null をカットする
    i = InStr(ofn.lpstrFile, vbNullChar)
    Debug.Print Left$(ofn.lpstrFile, i - 1)
Else
    Debug.Print "Cancel"
End If

ここで、注意点が幾つかあります。

標準モジュールの fnHook という関数で、キャプションを変更しています。関数内のSetWindowText というAPIの"読込み"と"取消"が変更する文字です。

lpstrFilter には、フィルタ文字列を設定してますが、コモンダイアログコントロールのように "|" で区切る代わりに、vbNullChar で区切ってください。また、最後の文字は  vbNullChar & vbNullChar としてください。

取得したファイル名の最後は vbNullChar で終了しますので、以降の文字はカットしてください。

lpstrFile の最後は、vbNullChar で終了してください。

(注意)このページの内容は、Visual Basic6.0(SP3) を対象に記述されています。他のバージョンでは、対応できないこともあるので、ご注意願います。
(注意) ここでの情報については、あくまでも各自の責任にて、充分にテストを行ってご使用ください。内容に関する質問については、回答できる保証がありませんので、予めご了承願います。