この記事でできること
VBAの With〜End With を使って、同じオブジェクトへの操作をまとめて書く方法が分かる。
- 対象:VBAのコードが長くなってきた人、処理速度を上げたい人
- 所要時間:コピペ → 実行まで5分
どんな場面で使う?
- ヘッダー行の書式設定 — フォント・背景色・罫線・配置をまとめて設定するとき、同じセル範囲を何度も書かずに済む
- 月次レポートの体裁整え — 複数の範囲にそれぞれ異なる書式を適用するとき、Withのネストで階層的に整理できる
- 大量データへの一括処理 — 1万行以上のデータに対して書式設定と値の加工を行うとき、配列と組み合わせて高速化したい
- セルの読み書きが多い処理 — 同じシート・同じ範囲に対して5回以上プロパティやメソッドを呼ぶとき、コード量と実行時間の両方を削減したい
- 他人が読むコードの保守性向上 — チームで共有するマクロで、どのオブジェクトに対する操作なのかを明確にしたい
—
導入
毎回 Worksheets("Sheet1").Range("A1").Font.Bold = True のように長いコードを書いていた時期がある。同じシート・同じセルに対して5行も6行も同じオブジェクト名を繰り返していて、正直めんどくさかった(①共感)。
With を覚えてからは、コードが半分くらいの長さになって、読み返すのもラクになった。しかも処理速度まで上がる(②実感)。
この記事で、同じように「コードが長い」「もう少し速くしたい」と思っている人がサクッと解決できるようになればうれしい(③動機)。
—
Before / After
Before(Withなし):同じオブジェクト名を何度も書く
Worksheets("Sheet1").Range("A1:D1").Font.Bold = True
Worksheets("Sheet1").Range("A1:D1").Font.Size = 12
Worksheets("Sheet1").Range("A1:D1").Font.Color = RGB(255, 255, 255)
Worksheets("Sheet1").Range("A1:D1").Interior.Color = RGB(0, 112, 192)
Worksheets("Sheet1").Range("A1:D1").Borders(xlEdgeBottom).LineStyle = xlContinuous
After(Withあり):オブジェクト名は1回だけ
With Worksheets("Sheet1").Range("A1:D1")
.Font.Bold = True
.Font.Size = 12
.Font.Color = RGB(255, 255, 255)
.Interior.Color = RGB(0, 112, 192)
.Borders(xlEdgeBottom).LineStyle = xlContinuous
End With
5行 → 同じ5行だが、オブジェクト参照は1回だけ。コードが短くなり、修正時もセル範囲の変更が1箇所で済む。
—
手順
- Excelファイルを.xlsmで保存する
マクロ有効ブック(.xlsm)でないとVBAは保存できない。
- VBEを開く
Alt + F11 でVBE(コードを書く画面)を開く。
- 標準モジュールを挿入する
VBEのメニュー →「挿入」→「標準モジュール」。
- コードを貼り付ける
下のコードをそのままコピーして貼り付ける。
- 実行する
F5 キー、またはVBEのメニュー →「実行」→「Sub/ユーザーフォームの実行」。
—
基本コード(書式設定をWithでまとめる)
Sub WithBasic()
'============================================
' With〜End Withで書式設定をまとめる
' 対象: Sheet1のA1:D1セルにヘッダー書式を適用
'============================================
With Worksheets("Sheet1").Range("A1:D1")
' --- フォント設定 ---
.Font.Bold = True
.Font.Size = 12
.Font.Name = "Meiryo UI"
.Font.Color = RGB(255, 255, 255)
' --- 背景色 ---
.Interior.Color = RGB(0, 112, 192)
' --- 罫線(下線) ---
.Borders(xlEdgeBottom).LineStyle = xlContinuous
.Borders(xlEdgeBottom).Weight = xlMedium
' --- 配置 ---
.HorizontalAlignment = xlCenter
End With
MsgBox "書式設定が完了しました", vbInformation
End Sub
ポイント
Withの後にオブジェクト(セル範囲・シートなど)を指定する- ブロック内では ドット(
.)から始める。ドットがWithの後のオブジェクトにつながる End Withで閉じる
なぜWithを使うと速くなるのかというと、VBAはドットで区切られたオブジェクト参照(例:Worksheets("Sheet1").Range("A1").Font)を毎回「シートを探す → セルを探す → フォントオブジェクトを取得する」という手順で解決している。Withで囲むと、この解決処理が With の行で1回だけ行われ、ブロック内の .Font.Bold や .Font.Size は解決済みのオブジェクトに直接アクセスする。操作が5つ、10つと増えるほど差が開く仕組みだ。
もう1つの大きなメリットは保守性だ。セル範囲を "A1:D1" から "A1:F1" に変更したくなったとき、Withなしだと5行すべてを修正する必要がある。Withありなら With の行1箇所を直すだけで済む。修正漏れによるバグを防げるのは、チームで共有するマクロでは特に重要だ。
罫線の引き方をもっと詳しく知りたい場合は 罫線を一括で引く・消す・種類を変える方法 を参照。セルの背景色・文字色の指定方法は セルの背景色・文字色をRGBで自由に操作する方法 にまとめてある。
—
実務版コード
実務では、Withのネストや配列との組み合わせで、より大きな効果が得られる。
自分は月次レポートの書式設定をWithで整理してから、コードの見通しが一気によくなって、修正作業が半分以下の時間で終わるようになった。
実務版1:ネストWith(シート+セル範囲)
Sub WithNested()
'============================================
' ネストWith:シートとセル範囲を階層で指定
' Sheet1にヘッダー行+データ行の書式を一括適用
'============================================
Dim lastRow As Long
With Worksheets("Sheet1")
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
' --- ヘッダー行の書式 ---
With .Range("A1:D1")
.Font.Bold = True
.Font.Size = 12
.Font.Color = RGB(255, 255, 255)
.Interior.Color = RGB(0, 112, 192)
.HorizontalAlignment = xlCenter
End With
' --- データ行の書式 ---
With .Range("A2:D" & lastRow)
.Font.Size = 11
.Font.Name = "Meiryo UI"
.Borders.LineStyle = xlContinuous
.Borders.Weight = xlThin
.NumberFormat = "#,##0" ' ←必要に応じて変更
End With
' --- 列幅の自動調整 ---
.Columns("A:D").AutoFit
End With
MsgBox "書式設定が完了しました(" & lastRow & "行)", vbInformation
End Sub
ネストWithのルール
- 内側の
Withでは、外側のオブジェクトを直接参照できない - 内側で外側のオブジェクトを使いたい場合は、変数に入れておく
コード解説:
このコードでは外側の With Worksheets("Sheet1") でシートを固定し、内側の With .Range("A1:D1") でセル範囲を指定している。外側のWithがあるから、内側で .Range(...) と書けば自動的にSheet1の範囲を指す。ネストにすることで「どのシートのどの範囲に対する操作か」が視覚的に分かりやすくなる。lastRow の取得も外側のWithブロック内で .Cells(.Rows.Count, 1).End(xlUp).Row と書けるため、シート名の繰り返しがゼロになっている。
実務版2:Withと配列の組み合わせで高速化
大量データに書式を適用する場合、配列で値を処理してからWithで書式設定すると最速になる。画面更新を止める高速化テクニックについては 画面更新・再計算を止めてマクロを高速化する方法 も合わせて確認してほしい。
Sub WithArraySpeed()
'============================================
' With + 配列 + 画面更新停止で高速化
' 1万行のデータに対して値の加工+書式設定を行う
'============================================
Dim ws As Worksheet
Dim lastRow As Long
Dim dataArr As Variant
Dim i As Long
Dim startTime As Double
Set ws = Worksheets("Sheet1")
startTime = Timer
' --- 高速化設定 ---
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
With ws
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
' --- 配列に読み込み(セルへの直接アクセスを減らす) ---
dataArr = .Range("A2:D" & lastRow).Value
' --- 配列上で値を加工(例:D列を1.1倍) ---
For i = 1 To UBound(dataArr, 1)
If IsNumeric(dataArr(i, 4)) Then
dataArr(i, 4) = dataArr(i, 4) * 1.1
End If
Next i
' --- 配列をシートに書き戻す ---
.Range("A2:D" & lastRow).Value = dataArr
' --- Withで書式設定をまとめて適用 ---
With .Range("A1:D" & lastRow)
.Borders.LineStyle = xlContinuous
.Borders.Weight = xlThin
End With
With .Range("D2:D" & lastRow)
.NumberFormat = "#,##0"
.Font.Color = RGB(0, 0, 0)
End With
' --- ヘッダー行 ---
With .Range("A1:D1")
.Font.Bold = True
.Interior.Color = RGB(0, 112, 192)
.Font.Color = RGB(255, 255, 255)
End With
.Columns("A:D").AutoFit
End With
' --- 高速化設定を戻す ---
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
MsgBox "処理完了(" & lastRow - 1 & "行)" & vbCrLf & _
"処理時間: " & Format(Timer - startTime, "0.00") & "秒", vbInformation
End Sub
配列の基本は 配列を使ってVBAの処理速度を10倍にする方法 で詳しく解説している。処理を部品化して管理したい場合は マクロから別のマクロを呼び出して処理を分割する方法 を参照。
—
Withなしとの比較(行数・速度)
コード行数の比較
| 項目 | Withなし | Withあり | 削減率 |
|---|---|---|---|
| オブジェクト参照の記述回数 | 操作の行数分 | 1回 | 大幅削減 |
| ヘッダー書式(8項目) | 8行(各行にフルパス) | 10行(With/End With含む) | 文字数約40%減 |
| ネスト構造(シート+範囲) | 各行にシート名+範囲 | 外側1回+内側1回 | 文字数約50%減 |
速度の比較(1万行 × 5プロパティ設定)
Sub SpeedComparison()
'============================================
' Withあり / なし の速度比較
' Sheet1に1万行のテストデータがある前提
'============================================
Dim ws As Worksheet
Dim i As Long
Dim t1 As Double, t2 As Double
Dim lastRow As Long
Set ws = Worksheets("Sheet1")
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Application.ScreenUpdating = False
' --- Withなし ---
t1 = Timer
For i = 2 To lastRow
ws.Cells(i, 1).Font.Bold = True
ws.Cells(i, 1).Font.Size = 11
ws.Cells(i, 1).Font.Name = "Meiryo UI"
ws.Cells(i, 1).Interior.Color = RGB(255, 255, 200)
ws.Cells(i, 1).Borders.LineStyle = xlContinuous
Next i
t1 = Timer - t1
' --- 書式をリセット ---
ws.Range("A2:A" & lastRow).ClearFormats
' --- Withあり ---
t2 = Timer
For i = 2 To lastRow
With ws.Cells(i, 1)
.Font.Bold = True
.Font.Size = 11
.Font.Name = "Meiryo UI"
.Interior.Color = RGB(255, 255, 200)
.Borders.LineStyle = xlContinuous
End With
Next i
t2 = Timer - t2
Application.ScreenUpdating = True
MsgBox "Withなし: " & Format(t1, "0.000") & "秒" & vbCrLf & _
"Withあり: " & Format(t2, "0.000") & "秒" & vbCrLf & _
"差: " & Format(t1 - t2, "0.000") & "秒", vbInformation
End Sub
補足: Withによる速度向上はオブジェクト参照の解決が1回で済むため。ただし、セル単位のループ自体が遅いため、本格的な高速化には配列との組み合わせが効果的。
—
落とし穴
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 1 | With の中で . を付け忘れてエラーになる |
ドットなしだと With のオブジェクトではなく別のオブジェクトを参照してしまう |
ブロック内のプロパティ・メソッドは 必ずドットで始める |
| 2 | ネストWithで外側のオブジェクトにアクセスできない | 内側の With が優先されるため、外側の . は使えない |
外側のオブジェクトを変数に入れておく。例: Set ws = Worksheets("Sheet1") |
| 3 | With の中で Set を使ってオブジェクトを変更しても反映されない |
With が参照するオブジェクトはブロック開始時に確定する。途中で変えても With の参照先は変わらない |
With ブロック内でオブジェクト自体を差し替えない。別の With ブロックを使う |
| 4 | End With を書き忘れてコンパイルエラーになる |
With と End With は必ずペア。片方が欠けるとエラー |
VBEの自動インデントを活用して対応を確認する。自分もこれで何度かコンパイルエラーを出して、5分くらい原因を探したことがある |
| 5 | With の中に GoTo で飛び込むと予期しない動作になる |
With ブロックの外から中にジャンプすると、オブジェクト参照が未設定のまま実行される |
GoTo で With ブロックの途中に飛び込まない。ブロックの外に飛び出すのはOK |
| 6 | Withを使いすぎてネストが深くなり、かえって読みにくくなる | 3段以上のネストは人間が追いにくい | ネストは 2段まで を目安にする。3段以上必要なら変数でオブジェクトを受けて分離する |
VBAでWith End Withがエラーになるときの対処法
「With〜End Withブロックでコンパイルエラーが出る」という場合、原因は End With の書き忘れか、With と End With の対応がずれていることがほとんどだ。ネストしている場合は特に見落としやすい。対策として、VBEのインデント機能を活用して With と End With の対応を視覚的に確認する。もう1つのよくある原因は、Withブロック内でドット(.)を付け忘れているケースだ。ドットなしで書くとWithのオブジェクトではなくグローバルなオブジェクトを参照してしまい、型不一致やオブジェクトが見つからないエラーになる。
VBAでオブジェクト変数がNothingになるエラーの対処法
「Withブロックの中で実行時エラー91『オブジェクト変数またはWith ブロック変数が設定されていません』が出る」という場合、原因はWithに渡したオブジェクトが Nothing(未設定)であることだ。たとえば Set ws = Nothing の状態で With ws を実行するとこのエラーが発生する。対策として、Withの前に If ws Is Nothing Then のチェックを入れて、オブジェクトが有効かどうかを確認する。シート名の指定ミスで Worksheets("存在しない名前") を代入しようとしてNothingになるパターンも多いので、シート名は事前に確認しておくこと。
—
FAQ
Q1. Withを使うと必ず速くなりますか?
A. オブジェクト参照の解決回数が減るため、同じオブジェクトに複数の操作をする場合は速くなる。ただし、1つのプロパティしか操作しない場合は効果がほぼない。目安として、同じオブジェクトに3つ以上の操作をするならWithで囲むのがよい。
Q2. WithとSetの違いは何ですか?
A. Set は変数にオブジェクトを代入する。With はオブジェクトを一時的に「省略できる状態」にする。Set ws = Worksheets("Sheet1") のあとに With ws と書くこともできるし、With Worksheets("Sheet1") と直接書くこともできる。使い分けとしては、複数箇所で使うならSet、1箇所でまとめて操作するならWith。
Q3. Withの中でメソッド(CopyやDeleteなど)も使えますか?
A. 使える。プロパティだけでなく、メソッドもドット付きで呼び出せる。
With Worksheets("Sheet1").Range("A1:D10")
.Copy
.ClearFormats
.Borders.LineStyle = xlNone
End With
Q4. With文はFor Eachループの中でも使えますか?
A. 使える。むしろループ内こそWithの効果が大きい。
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
With ws.Range("A1")
.Value = ws.Name
.Font.Bold = True
End With
Next ws
Q5. Withのネストは何段まで使えますか?
A. VBAの仕様上は制限がないが、実務では2段までを推奨する。3段以上になると読みにくく、バグの温床になる。自分は2段を超えたら変数に切り出すルールで落ち着いた。
—
まとめ
この記事では、With〜End With で 同じオブジェクトへの操作をまとめて書く方法 を解説した。
- 基本:
With オブジェクトで囲み、ブロック内は.で始める - 実務:ネストWithで階層構造を整理し、配列と組み合わせて高速化
- 注意:ネストは2段まで、ブロック内には必ずドットを付ける
Withは「同じオブジェクトに3回以上アクセスするならとりあえず囲む」というルールで始めるとよい。自分の経験では、Withを使い始めてからコードの文字数が平均で30〜40%減り、修正時の「どこを直せばいいか分からない」問題がほぼなくなった。特にセルの書式設定はプロパティを5つ以上触ることが多いので、Withとの相性が抜群だ。速度面の効果はデータ量が少ないうちは体感しにくいが、1万行を超えるあたりから明確に差が出る。まずは基本コードで書式設定をまとめる感覚をつかみ、次にネストWithで階層構造を整理する流れで進めるのがおすすめだ。
バックアップの推奨: 書式設定は元に戻しにくい操作が含まれるため、マクロ実行前にファイルのコピーを取っておくことを推奨する。
—
前提条件
| 項目 | 内容 |
|---|---|
| Excel版 | Excel 2016以降 / Microsoft 365 |
| OS | Windows 10 / 11 |
| 保存形式 | .xlsm(マクロ有効ブック) |
| 貼り付け場所 | 標準モジュール |
| 実行方法 | F5 キー、またはマクロ実行 |
—
次にやりたくなること
- 処理速度をもっと上げたい → 画面更新・再計算を止めてマクロを高速化する方法 で、ScreenUpdating や Calculation の制御を覚えれば、Withとの組み合わせで大幅に速くなる。
- セルの色や罫線をもっと自由に操作したい → セルの背景色・文字色をRGBで自由に操作する方法 で、RGBの指定方法を確認できる。
- コードが長くなってきたので分割したい → マクロから別のマクロを呼び出して処理を分割する方法 で、Sub/Functionによるモジュール分割を学べる。
- 条件によって書式を変えたい → Select Caseで複数条件の分岐をスッキリ書く方法 で、Withブロック内に条件分岐を組み込むパターンを学べる。
- 最終行の取得をもっと正確にしたい → データの最終行・最終列を正確に取得する方法 で、With内で使う範囲指定の精度を上げられる。
—
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "VBAのWithを使うと必ず速くなりますか?",
"acceptedAnswer": {
"@type": "Answer",
"text": "同じオブジェクトに複数の操作をする場合はオブジェクト参照の解決回数が減るため速くなります。目安として、同じオブジェクトに3つ以上の操作をするならWithで囲むのが効果的です。"
}
},
{
"@type": "Question",
"name": "VBAのWithとSetの違いは何ですか?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Setは変数にオブジェクトを代入します。Withはオブジェクトを一時的に省略できる状態にします。複数箇所で使うならSet、1箇所でまとめて操作するならWithが適しています。"
}
},
{
"@type": "Question",
"name": "VBAのWith文の中でメソッド(CopyやDeleteなど)も使えますか?",
"acceptedAnswer": {
"@type": "Answer",
"text": "使えます。プロパティだけでなく、メソッドもドット付きで呼び出せます。例:With Range(\"A1:D10\") の中で .Copy や .ClearFormats が使えます。"
}
},
{
"@type": "Question",
"name": "VBAのWith文はFor Eachループの中でも使えますか?",
"acceptedAnswer": {
"@type": "Answer",
"text": "使えます。むしろループ内こそWithの効果が大きく、各ループでオブジェクト参照の解決が1回で済むため、速度面でもメリットがあります。"
}
},
{
"@type": "Question",
"name": "VBAのWithのネストは何段まで使えますか?",
"acceptedAnswer": {
"@type": "Answer",
"text": "VBAの仕様上は制限がありませんが、実務では2段までを推奨します。3段以上になると読みにくく、バグの温床になるため、変数に切り出すのが安全です。"
}
}
]
}


コメント