[[20250925080408]] 『複数モニターでユーザーフォームの表示位置を変更』(栗栄太) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) |

| 全文検索 | 過去ログ ]

 

『複数モニターでユーザーフォームの表示位置を変更する方法』(栗栄太)

現在左右にモニターを設置し、
2画面で作業をしています。

ユーザーフォームで UserForm1.StartUpPosition = 2 にして
右側のモニターでエクセルファイルを開くと
ユーザーフォームが左側に表示されます。
どうすればよいのでしょうか?

考えたのは
開いた後のマクロでマウスの位置を取得して
スクリーンの位置(左上の位置、幅、高さ)がわかれば
ユーザーフォームの表示位置を決定できるのですが
マウスの位置や、スクリーンの表示位置とかを
調べる方法はありますか?
また、他に良い方法があれば教えてください。

< 使用 Excel:Microsoft365、使用 OS:Windows11 >


 むしろ、右側のモニターでエクセルファイルを使うけどユーザーフォームは左側に表示させたかったから
 わざと StartUpPosition = 2 に設定したのではなかったのか... 
                                                              と思ってしまいました。^^;

(Office VBA)【決定版】マルチディスプレイ環境でユーザーフォームを親ウィンドウの中央に表示する・前編 - 日本語入力ソフトとVBAの覚え書き
https://dz11.hatenadiary.jp/entry/2019/05/08/085957
(Office VBA)【決定版】マルチディスプレイ環境でユーザーフォームを親ウィンドウの中央に表示する・後編 - 日本語入力ソフトとVBAの覚え書き
https://dz11.hatenadiary.jp/entry/2019/05/17/090258

 あと、マウスの位置についてはGetCursorPosってAPIを使う様です。

【VBA×WindowsAPI】GetCursorPos関数の使い方 | LiCLOG
https://liclog.net/getcursorpos-function-vba-windows-api/

 ピクセル単位なのでポイント単位に変換しないといけませんが、
 まぁとりあえず×0.75しておけばいいんじゃないかと思います。

(白茶) 2025/09/25(木) 09:45:36


すみません。
表示したいのは親ウィンドウではなく
スクリーンの中央なのです。

ファイル一覧を表示している窓(名称がわからない)?で
ファイルをダブルクリックして開く場合に
クリックしたのが右のディスプレイなら右に表示してほしいし
左のディスプレイなら左に出てほしいのです。

(栗栄太) 2025/09/25(木) 09:59:52


 SystemParametersInfoってAPIを使えばプライマリモニタのワークエリア矩形が取れる様です。

タスクバーを考慮してフォームを中央に寄せる
http://www.alato.ne.jp/kazu-/vb/tip14.htm

 あとGetSystemMetricsってAPIも何か色々情報が取れるっぽいですねぇ。(使った事ありませんが..)

API 関数解説
https://www.tokovalue.jp/function/GetSystemMetrics.htm

(白茶) 2025/09/25(木) 10:14:23


 ちょっと実験してみました。何かのヒントにでもなれば...

    Option Explicit
    'Private Type POINTAPI
    '    x As Long
    '    y As Long
    'End Type
    Private Type RECT
        Left   As Long
        Top    As Long
        Right  As Long
        Bottom As Long
    End Type
    Private Type MONITORINFO
        cbSize    As Long
        rcMonitor As RECT
        rcWork    As RECT
        dwFlags   As Long
    End Type
    Rem ディスプレイモニタに関する情報を取得する。
    Private Declare PtrSafe Function GetMonitorInfo Lib "user32" Alias "GetMonitorInfoA" (ByVal hMonitor As LongPtr, ByRef lpmi As MONITORINFO) As Long
    Private Const MONITOR_DEFAULTTONULL = &H0
    Private Const MONITOR_DEFAULTTOPRIMARY = &H1
    Private Const MONITOR_DEFAULTTONEAREST = &H2
    'Rem 指定された点を含むディスプレイモニタのハンドルを返す。
    'Private Declare PtrSafe Function MonitorFromPoint Lib "user32" (ByVal x As Long, ByVal y As Long, ByVal dwFlags As Long) As LongPtr
    Rem 指定したウィンドウの境界線により決まる長方形領域との交差部分が最も広いディスプレイモニタのハンドルを返す。
    Private Declare PtrSafe Function MonitorFromWindow Lib "user32" (ByVal hwnd As LongPtr, ByVal dwFlags As Long) As LongPtr
    'Rem マウスカーソル(マウスポインタ)の現在の位置に相当するスクリーン座標
    'Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
    Rem 呼び出し側のスレッドに関連付けられているウィンドウの中から、キーボードフォーカスを持つウィンドウのハンドルを取得
    Private Declare PtrSafe Function GetFocus Lib "user32" () As LongPtr

    'Private Sub UserForm_Click()
    '    Dim p As POINTAPI, hMon As LongPtr, mi As MONITORINFO
    '    Call GetCursorPos(p)
    '    hMon = MonitorFromPoint(p.x, p.y, MONITOR_DEFAULTTONEAREST)
    '    If hMon Then
    '        mi.cbSize = LenB(mi)
    '        If GetMonitorInfo(hMon, mi) Then
    '            Debug.Print "MonitorFromPoint: "; hMon
    '            Debug.Print , mi.rcMonitor.Top; mi.rcMonitor.Bottom, mi.rcMonitor.Left; mi.rcMonitor.Right
    '            Debug.Print , mi.rcWork.Top; mi.rcWork.Bottom, mi.rcWork.Left; mi.rcWork.Right
    '        End If
    '    End If
    '    hMon = 0
    '    hMon = MonitorFromWindow(GetFocus, MONITOR_DEFAULTTONEAREST)
    '    If hMon Then
    '        mi.cbSize = LenB(mi)
    '        If GetMonitorInfo(hMon, mi) Then
    '            Debug.Print "MonitorFromWindow: "; hMon
    '            Debug.Print hMon, mi.rcMonitor.Top; mi.rcMonitor.Bottom, mi.rcMonitor.Left; mi.rcMonitor.Right
    '            Debug.Print hMon, mi.rcWork.Top; mi.rcWork.Bottom, mi.rcWork.Left; mi.rcWork.Right
    '        End If
    '    End If
    'End Sub

    Private Sub UserForm_Initialize()
        Me.StartUpPosition = 0
        Dim hMon As LongPtr, mi As MONITORINFO
        hMon = MonitorFromWindow(GetFocus, MONITOR_DEFAULTTONEAREST)
        If hMon Then
            mi.cbSize = LenB(mi)
            If GetMonitorInfo(hMon, mi) Then
                Me.Top = mi.rcWork.Top * 0.75 + (mi.rcWork.Bottom - mi.rcWork.Top - Me.Height) * 0.75 / 2
                Me.Left = mi.rcWork.Left * 0.75 + (mi.rcWork.Right - mi.rcWork.Left - Me.Width) * 0.75 / 2
            End If
        End If
    End Sub

(白茶) 2025/09/25(木) 11:38:26


白茶さんありがとうございます。
処理させてみました。

違うかもしれませんが、
UserForm1.StartUpPosition = 2
の時と同じ動きをしているように思います。

ファイル一覧を表示している窓(名称がわからない)?
→調べたところ多分エクスプローラー画面という名称だと思う。

ここでアイコンをクリックすると、
そのスクリーンの中心に表示したいのです。

現状では、前回開いていたエクセルや動作していたスクリーン上の
中心に表示されてしまいます。
おそらく、シートの表示された位置のスクリーンの中心に
表示されていると思われます。

今回はスクリーン中央ですが
ゆくゆくは、アイコンをクリックすると
その近辺(クリックしたマウス位置のすぐ右下とか)に
エクセルシートを非表示にしてフォームを表示させたいと
考えています。

(栗栄太) 2025/09/26(金) 08:24:46


 例示したのは、
 キーボードフォーカスを持つウィンドウが表示されているディスプレイの中心にユーザーフォームを表示する例になります。
 こちらで手っ取り早く動作確認するのに都合の良い書き方をさせて頂きました。

 エクスプローラを基準にするのであれば、その時に表示されているエクスプローラのhwndを探して、
 エクスプローラの表示されているディスプレイの中心に...という事も可能でしょう。
 (ちょっとそっちまでは食指が動きませんでした ^^;)

 あと見ての通りですが、
 コメントアウトした部分にGetCursorPos使った実験も残してありますのでご参考まで〜

(白茶) 2025/09/26(金) 09:49:06


 食指動いたので追記しておきます ^^;

 ↓エクスプローラのhwndを探して、あればそっちのディスプレイの中心に表示する例です。

    Private Type RECT
        Left   As Long
        Top    As Long
        Right  As Long
        Bottom As Long
    End Type
    Private Type MONITORINFO
        cbSize    As Long
        rcMonitor As RECT
        rcWork    As RECT
        dwFlags   As Long
    End Type
    Private Declare PtrSafe Function GetMonitorInfo Lib "user32" Alias "GetMonitorInfoA" (ByVal hMonitor As LongPtr, ByRef lpmi As MONITORINFO) As Long
    Private Const MONITOR_DEFAULTTONULL = &H0
    Private Const MONITOR_DEFAULTTOPRIMARY = &H1
    Private Const MONITOR_DEFAULTTONEAREST = &H2
    Private Declare PtrSafe Function MonitorFromWindow Lib "user32" (ByVal hwnd As LongPtr, ByVal dwFlags As Long) As LongPtr
    Private Declare PtrSafe Function FindWindowW Lib "user32" (ByVal lpClassName As LongPtr, ByVal lpWindowName As LongPtr) As LongPtr
    Private Declare PtrSafe Function GetFocus Lib "user32" () As LongPtr

    Private Sub UserForm_Initialize()
        Const TARGET_CLASS_NAME As String = "CabinetWClass"
        Me.StartUpPosition = 0
        Dim hwnd As LongPtr, hMon As LongPtr, mi As MONITORINFO
        hWnd = FindWindowW(StrPtr(TARGET_CLASS_NAME), 0&)
        If hwnd = 0 Then hwnd = GetFocus
        hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST)
        If hMon Then
            mi.cbSize = LenB(mi)
            If GetMonitorInfo(hMon, mi) Then
                Me.Top = mi.rcWork.Top * 0.75 + (mi.rcWork.Bottom - mi.rcWork.Top - Me.Height) * 0.75 / 2
                Me.Left = mi.rcWork.Left * 0.75 + (mi.rcWork.Right - mi.rcWork.Left - Me.Width) * 0.75 / 2
            End If
        End If
    End Sub

 あとオマケで。↓マウスカーソルに近寄ってくるやつ。^^;

    Private Type POINTAPI
        x As Long
        y As Long
    End Type
    Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
    Private Declare PtrSafe Sub Sleep Lib "KERNEL32.dll" (ByVal dwMilliseconds As Long)
    Private Sub UserForm_Activate()
        Dim p As POINTAPI, i As Long, t As Long, l As Long
        Call GetCursorPos(p)
        For i = 1 To 10
            t = (Me.Top + Me.Height / 2) * 1.33
            l = (Me.Left + Me.Width / 2) * 1.33
            Me.Left = Me.Left + (p.x - l) / 2 * 0.75
            Me.Top = Me.Top + (p.y - t) / 2 * 0.75
            DoEvents
            Sleep 50
        Next
    End Sub

(白茶) 2025/09/27(土) 10:59:16


白茶さん
ありがとうございます。

意図したディスプレイに開くことができました。

おまけ?の方も
フォームがスライドして表示しています。

(栗栄太) 2025/09/30(火) 07:40:50


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.