和風スパゲティのレシピ

日本語でコーディングするExcelVBA

ロックされていないセルをすべて選択/クリアする

ロックされていないセルをすべて選択/クリアする方法を解説します。

ユーザーが編集できるセル以外をロックしているシートなどにおいて、
編集可能セルを一括取得するときなどに使用するコードです。


この手の操作に便利な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を返す関数はとても使い勝手がいいので、
積極的に関数化して、自作関数集に加えておきましょう。

自作関数の作り方についてはこちらをご覧ください。

www.limecode.jp

おまけ: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