ロックされていないセルをすべて選択/クリアする方法を解説します。
ユーザーが編集できるセル以外をロックしているシートなどにおいて、
編集可能セルを一括取得するときなどに使用するコードです。
この手の操作に便利なSpecialCellsメソッドには、
残念ながら該当の機能がありませんので、
For Eachステートメントで地道に取得していきます。
ロックされていないセルをすべて選択する
ロックされていないセルをすべて選択するには、以下のコードを実行します。
Sub ロックされていないセルをすべて選択する() ' 目標のRangeオブジェクト Dim cells非ロックセル集 As Range ' 対象セル範囲のセルをすべて判定 Dim cell As Range For Each cell In Worksheets("○○").UsedRange ' ロックされていなければ目的のRange変数に追加 If cell.Locked = False Then If cells非ロックセル集 Is Nothing Then Set cells非ロックセル集 = cell Else Set cells非ロックセル集 = Union(cells非ロックセル集, cell) End If End If Next ' 目的のRangeオブジェクトを選択 If Not cells非ロックセル集 Is Nothing Then Worksheets("○○").Activate cells非ロックセル集.Select End If End Sub
非ロックセルを一括取得するプロパティはありませんので、
For Eachステートメントですべてのセルを走査し、
LockedプロパティがFalseのセルを取得していきます。
サンプルでは「Worksheets("○○").UsedRange」へFor Eachを回していますが、
これの範囲を絞れば、「特定範囲内の非ロックセル」の選択ができます。
For Each cell In Worksheets("○○").Range("A1:A10") ' ↑A1:A10内の非ロックセルを選択する場合はこう書き換える
コードを書く上での注意点ですが、
Unionメソッドは引数がNothingだとエラーになるイマイチ仕様があるため、
最初の1セル目はUnionではなくただのSetで行う必要があることにご注意ください。
あとは細かいですが、最後の選択時に、
- ひとつも非ロックセルがない(目標のRnageオブジェクトが空)
- 対象シートがアクティブでない
ことによるエラーにもお気を付けください。
このコードは何かのマクロに組み込むというよりは、
個人用マクロブックなどに書いておき、
リボンから実行できるようにすると便利なマクロです。
編集可能なセルを一目で確認するときにも使えますし、
実行後にDeleteキーを押して一括クリアをしたり、
非ロックセルの書式を一括で設定することもできます。
個人用マクロブックの使い方はこちらをどうぞ。
www.limecode.jp
ロックされていないセルをすべてクリアする
続いてロックされていないセルの一括クリアです。
ほぼ同じコードですが、こちらを実行します。
Sub ロックされていないセルをすべてクリアする() Dim cells非ロックセル集 As Range Dim cell As Range For Each cell In Worksheets("○○").UsedRange If cell.Locked = False Then If cells非ロックセル集 Is Nothing Then Set cells非ロックセル集 = cell Else Set cells非ロックセル集 = Union(cells非ロックセル集, cell) End If End If Next If Not cells非ロックセル集 Is Nothing Then cells非ロックセル集.Value = "" End Sub
選択するバージョンとほぼ同じコードなので解説は省略します。
最後の「cells非ロックセル集.Value = ""」を、
「cells非ロックセル集.ClearContents」とやりたくなりますが、
ClearContentsメソッドは結合セルでエラーになります。
ロック/非ロックが混在するシートは帳票や入力フォームが多く、
結合された入力欄もたくさんあるため、""の代入で対応します。
非ロックセルの取得部分を関数化
上記の2コードはほとんど同じコードで書いています。
最後が「選択」か「クリア」かだけの違いなので当然ですね。
こういったコードは、最終目的の一歩手前である、
「非ロックセル集の取得」部分を関数(Function)にしておくと、
綺麗にコードを整理することができます。
そのFunctionと実行例がこちら↓
' 非ロックセル集をRangeオブジェクトで返す関数 Function GetCells非ロックセル(対象セル範囲 As Range) As Range Dim range結果値 As Range Dim cell As Range For Each cell In 対象セル範囲.Cells If cell.Locked = False Then If range結果値 Is Nothing Then Set range結果値 = cell Else Set range結果値 = Union(range結果値, cell) End If End If Next Set GetCells非ロックセル = range結果値 End Function ' 実行例 Sub ロックされていないセルをすべてクリアする() GetCells非ロックセル(Worksheets("○○").UsedRange).Value = "" End Sub Sub ロックされていないセルをすべて選択する() Dim rng As Range Set rng = GetCells非ロックセル(Worksheets("○○").UsedRange) If Not rng Is Nothing Then rng.Worksheet.Activate rng.Select End If End Sub
だいぶコードがスッキリしたのがわかります。
特にエラー判定がOn Error Resume Nextで済む「クリア」のコードは、
2行で済むようになっていますね。
このようにセル範囲やセルのリストに対して何かを処理するコードは、
Rangeオブジェクトを取得するところまでをFunctionにしておくことで、
似た処理を簡単に書けるようになります。
Rangeを返す関数はとても使い勝手がいいので、
積極的に関数化して、自作関数集に加えておきましょう。
自作関数の作り方についてはこちらをご覧ください。
おまけ:Unionメソッドのイマイチな仕様
汎用関数でコードをスッキリさせたいとなったとき、
非ロックセルの取得以前に、気になる部分があります。
それがここ↓
If range結果値 Is Nothing Then Set range結果値 = cell Else Set range結果値 = Union(range結果値, cell) End If
この部分は、
Set range結果値 = Union(range結果値, cell)
これだけで動いてくれればいいのですが、
UnionさんはNothingを受け付けてくれないため、
最初のセルを追加するためのIF文を書かなければいけません。
セル同士を足す、いわば「Rangeの足し算」を定義した関数のくせに、
0 + 1 を定義しないとはいかがなものか。
和集合(A ⋃ B)の「⋃」ってUnionの頭文字なのに、
和集合の定理(A ⋃ ∅ = A)を満たさずUnionを名乗っていいものなのか。
と、文句を言っても仕方ないので、真のUnionを作ってしまいましょう。
その関数がこちら↓
Function UnionEx(Arg1 As Range, Arg2 As Range) As Range If Arg1 Is Nothing Then Set UnionEx = Arg2 ElseIf Arg2 Is Nothing Then Set UnionEx = Arg1 Else Set UnionEx = Union(Arg1, Arg2) End If End Function
これで完成です。
UnionがNothingも受け取れるように、
If文も内包した新しいUnionを作りました。
このように関数を関数で包んで使いやすくすることを、
「関数をラップする」「ラッパー関数を作る」などと表現します。
この関数を作ったことで、
非ロックセルの取得関数は↓のように書けるようになります。
Function GetCells非ロックセル(対象セル範囲 As Range) As Range Dim range結果値 As Range Dim cell As Range For Each cell In 対象セル範囲.Cells If cell.Locked = False Then Set range結果値 = UnionEx(range結果値, cell) Next Set GetCells非ロックセル = range結果値 End Function
コード量が激減しましたね。
Unionが1行で済むようになったため、
Lockedを判定するIf文も1行表現形式で書けるようになりました。
ここまで短ければ結果用の変数もなくして良さそうなので、
Function GetCells非ロックセル(対象セル範囲 As Range) As Range Dim cell As Range For Each cell In 対象セル範囲.Cells If cell.Locked = False Then Set GetCells非ロックセル = UnionEx(GetCells非ロックセル, cell) Next End Function
これでもよいかもしれません。
このように、関数は複雑な処理の分割を行うためではなく、
ちょっとしためんどくさいコードを省略するためにも使えます。
簡単で便利というのはFunctionの勉強にももってこいですので、
ぜひ作成してみてください。
なお、真のUnionと言っておきながら、
このUnionExには引数が2つしかありません。
本家Unionに負けないよう、引数を任意の数とれるようにするには、
ParamArrayキーワードを使うと実現できます。
気が向いたら挑戦してみてください。
www.limecode.jp