ToolStripManagerとSettingsの極意:ユーザーごとのツールバー状態永続化を実装する
Windows Forms(WinForms)による業務アプリケーション開発において、エンドユーザーからの「要望」で最も多いものの一つが、「ツールバーのレイアウトやドッキング位置を記憶してほしい」という要件だ。
開発初期の段階では、デフォルトのドッキング位置で満足していても、現場で実務をこなすにつれて、ユーザーは自分の利き手や作業フローに合わせてメニューバーを左右に寄せたり、特定のツールバーをフローティングさせたり、不要な項目を非表示にしたりしたくなる。
これを毎回手動で戻させるようなUI設計をしているとしたら、それはアプリケーションの設計ミスであり、ユーザーの業務効率を著しく阻害していると言わざるを得ない。
今回は、VB.NETの `System.Windows.Forms.ToolStripManager` と アプリケーション設定 (`My.Settings`) を完全に掌握し、「バグを踏まない、一撃で完璧にレイアウトを永続化・復元するメカニズム」を伝授する。
—
なぜ従来の安易な実装では破綻するのか?
多くの初学者や、場当たり的なコーディングを行うプログラマは、各 `ToolStrip` の `Dock` プロパティや `Visible` プロパティを個別に `My.Settings` へ保存しようとする。
‘ 【アンチパターン】絶対にやってはいけない個別保存
My.Settings.Toolbar1_Top = ToolStrip1.Top
My.Settings.Toolbar1_Visible = ToolStrip1.Visible
‘ これをフォーム上の全てのツールバーに対して書く…
このアプローチは悪夢の始まりである。
将来的にツールバーを追加・削除したり、動的に生成する設計変更が入った瞬間、設定のスキーマが破綻し、インデックスがズレ、最悪の場合は起動時に NullReferenceException の嵐に見舞われることになる。
究極の解決策:ToolStripManagerの活用
.NET Frameworkには、この「ツールバーの迷宮」を鮮やかに解決するための専用クラスが標準で用意されている。それが `ToolStripManager` だ。
`ToolStripManager` を使えば、フォーム上の全ての `ToolStrip` の状態(ドッキング位置、フローティング状態、表示/非表示、サイズ、アイテムの順序まで)を、たった数行のシリアライズされた文字列として一括管理できる。
—
実装アーキテクチャの全体像
堅牢な永続化システムを構築するためには、以下のライフサイクルを厳守する必要がある。
1. 復元(Load時): フォームの初期化が完了し、`ToolStrip` 群がインスタンス化された直後(`Form_Load` の後半)に状態を流し込む。
2. 保存(FormClosing時): ユーザーがアプリケーションを終了する際、フォームが破棄される直前に状態をキャプチャし、永続化レイヤーに書き出す。
それでは、実際のプロダクションコードを見ていこう。
—
プロダクションコード例 (VB.NET)
以下のコードは、そのままプロジェクトに組み込んで即座に実用できる、堅牢な実装である。
前提条件
Visual Studioのプロジェクトプロパティから「設定 (Settings)」タブを開き、以下の設定を追加しておく。
- 名前: `ToolStripState`
- 型: `String`
- スコープ: `User`
フォーム側の実装コード
Public Class MainForm
Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
‘ 1. ユーザー固有のレイアウト設定を復元する
LoadToolbarState()
Catch ex As Exception
‘ 復元失敗時はログに記録し、デフォルトレイアウトで続行させる(例外でアプリを落とさない)
System.Diagnostics.Debug.WriteLine($”ツールバー復元エラー: {ex.Message}”)
‘ 必要に応じてデフォルト状態にリセットする処理をここに記述
End Try
End Sub
Private Sub MainForm_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
Try
‘ 2. 現在のレイアウト状態をキャプチャして永続化する
SaveToolbarState()
Catch ex As Exception
‘ 保存失敗がアプリケーション終了をブロックしないようにする
System.Diagnostics.Debug.WriteLine($”ツールバー保存エラー: {ex.Message}”)
End Try
End Sub
”’
”’
Private Sub LoadToolbarState()
‘ My.Settings.ToolStripState に保存された文字列が存在するかチェック
Dim savedState As String = My.Settings.ToolStripState
If Not String.IsNullOrEmpty(savedState) Then
‘ ToolStripManagerを使用して、フォーム全体のツールバー状態を復元
‘ 引数には「フォームの型名」をプレフィックスとして渡すのがベストプラクティス(複数フォーム対応)
Dim settingsKey As String = Me.GetType().FullName
‘ 既存の設定適用
‘ 注意: フォーム内のすべての ToolStrip コントロールがマネージャーに登録されている必要がある
ToolStripManager.LoadSettings(Me, settingsKey)
End If
End Sub
”’
”’
Private Sub SaveToolbarState()
Dim settingsKey As String = Me.GetType().FullName
‘ ToolStripManagerに現在の状態をキャプチャさせる
ToolStripManager.SaveSettings(Me, settingsKey)
‘ アプリケーション設定へコミット(ディスクへの書き込み)
My.Settings.Save()
End Sub
End Class
—
現場で役立つチーフアーキテクトからの重要アドバイス
この実装を導入するにあたり、プロの現場でハマりがちな「罠」と、その回避策を共有しておこう。
1. フォームの型名をキーにする理由
複数画面(MDI親フォームや複数の子ウィンドウ)を持つアプリケーションの場合、`ToolStripManager.SaveSettings(Me)` のシグネチャを安易に使うと、設定のキーが競合し、他の画面のレイアウトを上書きしてしまう事故が起きる。
必ず `Me.GetType().FullName`(または一意に識別できる文字列)をキーとして明示的に渡す設計にすること。
2. 動的に追加される ToolStrip への配慮
もしアプリケーションの起動後、ユーザーの権限や動的なデータバインドによって `ToolStrip` コントロールが後から動的に生成・追加される場合、`LoadToolbarState` を呼び出すタイミングは「全ての ToolStrip の生成とフォームへのコントロール追加が完全に終わった後」でなければならない。
タイミングを誤ると、保存されていた設定データの宛先が見つからず、レイアウトが崩壊する原因になる。
3. 設定ファイルが破損したときの「逃げ道」
ユーザーのPC環境の異常終了やディスク容量不足などにより、`user.config` が破損することが稀にある。
このような場合、起動時に `ConfigurationErrorsException` がスローされることがあるため、アプリケーションのエントリーポイント(`Application_ThreadException` や `AppDomain.CurrentDomain.UnhandledException`)で例外を捕捉し、破損した設定ファイルを自動削除(`My.Settings.Upgrade()` やファイル削除)してデフォルト起動させるフォールバック機構を組んでおくと、ヘルプデスクへの問い合わせを劇的に減らすことができる。
—
まとめ
業務アプリケーションの価値は、「にいのまゝに動くこと」だけでなく、「ユーザーのストレスを極限まで排除し、日々のルーティンワークを加速させること」にある。
たったこれだけのコード、たったこれだけの設計思想を取り入れるだけで、アプリケーションの「プロダクトとしての格」は一段と跳ね上がる。
ぜひあなたのプロジェクトでもこの実装を取り入れ、ワンランク上のWindows Formsアプリケーションを構築してほしい。
