目次
はじめに
リストの項目を追加するたびに、毎回ソートボタンを押すのは面倒ですよね。今回紹介するコードを使うと、A列のどこかを編集したタイミングで自動的にリスト全体を並び替えることができます。作業の手間を大幅に減らせる便利なTipsです。
イベントプロシージャとは
イベントプロシージャは、シート上で何か操作が行われたときに自動的に実行されるコードです。Worksheet_Changeは、セルの値が変更されたときに動作するイベントプロシージャの一つで、シートモジュールに記述する必要があります。
シートモジュールへのアクセス方法は以下の通りです。
標準モジュールではなくシートモジュールに記述する点が重要なポイントです。
コードの全体像
Private Sub Worksheet_Change(ByVal Target As Range)
' A列が変更されたときのみ実行
If Not Intersect(Target, Me.Columns("A")) Is Nothing Then
' 空でないセルがある範囲を昇順に並び替え
With Me.Sort
.SortFields.Clear
.SortFields.Add Key:= _
Range("A1", Range("A" & Rows.Count).End(xlUp)), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
.SetRange Range("A1", Range("A" & Rows.Count).End(xlUp))
.Header = xlNo
.Apply
End With
End If
End Sub
このコードは、A列のどのセルが変更されても、A列全体のデータを昇順に自動ソートする処理を行います。
A列の変更を検知する仕組み
【Targetパラメータの役割】
Worksheet_Changeイベントには、Targetというパラメータが渡されます。これは「どのセルが変更されたか」を表すRangeオブジェクトです。
Private Sub Worksheet_Change(ByVal Target As Range)
ユーザーがA5セルを変更した場合、TargetにはA5セルの情報が入ります。複数のセルをまとめて変更した場合は、その範囲全体がTargetに格納される仕組みです。
【Intersectメソッドで列全体をチェック】
Intersectメソッドは、2つの範囲が重なっているかを判定する関数です。
If Not Intersect(Target, Me.Columns("A")) Is Nothing Then
Me.Columns("A")は、A列全体を表します。この条件式は以下のように動作します。
-
Intersect(Target, Me.Columns("A"))で、変更されたセルとA列が重なっているか確認 - 重なっていれば
Rangeオブジェクトが返され、重なっていなければNothingが返される -
Is NothingでNothingかどうかを判定 -
Notで条件を反転させることで、「重なっている場合」を検知
つまり、「変更されたセルがA列に含まれていたら処理を実行する」という意味になります。A1セルでもA10セルでも、A列のどこを変更しても動作する仕組みです。
Columns("A")は列全体を指すため、A1からA1048576までの全セルが対象になります。どの行を変更してもソートが実行される点が、特定セル指定との大きな違いです。
自動ソート処理の実装
【Sortオブジェクトの基本構造】
VBAでソートを実行するには、Sortオブジェクトを使用します。With文を使うことで、何度も同じオブジェクト名を書かずに済むため、コードが読みやすくなります。
With Me.Sort
' ソート設定をここに記述
End With
Meは、コードが書かれているシート自体を指すキーワードです。
【SortFieldsの設定】
ソートの基準となる列や順序を指定するのがSortFieldsです。
.SortFields.Clear
.SortFields.Add key:=Range("A1", Range("A" & Rows.Count).End(xlUp)), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
SortFields.Clearで既存のソート設定をクリアしてから、Addメソッドで新しい設定を追加します。
各パラメータの意味は以下の通りです。
-
key: ソートの基準となる範囲。ここではA1セルからデータの最終行まで -
SortOn: 何を基準にソートするか。xlSortOnValuesは値を基準にする設定 -
Order: 昇順(xlAscending)か降順(xlDescending)か -
DataOption: 数値と文字列の扱い方。xlSortNormalは標準的な並び替え
【データの最終行を取得する方法】
Range("A" & Rows.Count).End(xlUp)
この式は、A列の最下行から上方向に検索して、最初にデータが入っているセルを見つける処理です。Rows.Countはシートの最大行数(通常は1048576)を表します。
Ctrl+↑キーを押したときと同じ動作をコードで表現していると考えると分かりやすいかもしれません。
【ソート範囲とヘッダー設定】
.SetRange Range("A1", Range("A" & Rows.Count).End(xlUp))
.Header = xlNo
SetRangeでソートを適用する範囲全体を指定します。A1セルから最終行までを対象にしています。
Header = xlNoは、1行目がヘッダー(見出し)ではないことを示す設定です。もし1行目を見出しとして固定したい場合は、xlYesに変更するとA2セル以降だけがソートされます。
【ソートの実行】
.Apply
最後にApplyメソッドを呼び出すことで、設定した内容が実際に適用されます。
実際に動かしてみる
動作確認の手順は以下の通りです。
-
Excelで新しいブックを開く
-
Sheet1のタブを右クリックして「コードの表示」を選択
-
開いたウィンドウに上記のコードを貼り付ける
-
Excelに戻り、A列に以下のようなデータを貼りつける
りんご みかん いちご バナナ -
自動的に昇順にソートされる
A1、A2、A3、A4のどのセルを変更しても同じようにソートが実行されます。試しにA10セルに新しい項目を追加してみてください。入力後すぐに適切な位置に並び替えられることが確認できます。
B列やC列を編集してもソートは実行されません。A列以外を変更しても何も起こらないことを確認してみてください。
応用例
【定数を使って列を簡単に変更できるようにする】
コード内に直接「A」と書かれている部分(マジックナンバー)を定数として定義すると、後から変更しやすくなります。
Const TARGET_COLUMN As String = "A"
Private Sub Worksheet_Change(ByVal Target As Range)
' 指定列が変更されたときのみ実行
If Not Intersect(Target, Me.Columns(TARGET_COLUMN)) Is Nothing Then
With Me.Sort
.SortFields.Clear
.SortFields.Add Key:= _
Range(TARGET_COLUMN & "1", Range(TARGET_COLUMN & Rows.Count).End(xlUp)), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
.SetRange Range(TARGET_COLUMN & "1", Range(TARGET_COLUMN & Rows.Count).End(xlUp))
.Header = xlNo
.Apply
End With
End If
End Sub
Const TARGET_COLUMN As String = "A"という行で、対象となる列を定義しています。B列に変更したい場合は、この1行を以下のように書き換えるだけで済みます。
Const TARGET_COLUMN As String = "B"
コード内の複数箇所を修正する必要がなくなるため、メンテナンスがしやすくなります。
【ソート順も定数で管理する】
昇順・降順の切り替えも定数で管理できます。
Const TARGET_COLUMN As String = "A"
Const SORT_ORDER As Long = xlAscending
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Columns(TARGET_COLUMN)) Is Nothing Then
With Me.Sort
.SortFields.Clear
.SortFields.Add Key:= _
Range(TARGET_COLUMN & "1", Range(TARGET_COLUMN & Rows.Count).End(xlUp)), _
SortOn:=xlSortOnValues, _
Order:=SORT_ORDER, _
DataOption:=xlSortNormal
.SetRange Range(TARGET_COLUMN & "1", Range(TARGET_COLUMN & Rows.Count).End(xlUp))
.Header = xlNo
.Apply
End With
End If
End Sub
降順にしたい場合は、SORT_ORDERの値をxlDescendingに変更します。
【ヘッダー設定も定数化する】
ヘッダーの有無も定数で管理すると、設定の見通しが良くなります。
Const TARGET_COLUMN As String = "A"
Const SORT_ORDER As Long = xlAscending
Const HAS_HEADER As Long = xlNo
Const START_ROW As Long = 1
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Columns(TARGET_COLUMN)) Is Nothing Then
With Me.Sort
.SortFields.Clear
.SortFields.Add Key:= _
Range(TARGET_COLUMN & START_ROW, Range(TARGET_COLUMN & Rows.Count).End(xlUp)), _
SortOn:=xlSortOnValues, _
Order:=SORT_ORDER, _
DataOption:=xlSortNormal
.SetRange Range(TARGET_COLUMN & START_ROW, Range(TARGET_COLUMN & Rows.Count).End(xlUp))
.Header = HAS_HEADER
.Apply
End With
End If
End Sub
ヘッダー行がある場合は、HAS_HEADERをxlYesに変更します。
Const HAS_HEADER As Long = xlYes
これで2行目以降がソート対象となり、1行目は固定されます。
【複数列のデータを同時にソートする】
A列を基準にして、B列やC列のデータも一緒に並び替えたい場合は、終了列の定数を追加します。
Const TARGET_COLUMN As String = "A"
Const END_COLUMN As String = "C"
Const SORT_ORDER As Long = xlAscending
Const HAS_HEADER As Long = xlNo
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Columns(TARGET_COLUMN)) Is Nothing Then
With Me.Sort
.SortFields.Clear
.SortFields.Add Key:= _
Range(TARGET_COLUMN & "1", Range(TARGET_COLUMN & Rows.Count).End(xlUp)), _
SortOn:=xlSortOnValues, _
Order:=SORT_ORDER, _
DataOption:=xlSortNormal
.SetRange Range(TARGET_COLUMN & "1:" & END_COLUMN & Rows.Count)
.Header = HAS_HEADER
.Apply
End With
End If
End Sub
END_COLUMNを変更することで、ソート範囲を簡単に調整できます。例えばE列まで含めたい場合は以下のようにします。
Const END_COLUMN As String = "E"
定数を使うメリットは、コードの可読性(読みやすさ)が向上することと、設定変更時の修正漏れを防げることです。特に複数箇所で同じ値を使う場合は、定数化することをおすすめします。
まとめ
A列の変更をトリガーにした自動ソート機能を実装する方法を紹介しました。Worksheet_ChangeイベントとIntersectメソッド、Columnsプロパティを組み合わせることで、リストの更新作業を効率化できます。
