【VBA×WindowsAPI】指定のウィンドウ(UserForm)に図形を描画する

VBAのUserFormに何かしらの図形を描画したいという場合、VBAの標準機能だけでは実現することが難しいです。Frameを細くして直線に見立てたり、Imageとして用意しておいた図形を挿入したりすることはできますが、どちらもあまり汎用性はなく動的に図形の描画を変更することもできません。

こういった場合には、WindowsAPIの関数を利用することでUserFormの指定座標に直線や長方形、楕円などを(静的にも動的にも)描画することが可能になります。

UserFormに図形を描画する

WindowsAPIを使うことで上画像のようにUserForm上に直線や四角形、楕円や多角形などの基本的な図形を描画することができます。このとき描画する図形それぞれに対して線種や線幅、色、塗りつぶし、ハッチングの種類、ビットマップパターンなどの細かな設定を行うことが可能です。

これらの図形を描画するにはUserFormウィンドウが持つデバイスコンテキスト(DC)にアクセスする必要があります。DCとは主にウィンドウの描画関係の機能を取り扱うもので、GetDC関数GetWindowDC関数に引数として対象のウィンドウハンドルを渡すことで取得することができます。

図形の描画を行う際には線の色や線種、線幅の情報を持つ「ペン」と塗りつぶしの色やスタイルの情報を持つ「ブラシ」といったGDIオブジェクトというものを作成して、描画対象のDCに関連付けさせる必要があります。(DCとGDIオブジェクトの関連付けはSelectObject関数で設定します)

VBAで指定のウィンドウ(UserForm)に図形を描画するには下記のWindows APIを利用します。
それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。
描画関連の関数は非常に多くあり、下記は主なものを抜粋したものであり全てではありません。

icon-check-square FindWindow関数              :ウィンドウハンドルを取得する
icon-check-square GetDC関数                        :デバイスコンテキスト(DC)を取得する
icon-check-square ReleaseDC関数                 :指定のDCを解放する
icon-check-square SelectObject関数              :指定のDCでオブジェクト(ペン/ブラシ等)を1つ選択する
icon-check-square DeleteObject関数              :指定のオブジェクト(ペン/ブラシ等)を1つ削除する
icon-check-square CreatePenIndirect関数    :論理ペンを作成する

icon-check-square CreateBrushIndirect関数  :論理ブラシを作成する
icon-check-square MoveToEx関数                   :現在の位置を指定された座標に更新する(始点の定義)
icon-check-square LineTo関数                         :直線を描画する
icon-check-square Polyline関数                      :折れ線(ポリライン)を描画する
icon-check-square PolyBezier関数                  :ベジェ曲線を描画する
icon-check-square Rectangle関数                   :長方形を描画する
icon-check-square RoundRect関数                 :角が丸い長方形を描画する
icon-check-square Polygon関数                      :多角形を描画する
icon-check-square Ellipse関数                        :楕円を描画する
icon-check-square Pie関数                             :パイ形(扇形)を描画する
icon-check-square Ellipse関数                        :楕円を描画する

「そもそもWindows APIって何?」という方はコチラ(メインページ)も併せて参照下さい。
また、その他のウィンドウへの描画関連の操作に関しては下記も併せて参照下さい。

UserFormに回転させたテキスト(文字列)を描画する

サンプルコード

UserFormに図形を描画するためのサンプルコードは下記の通りです。
下記コードをUserFormのコードにコピペし、UseFormを表示させるだけ(UserForm1.Show等)で図形を描画することができます。

コード解説

図形を描画するには下記の手順を行います。

① 描画対象のウィンドウのDCへのハンドルを取得する (GetDC関数)
② ペン, ブラシの作成 (CreatePenIndirect関数, CreateBrushIndirect関数)
③ ペン, ブラシを描画対象のウィンドウのDCに関連付ける (SelectObject関数)
④ 図形の描画 (Rectangle関数, Ellipse関数, Polyline関数など)
⑤ ペン, ブラシの削除 (DeleteObject関数)
⑥ DCの解放 (ReleaseDC関数
 

icon-edit 描画対象のウィンドウのDCへのハンドルを取得する

ウィンドウには描画関係の処理を行うためのデバイスコンテキスト(DC)というものが存在します。
このDCを介すことで、ウィンドウ内のピクセル情報を取得したり、図形の描画を行ったりすることが可能になります。DCを取得するにはまず対象のウィンドウのンドルを取得する必要があります。これはウィンドウを識別するためのID情報のようなもので、ハンドルを取得すれば”どの”ウィンドウに対して処理を行うのかを簡単に指示することができます。(Windows APIでは頻出ワードです)

今回は指定のウィンドウ(ここではUserForm)に図形を描画させたいので、その対象となるウィンドウのハンドルを取得します。ウィンドウハンドルを取得する方法はいくつも存在しますがここでは常套手段のFindWindow関数を使ってウィンドウハンドルを取得しています。FindWindow関数は下記のように記載することで指定のクラス名もしくはウィンドウ名から該当のウィンドウへのハンドルを取得することができます。(UserFoemの場合はウィンドウ名に「Me.Caption」で取得可能)

icon-code FindWindow関数 

hWnd = FindWindow(クラス名”, “ウィンドウ名”)

描画対象のウィンドウのハンドルが取得できれば、それをそのままGetDC関数に引数として渡せばそのウィンドウのDCハンドルを取得することができます。GetDC関数は下記のように記載します。

icon-code GetDC関数 

hDC = GetDC(ウィンドウハンドル)

ここで取得したDCは最終的にメモリから明示的に解放させる必要があるので注意が必要です。
 

icon-edit ペン, ブラシの作成

図形を描画するにはGDIオブジェクトである論理ペンと論理ブラシというものを作成する必要があります。ペンとブラシを作成するための関数はいくつか存在しますが、ここでは汎用性の高いCreatePenIndirect関数CreateBrushIndirect関数を使います。

CreatePenIndirect関数は下記のようにペンの太さ、ペンのスタイル、ペンの色を格納したLOGPEN構造体を引数として入力することで任意の論理ペンを作成し、そのハンドルを取得することができます。論理ペンは主に図形の輪郭線を描く際に使用されるものです。

icon-code CreatePenIndirect関数 

With lpLogPen
    .lopnWidth = lpPoint  ‘ペンの太さ
    .lopnStyle = lStyle   ‘ペンのスタイル(線種)
    .lopnColor = lColor    ‘ペンの色
End With
hPen
= CreatePenIndirect(lpLogPen)

CreateBrushIndirect関数は下記のようにブラシのスタイル、ブラシの色、ハッチングのスタイルを格納したLOGBRUSH構造体を引数として入力することで任意の論理ブラシを作成し、そのハンドルを取得することができます。論理ブラシは主に図形の内側の塗りつぶしに使用されるものです。

icon-code CreateBrushIndirect関数 

With lpLogBrush
    .lbStyle = lStyle     ‘ブラシのスタイル
    .lbColor = lColor    ‘ブラシの色
    .lbHatch = lHatch  ‘ハッチングのスタイル (※lStyleがBS_HATCHED時に有効)
End With
hPen
= CreatePenIndirect(lpLogBrush)

ペンのスタイル(線種)やブラシのスタイル(塗りつぶし方法)は既に定数値で決められており、その頭文字から「PS_xxx」「BS_xxx」という名前が付けられています。ブラシの”ハッチングのスタイル”とは平行線で描かれた模様のことで図面や絵画、版画などで利用される技法です。コチラも定数値が用意されており「HS_xxx」という名称が付けられています。(VBAでは明示的に定義する必要があります)
  

icon-edit ペン, ブラシを描画対象のウィンドウのDCに関連付ける

作成した論理ペンと論理ブラシは描画対象のDCと関連付けることで、そのDC内で描画を行うことが可能になります。これはSelectObject関数を使って下記のように記載することで設定することができます。SelectObject関数はこれまでに取得した描画対象ウィンドウのDCハンドル(hDc)と、描画するための論値ペンのハンドル(hPen),もしくは論理ブラシ(hBrush)を引数として渡すだけです。

icon-code SelectObject関数 

hPen = SelectObject(hDc, hPen)

このとき、戻り値であるhPenは今回関連付けたペン(hPen)の前に関連付けられていたペンへのハンドルです。初回の実行ではDCにデフォルトで設定されていたペンへのハンドルが返されます。サンプルコードでは一時的なペンとブラシを作成し、このDCにデフォルトで設定されていたペンへのハンドルを取得しています(ペンとブラシの削除時に使用します)。DCには常に1つのペン、ブラシしか関連付けさせることが出来ないため、ペンを入れ替えたい場合は毎回SelectObject関数で関連付けさせているペンもしくはブラシを切り替える必要があります
 

icon-edit 図形の描画

上記までで描画するための設定は完了したので実際に図形を描画していきます。図形を描画するための関数は非常に多くあるため、ここでは長方形を描画するためのRectangle関数を例にします(多少の違いはありますが考え方はどれも同じ)。Rectangle関数は長方形の左上頂点のXY座標、右下頂点のXY座標の4つの座標を入力することで任意のサイズの長方形を描画することが可能です。

icon-code Rectangle関数 

Call Rectangle(hDc, dX1, dY1, dX2, dY2)

第1引数のhDcにはすでにSelectObject関数でペンとブラシが設定されているので、描画される長方形は関連付けられたペンとブラシによって描画されます。これにより長方形のサイズだけでなく輪郭線や塗りつぶしも任意の設定で描画することが可能です。他の図形を描画する場合も考え方は同じで円を描きたい場合はEllipse関数、多角形を描きたい場合はPolyline関数を使うというように関数が変わるだけでどれもペンとブラシが設定されているhDcを渡すだけですぐに描画可能です。
 

icon-edit ペン, ブラシの削除

論理ペンと論理ブラシは使わなくなった場合、削除してメモリを解放する必要があります。
削除するにはDeleteObject関数を使って下記のように記載します。引数に作成したペン、ブラシのハンドルを渡すだけです。サンプルコードでは作成したペンとブラシのハンドルを一旦すべてコレクションに格納し、UserFormのTerminateイベントで一気に解放していますが、ペンとブラシを使わなくなった時点で解放しても問題ありません。

icon-code DeleteObject関数 

Call DeleteObject(hPen)  ‘※hPenはDCと関連付けられていないこと

注意としてこの関数でペンとブラシを削除する際、削除するペンとブラシはDCに関連付けられて”いない”必要がありますつまりは最新でDCに関連付けたペンとブラシは、DCとの関連付けを終了させるまで削除をしてはいけないということです。DCに関連付けているペンとブラシはSelectObject関数で切り替えることができるので、先に保持しておいたデフォルトのペンとブラシをDCに関連付けさせることで作成したペンとブラシをすべて削除することが可能になります。
  

icon-edit DCの解放

ペンやブラシと同様にDCも使わなくなった場合は明示的にメモリを解放する必要があります。
DCの解放はReleaseDC関数を使って下記のように記載します。引数に解放するDCのハンドルとそのDCを持つウィンドウのハンドルを渡すだけです。

icon-code ReleaseDC関数 

Call ReleaseDC(hWnd, hDc)

関連情報

icon-share-square VBA×WindowsAPIまとめページ

その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。

icon-share-square 参考

Microsoft公式:色付きのペンとブラシの作成 – Win32 apps

2023年12月3日Excel, VBA, Windows API

Posted by Lic