【テクニカル・上級編】【ユーザーインターフェース(UI)の近代化】UserFormとListViewコントロールを組み合わせたリッチなBOMプレビュー – SolidWorks VBA解析バイブル

スポンサーリンク

【SolidWorks VBAを掌握する極限の知見】UIの近代化:UserFormとListViewによるリッチなBOMプレビューの実装

SolidWorksマクロ開発において、未だに`MsgBox`や`InputBox`といった前近代的なUIでユーザーと対話している現場を見かける。複雑なアセンブリ構造から抽出したBOM(部品表)データをテキストの羅列で表示し、ユーザーに「合っていますか?」と問うのは、システム設計の怠慢に他ならない。

シニアエンジニアや社内システムアーキテクトが目指すべきは、ネイティブアプリケーションと見紛うほどの洗練されたUX(ユーザー体験)の提供である。

本稿では、VBAの`UserForm`上に`ListView`コントロールを配置し、SolidWorks APIから直接取得したコンポーネント情報を高速かつリッチに描画・プレビューする手法を解説する。単なるコントロールの配置方法ではない。メモリ管理、COMオブジェクトのライフサイクル、そしてWindows 64bit環境における罠を回避するための極限の知見をここに開示する。

1. 現代のVBA開発におけるUIの課題とアプローチ

VBA標準のフォーム部品は貧弱であり、階層構造や表形式データを扱うには限界がある。特に、数千点規模の部品を持つ大規模アセンブリのBOMを処理する場合、描画の遅延やメモリリークが致命的な問題となる。

ここで投入するのが、Windowsコモンコントロール(`MSComctlLib.ListView` または `Microsoft Windows Common Controls`)である。しかし、このアプローチには「参照設定のバージョン依存問題」「64bit Office環境におけるポインタ不整合」という、幾多のエンジニアを葬ってきた闇が潜んでいる。

これを完全克服するためのアーキテクチャを構築する。

2. アーキテクチャ設計と実装の全体像

以下のモジュール構成で実装を行う。

1. SldWorks.SldWorks / ModelDoc2: アクティブドキュメントの安全な取得とコンポーネント走査。
2. UserForm: `ListView`を搭載し、ユーザーの視覚的インタラクション(選択・ソート)を受け付ける。
3. Class Module (Optional): 循環参照を防ぐためのデータ保持構造。

3. 実装コード:リッチBOMプレビューフォーム

以下のコードは、64bit VBA環境を前提とし、堅牢性とパフォーマンスを極限まで高めた実装例である。

UserFormの初期設定とListViewの構築

UserForm(フォーム名:`BOMPreviewForm`)を新規作成し、`ListView`コントロール(レポート形式:Report)を配置した上で、以下のコードを記述する。

Option Explicit

‘ ==============================================================================
‘ 伝説のチーフアーキテクトによる実装:ListViewを活用したBOMプレビュー
‘ ==============================================================================

‘ Windows API: フォームのウィンドウハンドル操作や描画抑制用(必要に応じ拡張)
If VBA7 Then
Private Declare PtrSafe Function SendMessage Lib “user32” Alias “SendMessageA” (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, ByRef lParam As Any) As LongPtr
Else
Private Declare Function SendMessage Lib “user32” Alias “SendMessageA” (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
End If

Const WM_SETREDRAW = &HB

Private Sub UserForm_Initialize()
Me.Caption = “SolidWorks Advanced BOM Previewer [Enterprise Edition]”
Call InitializeListViewColumns
End Sub

‘ ——————————————————————————
‘ ListViewの列ヘッダー初期化(メモリ効率を考慮した一括設定)
‘ ——————————————————————————
Private Sub InitializeListViewColumns()
With Me.ListView1
.View = lvwReport
.Gridlines = True
.FullRowSelect = True
.HideSelection = False

‘ 柱(カラム)の定義
.ColumnHeaders.Clear
.ColumnHeaders.Add , “colItem”, “アイテム”, 60
.ColumnHeaders.Add , “colPartNo”, “部品番号 / 品名”, 180
.ColumnHeaders.Add , “colConfig”, “コンフィギュレーション”, 140
.ColumnHeaders.Add , “colQty”, “数量”, 60, lvwColumnRight
.ColumnHeaders.Add , “colPath”, “ファイルパス”, 300
End With
End Sub

‘ ——————————————————————————
‘ SolidWorksモデルからBOMデータを抽出し、ListViewに高速バインドする
‘ ——————————————————————————
Public Sub LoadBOMData(ByVal swApp As SldWorks.SldWorks)
Dim swModel As SldWorks.ModelDoc2
Set swModel = swApp.ActiveDoc

If swModel Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical, “エラー”
Exit Sub
End If

If swModel.GetType() <> swDocASSEMBLY Then
MsgBox “この機能はアセンブリドキュメントでのみ実行可能です。”, vbExclamation, “警告”
Exit Sub
End If

‘ 描画停止によるパフォーマンス爆速化(フリッカー防止)
SendMessage Me.ListView1.hwnd, WM_SETREDRAW, 0, ByVal 0&

Dim lItem As ListItem
Dim dictBOM As Object
Set dictBOM = CreateObject(“Scripting.Dictionary”)

Dim swAssembly As SldWorks.AssemblyDoc
Set swAssembly = swModel

‘ コンポーネントツリーの再帰取得(トップレベルのみ、または全階層)
Dim vComps As Variant
vComps = swAssembly.GetComponents(True)

If IsEmpty(vComps) Then GoTo CleanUp

Dim i As Long
Dim swComp As SldWorks.Component2
Dim partName As String
Dim configName As String
Dim modelDoc As SldWorks.ModelDoc2

Me.ListView1.ListItems.Clear

‘ 簡易的な集計ロジック(実際にはより高度なコンポーネント解析をここに実装)
For i = LBound(vComps) To UBound(vComps)
Set swComp = vComps(i)

‘ 抑制されたコンポーネントや軽量化された不完全状態の排除
If Not swComp.IsSuppressed() Then
partName = swComp.Name2
configName = swComp.ReferencedConfiguration

‘ 辞書オブジェクトによる集計キー生成
Dim key As String
key = partName & “|” & configName

If dictBOM.Exists(key) Then
dictBOM(key) = dictBOM(key) + 1
Else
dictBOM(key) = 1

‘ ListViewへのアイテム追加
Set lItem = Me.ListView1.ListItems.Add(, , CStr(Me.ListView1.ListItems.Count + 1))
lItem.SubItems(1) = swComp.GetPathName() ‘ 実際にはドキュメントタイトル等に変換
lItem.SubItems(2) = configName
lItem.SubItems(3) = “1” ‘ 数量(集計処理を入れる場合はここで制御)
lItem.SubItems(4) = swComp.GetPathName()
End If
End If
Next i

CleanUp:
‘ 描画再開
SendMessage Me.ListView1.hwnd, WM_SETREDRAW, 1, ByVal 0&
Me.ListView1.Refresh

‘ オブジェクトの明示的解放(VBAにおけるCOMリーク防止の鉄則)
Set swModel = Nothing
Set swAssembly = Nothing
Set dictBOM = Nothing
End Sub

Private Sub cmdClose_Click()
Unload Me
End Sub

4. チーフアーキテクトが教える「死角のない」メモリ最適化とAPIの作法

上記のコードを単に動かすだけなら中級者でも可能だが、「実務で何万回も実行される大規模システム」「メモリが逼迫したデスクトップ環境」では、以下の設計思想が生死を分ける。

A. COMオブジェクトの参照解放(`Set xxx = Nothing`)の徹底

SolidWorks VBAにおいて、`SldWorks.Component2` や `SldWorks.ModelDoc2` などのCOMインターフェースをループ内で取得し続けると、VBAの背後にあるCOMランタイムの参照カウンタが暴騰し、Excel/SolidWorksプロセス全体のメモリリークを引き起こす。
ループ変数や一時オブジェクトは、スコープを抜けたら必ず `Set obj = Nothing` で明示的に解放し、ガベージコレクションの確実な発火を促さなければならない。

B. `WM_SETREDRAW` による描画フリーズの回避

数千行のアイテムを `ListView` に `Add` する際、1行追加するごとにWindowsはUIの再描画(リペイント)を実行する。これがアセンブリBOM表示時の「カクつき」や「数秒間のフレス」の元凶である。
APIの `SendMessage` を用いて、データ投入前に描画を停止(`WM_SETREDRAW = 0`)させ、全データの流し込みが終わった後に再開(`WM_SETREDRAW = 1`)させることで、描画速度を最大50倍以上に跳ね上げることが可能だ。これはプロフェッショナルなWindowsプログラミングの常道である。

5. まとめ

本稿で解説した `UserForm` と `ListView` の融合は、単に「見た目がカッコいいマクロ」を作るためのものではない。
ユーザーが巨大なアセンブリの全体像を視覚的に把握し、誤った設計変更や処理の実行を未然に防ぐための「強力なリスクヘッジ機構」である。

レガシーな `MsgBox` の時代は終わった。
SolidWorks APIの深淵を理解し、OSの挙動までをコントロールする洗練されたUIアーキテクチャを、あなたの現場にも今すぐ導入してほしい。

タイトルとURLをコピーしました