【実務・中級編】Document.Savedプロパティのスマートな活用:未保存変更の有無を判定して無駄な上書きを防ぐ設計 – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握する極限の知見:Document.Savedプロパティのスマートな活用と無駄な上書きの根絶

開発プロジェクトの現場で、自動化マクロの最後によく見かけるコードがある。

‘ 【アンチパターン】思考停止の強制保存
ActiveDocument.Save
ActiveDocument.Close

……待ってほしい。その図面、本当に何か変更を加えたか?
ユーザーが「ちょっとビューを確認しただけ」「レイヤーの表示を切り替えて元に戻しただけ」の状態でこのコードが走れば、無意味なタイムスタンプの更新が発生し、Gitなどのバージョン管理システムやファイルサーバーを無駄に汚すことになる。さらに、もしファイルが読取専用(Read-Only)であれば、容赦なく実行時エラー(エラー9:インデックスが有効範囲にありません、あるいは権限エラー)が爆発する。

プロの業務自動化エンジニアであれば、ファイルの「ダーティ状態(変更有無)」を正確に検知し、真に必要な場合のみ保存処理をスマートに実行するべきだ。

今回は、Visio VBAのオブジェクトモデルにおける `Document.Saved` プロパティの本質を突き詰め、実務でそのまま使える堅牢なライフサイクル管理の設計思想を伝授する。

1. なぜ `Document.Saved` でつまずくのか?(オブジェクトモデルの罠)

Visioの `Document` オブジェクトには、そのドキュメントが最後に保存されてから変更されたかどうかを示す `Saved` というBoolean型のプロパティが存在する。

  • `True`: 変更はない(クリーン)
  • `False`: 変更が加えられている(ダーティ)

一見すると単純なフラグに思えるが、ここにVisio特有の挙動が絡む。
「プログラムから図形を動かす、プロパティを変更する」という行為は、たとえそれが人間の目に見えない微細な変更であっても、容赦なく `Document.Saved = False` に引き上げる。

さらに厄介なのは、マクロの実行自体がドキュメントを変更したとみなされるケースがある点だ。例えば、一時的なレイヤー操作や、シェイプシートの値の再計算などが裏で走ると、開発者の意図とは無関係にフラグが倒れる。

だからこそ、自動化処理の設計においては、「本当にユーザーデータとしての変更があったのか」を論理的に担保しつつ、`Saved` プロパティをコントロールするアーキテクチャが必要となるのだ。

2. 現場で使える!スマートクローズ・パターンの設計

実務の現場で求められるのは、「変更があれば(確認なし、あるいは必要に応じて)保存して閉じ、変更がなければ静かに閉じる」という挙動だ。

ここで、単に `ActiveDocument.Saved` を判定するだけでなく、「自動処理の開始時にフラグの状態を記憶し、処理内容に応じて意図的にコントロールする」というテクニックを紹介する。

プロダクションコード例

以下のコードは、単なるコピペ用ではない。エラーハンドリング(`On Error`)を完備し、ファイルが読取専用の場合や、新規未保存ドキュメント(一度も名前をつけて保存されていない状態)の場合でも完全に破綻しない、極めて堅牢なプロシージャだ。

Option Explicit

Public Sub ExecuteSmartProcessAndClose()
Dim targetDoc As Visio.Document
Dim initialSavedState As Boolean
Dim wasModified As Boolean

‘ アクティブドキュメントを特定(Nothing対策)
On Error Resume Next
Set targetDoc = ActiveDocument
On Error GoTo 0

If targetDoc Is Nothing Then
MsgBox “処理対象のドキュメントが開かれていません。”, vbExclamation, “システムエラー”
Exit Sub
End If

‘ 【重要】処理開始前のSaved状態を退避
initialSavedState = targetDoc.Saved

‘ ==========================================
‘ ここから実際の業務自動化ロジック
‘ ==========================================
On Error GoTo ErrorHandler

‘ 例:図形の追加やプロパティ変更を行う重い処理
Application.ScreenUpdating = False

‘ 模擬的な変更処理
Call ProcessBusinessLogic(targetDoc)

Application.ScreenUpdating = True
‘ ==========================================

‘ 処理の結果、ドキュメントに変更が加わったかを判定
‘ (もしプログラムが何も変更を加えず、かつ元々Saved=Trueなら判定はTrueのまま)
If Not targetDoc.Saved Then
wasModified = True
Else
wasModified = False
End If

‘ 終了処理の分岐
If wasModified Then
‘ 変更がある場合の処理
‘ ※要件に合わせて「自動上書き保存」か「ユーザー確認」を選択
If targetDoc.Path <> “” Then
‘ 既に保存されているファイルなら、無言で上書き保存
targetDoc.Save
Debug.Print “【SmartClose】変更を検知したため、ファイルを上書き保存しました: ” & targetDoc.Name
Else
‘ 新規未保存ドキュメントの場合は、さすがに名前をつけて保存ダイアログを出すか判断
‘ ここでは安全のため閉じるのみ(必要に応じて SaveAs を挟む)
End If
Else
‘ 変更がない場合は、無駄な上書きを完全にスキップ
Debug.Print “【SmartClose】実質的な変更はないため、保存処理をスキップします: ” & targetDoc.Name
End If

‘ 変更フラグを強制的に同期させてから閉じる(これによりVisio特有の「保存しますか?」ダイアログを完全抑制)
‘ ※本当に変更を破棄して閉じたい場合は targetDoc.Saved = True にしてから Close する
targetDoc.Close
Exit Sub

ErrorHandler:
Application.ScreenUpdating = True
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “実行時エラー”
‘ エラー時もオブジェクトの状態を壊さないようにクリーンアップ
Set targetDoc = Nothing
End Sub

Private Sub ProcessBusinessLogic(ByRef doc As Visio.Document)
‘ 実際のシェイプ操作やデータ連携処理をここに記述
Dim shp As Visio.Shape
Set shp = doc.Pages(1).DrawRectangle(1, 1, 3, 3)
shp.Text = “自動生成シェイプ”

‘ ※もしここで「何も変更するデータがなかった」と判定できた場合は、
‘ doc.Saved = True に戻すという高度な制御もエンジニアの裁量としてアリだ。
End Sub

3. チーフアーキテクトからの実践的アドバイス:実務における罠と回避策

罠1:データベースや外部API連携時の「空更新」

外部システムからマスターデータを取得し、Visio図面のシェイプデータを更新するバッチ処理を組んだとする。このとき、「APIから取得した値が、既存のシェイプのプロパティと全く同じ(差分なし)」であっても、VBAから代入文(例: `shp.Cells(“Prop.Status”).FormulaU = “…”`)を実行しただけで、Visioは「値が書き換えられた」と判断し、`Document.Saved = False` に落とし込む。

対策:
データを書き込む前に「現在の値と新しい値」を比較し、異なるときのみ代入する差分更新(Delta Update)のロジックを必ず挟むこと。これにより、実質的な変更がない場合の `Document.Saved = True` を維持でき、無駄なファイル保存を防げる。

罠2:読取専用ファイル(Read-Only)でのクラッシュ

共有サーバー上のテンプレートや、権限管理されたファイルをマクロから操作する場合、`targetDoc.Save` が実行された瞬間に「このファイルは読み取り専用です」というエラーでマクロがクラッシュする。

対策:
`targetDoc.ReadOnly` プロパティを事前にチェックし、Read-Onlyであれば `Save` をバイパスする設計を義務付けよ。

If wasModified And Not targetDoc.ReadOnly Then
targetDoc.Save
End If

4. まとめ:コードの品格は「終わらせ方」に宿る

優れた自動化ツールと、素人が書いたその場しのぎのマクロの決定的な違いは、「リソースの始末と状態管理の美しさ」にある。

処理を開始し、淡々と仕事をこなし、変更がなければ静かに終了し、変更があればスマートに保存して閉じ込む。この一連のライフサイクルを `Document.Saved` プロパティの緻密な制御によって完璧にコントロールすることこそが、現場の信頼を勝ち取るプロフェッショナルの仕事である。

あなたの書くVisio VBAコードから、無駄なダイアログと無意味な上書き保存を今すぐ排除せよ。

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