【SolidWorks VBAを極める】UserFormとListViewで作る近代的女王BOMプレビュー
工業用3D CAD「SolidWorks」のVBA(マクロ)開発において、最大のボトルネックは何か知っているか?
それは、「処理のブラックボックス化」と「貧弱なユーザーインターフェース(UI)」だ。
未だに「処理を実行しますか? [はい/いいえ]」のMsgBoxを出し、コンソールにも似たイミディエイトウインドウに結果を流し込むような前時代的なマクロを作っていないだろうか? そんなツールを現場に放り投げようものなら、「動いたけど何が起きたか分からない」「選択ミスでアセンブリが崩壊した」という悲鳴と共に、二度と使われなくなるのがオチだ。
プロフェッショナルな業務自動化エンジニアが目指すべきは、「ミスを物理的に起こさせず、視覚的な安心感を与えるモダンなUI」の実装である。
今回は、VBA標準のUserFormにWindowsコモンコントロール(`ListView`)をブチ込み、SolidWorksのアセンブリ構造(BOM)をリッチにプレビュー・選択させてから一括処理を行う、プロダクション品質のコードと設計思想を伝授する。
—
1. なぜ標準のListBoxではダメなのか?
VBAの標準フォームには `ListBox` が用意されている。しかし、実務で数千点の部品を扱うBOM(部品表)を処理する際、この標準 `ListBox` はあまりにも無力だ。
- 列(カラム)の概念がない:「部品番号」「品名」「数量」「材質」を綺麗に並べて表示できない。
- ヘッダーソートができない:ユーザーがクリックして並び替える機能がない。
- 拡張性の欠如:アイコン表示やチェックボックスの埋め込みが面倒。
そこで登場するのが、Windows API(COMコンポーネント)である Microsoft ListView Control (MSComctlLib.ListView) だ。これをUserFormの武器として使いこなせば、VBAとは思えないほどのモダンなグリッドインターフェースが手に入る。
—
2. アーキテクチャ設計:堅牢性とパフォーマンスの極意
SolidWorks APIとUIを連携させる際、以下の3つの「罠」に気をつけなければならない。
1. COMオブジェクトの解放漏れ(メモリリーク)
SolidWorksの `ModelDoc2` や `Component2` は、参照を取得するたびにメモリを消費する。特にループ内でアセンブリ構造を再帰的に巡回する際は、明示的な変数管理が不可欠。
2. 画面描画のフリーズ(ScreenUpdating)
数千コンポーネントを `ListView` に1行ずつ追加すると、描画更新が走って地獄のように遅くなる。`Visible = False` や `SendMessage` による描画抑止が必須。
3. モデルとUIのID紐付け
ListViewでユーザーが選択した「行」と、SolidWorks内の「実際のコンポーネントオブジェクト(`Component2`)」をどう紐付けるか。ここが設計のキモだ。
—
3. 実装コード:プロダクション品質のBOMプレビューツール
以下のコードは、アクティブなアセンブリから構成部品を抽出し、ListViewに「部品名」「コンポーネント名」「設定名」「ファイルパス」をマッピングしてプレビュー表示、さらに選択した部品に対して何らかの処理(例:プロパティ書換やエクスポート)を実行するための完全な実装サンプルだ。
前提条件(環境構築)
1. VBAエディタ(VBE)を開く。
2. ツール > 参照設定 から 「Microsoft Windows Common Controls 6.0 (SP6)」 にチェックを入れる(※環境によってパスが異なる場合は適宜ocxを登録)。
3. UserFormを1つ作成し、名前を `BOMPreviewForm` とする。
4. フォーム上に以下のコントロールを配置する。
- `ListView` (コントロール名を `lvwBOM` に変更)
- `CommandButton` (コントロール名を `cmdProcess`、Captionを「選択項目を処理」に変更)
- `CommandButton` (コントロール名を `cmdCancel`、Captionを「キャンセル」に変更)
—
A. UserFormモジュール(`BOMPreviewForm`)のコード
Option Explicit
‘ フォーム側ではUIイベントとユーザーの選択結果のみを管理する
Private m_IsCancelled As Boolean
Public Property Get IsCancelled() As Boolean
IsCancelled = m_IsCancelled
End Property
Private Sub UserForm_Initialize()
m_IsCancelled = True
Call InitializeListView
End Sub
‘ ListViewの初期設定(カラムヘッダーの構築)
Private Sub InitializeListView()
With Me.lvwBOM
.View = lvwReport ‘ 詳細表示モード
.FullRowSelect = True ‘ 行全体を選択
.Gridlines = True ‘ グリッド線を表示
.MultiSelect = False ‘ 単一選択(必要に応じてTrueに変更可)
‘ カラムヘッダーの追加 (テキスト, 幅)
.ColumnHeaders.Add , “colNo”, “No”, 40
.ColumnHeaders.Add , “colCompName”, “コンポーネント名”, 150
.ColumnHeaders.Add , “colConfig”, “コンフィギュレーション”, 120
.ColumnHeaders.Add , “colPath”, “ファイルパス”, 250
End With
End Sub
‘ 「処理実行」ボタン
Private Sub cmdProcess_Click()
If Me.lvwBOM.SelectedItem Is Nothing Then
MsgBox “処理するアイテムが選択されていません。”, vbExclamation, “警告”
Exit Sub
End If
m_IsCancelled = False
Me.Hide
End Sub
‘ 「キャンセル」ボタン
Private Sub cmdCancel_Click()
m_IsCancelled = True
Me.Hide
End Sub
‘ 外部からListViewにデータを一括流し込むためのパブリックメソッド
Public Sub AddBOMRow(ByVal index As Long, ByVal compName As String, ByVal configName As String, ByVal filePath As String, ByVal compObj As Object)
Dim itm As ListItem
Set itm = Me.lvwBOM.ListItems.Add(, “K_” & index, CStr(index))
itm.SubItems(1) = compName
itm.SubItems(2) = configName
itm.SubItems(3) = filePath
‘ 重要:ListViewのアイテムに実際のSolidWorksコンポーネントオブジェクトを紐付けることはできないため、
‘ キーや独自コレクション、または別モジュールで管理するアプローチをとる。
‘ ここではTagプロパティやカスタムクラス連携の土台を作る。
Set itm.Tag = compObj ‘ 参照を保持(※循環参照に注意だがVBAのフォーム破棄時に解放される)
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
‘ ✕ボタンで閉じられた場合のハンドリング
If CloseMode = vbFormControlMenu Then
m_IsCancelled = True
End If
End Sub
—
B. 標準モジュール(`MainModule`)のコード
ここがSolidWorks APIとフォームを仲介する司令塔だ。
Option Explicit
‘ SolidWorksアプリケーションオブジェクトのグローバル定義
Public swApp As SldWorks.SldWorks
Sub RunBOMPreviewTool()
Set swApp = Application.SldWorks
Dim swModel As SldWorks.ModelDoc2
Set swModel = swApp.ActiveDoc
‘ 1. ドキュメントが開かれているか、かつアセンブリであるかのバリデーション
If swModel Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical, “エラー”
Exit Sub
End If
If swModel.GetType <> swDocASSEMBLY Then
MsgBox “このマクロはアセンブリドキュメントでのみ実行可能です。”, vbCritical, “エラー”
Exit Sub
End If
Dim swAssy As SldWorks.AssemblyDoc
Set swAssy = swModel
‘ 2. フォームのインスタンス化
Dim frm As BOMPreviewForm
Set frm = New BOMPreviewForm
‘ 3. 画面描画のロック(パフォーマンス劇的改善の秘訣)
‘ ※UserForm表示前のデータロードを高速化
Dim vComps As Variant
vComps = swAssy.GetComponents(True) ‘ トップレベルのみ、または再帰
If IsEmpty(vComps) Then
MsgBox “コンポーネントが見つかりませんでした。”, vbInformation, “情報”
Exit Sub
End If
‘ 4. データの収集とListViewへの流し込み
Dim i As Long
Dim swComp As SldWorks.Component2
Dim compName As String
Dim configName As String
Dim filePath As String
‘ ちらつき防止のためフォームを表示する前にデータをロード
For i = LBound(vComps) To UBound(vComps)
Set swComp = vComps(i)
‘ 隠しコンポーネントや軽量化の状態を考慮
If Not swComp.IsSuppressed Then
compName = swComp.Name2
configName = swComp.ReferencedConfiguration
filePath = swComp.GetPathName
‘ フォームのメソッドを叩いて行を追加
frm.AddBOMRow i + 1, compName, configName, filePath, swComp
End If
Next i
‘ 5. ユーザーにフォームを提示
frm.Show vbModal
‘ 6. キャンセルされた場合の処理
If frm.IsCancelled Then
Unload frm
Exit Sub
End If
‘ 7. ユーザーが選択したアイテムに対するメイン処理の実行
Dim selectedItem As Object
On Error Resume Next
Set selectedItem = frm.lvwBOM.SelectedItem
On Error GoTo 0
If Not selectedItem Is Nothing Then
‘ Tagに格納したComponent2オブジェクトを取り出す
Dim targetComp As SldWorks.Component2
Set targetComp = selectedItem.Tag
If Not targetComp Is Nothing Then
‘ 実際のビジネスロジック(例:選択されたコンポーネントをSolidWorks上で選択状態にする)
Dim status As Boolean
swModel.ClearSelection2 True
status = targetComp.Select4(False, 0, False)
MsgBox “以下のコンポーネントを選択しました:” & vbCrLf & _
targetComp.Name2, vbInformation, “処理完了”
End If
End If
‘ クリーンアップ
Unload frm
Set frm = Nothing
Set swApp = Nothing
End Sub
—
4. プロのエンジニアが教える「現場でハマる罠」と回避策
1. x64環境におけるListViewの互換性問題
現代のPCはほぼ100% 64bit版Excel/SolidWorksである。`MSComctlLib.ListView`(`MSCOMCTL.OCX`)は、環境によってはOfficeのバージョンやセキュリティパッチによって「オブジェクトを読み込めません」というエラー(いわゆるControlライセンスのバグ)を吐くことがある。
- 対策: 現場への展開時は、対象PCの `SysWOW64` や `System32` で該当OCXが正しくレジストリ登録されているか確認するバッチファイル(`regsvr32`)を同梱するか、あるいはよりモダンな手法として 「WPFをVBAからホスティングする」 または 「VB.NETでCOMアドイン(外出しEXE/DLL)として実装する」 への移行を視野に入れた設計フットプリントを意識すること。
2. メモリリークの温床 `Tag` プロパティへのオブジェクト格納
今回のコードでは `ListItem.Tag` に `Component2` のCOM参照を代入している。VBAのガベージコレクションは貧弱であるため、フォームを閉じる際に明示的に `Unload` しない形体でインスタンスが残ると、Excel/SolidWorksごとメモリリークを起こす。
- 対策: 必ず明示的に `Unload frm` を行い、ローカル変数の参照を `Nothing` にクリアする習慣を徹底すること。
—
5. まとめ:UIの近代化が現場の信頼を生む
「動けばいいや」で作られたマクロは、エラーが出た瞬間にゴミ箱行きだ。しかし、今回紹介したような `ListView` によるリッチなプレビュー機能、直感的な選択、そして堅牢なエラーハンドリングを備えたツールは、現場のエンジニアたちに「これ、めちゃくちゃ仕事が早いぞ」と言わせる力を持つ。
APIの仕様を叩き込み、オブジェクトのライフサイクルをコントロールし、UIの洗練にこだわること。それこそが、ただの「マクロ職人」から「真の業務自動化エンジニア」へ脱却するための唯一の道である。
さあ、今すぐその古臭いMsgBoxを捨て、このコードをあなたの開発環境に実装してほしい。
