[[20210303173242]] 『データ入力後に特定のセルをロックするマクロ』(お正月) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『データ入力後に特定のセルをロックするマクロ』(お正月)

お尋ねします。
以下のコードは、ネットから参照(https://ja.extendoffice.com/documents/excel/3778-excel-lock-cell-after-data-entry-input.html)したものです。
「Excelでのデータ入力または入力後にセルをロックまたは保護するにはどうすればよいですか?」というもので設定したのですが、
特定のセル=B12:B2011にリストで選択して入力すると、そのセルにロックをかけたいのですが、入力してもロックはかからず、入力したデータを消すとそこで初めてロックがかかってくれます。
これを入力した時点でロックがかかるようにするには、どのように修正したらよいでしょうか?

どうかよろしくお願いいたします。

Dim mRg As Range
Dim mStr As String

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Intersect(Range("B12:B2011"), Target) Is Nothing Then

    Set mRg = Target.Item(1)
    mStr = mRg.Value
End If
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
    Dim xRg As Range
    On Error Resume Next
    Set xRg = Intersect(Range("B12:B2011"), Target)
    If xRg Is Nothing Then Exit Sub
    Target.Worksheet.Unprotect Password:="12345"
    If xRg.Value <> mStr Then xRg.Locked = True
    Target.Worksheet.Protect Password:="12345"
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Intersect(Range("B12:B2011"), Target) Is Nothing Then
    Set mRg = Target.Item(1)
     mStr = mRg.Value
End If
End Sub

< 使用 Excel:Excel2019、使用 OS:Windows10 >


マクロの改造を試みるなら、まずは現状が何をやっているのか理解しなければなりませんから、まずは【ブレークポイント】を設定して【ステップ実行】してみて何をやっているのか研究してみてはどうでしょうか?

    Dim mRg As Range
    Dim mStr As String
    '-----------------------------------------------------------
    Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
        If Not Intersect(Range("B12:B2011"), Target) Is Nothing Then
            Set mRg = Target.Item(1)
            mStr = mRg.Value
        End If
    End Sub
    '-----------------------------------------------------------
    Private Sub Worksheet_Change(ByVal Target As Range)
        Dim xRg As Range
        On Error Resume Next
        Set xRg = Intersect(Range("B12:B2011"), Target)
        If xRg Is Nothing Then Exit Sub

        Target.Worksheet.Unprotect Password:="12345"
        If xRg.Value <> mStr Then xRg.Locked = True  '←ココに注目
        Target.Worksheet.Protect Password:="12345"

    End Sub
     '----------------------------------------------------------
    Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    If Not Intersect(Range("B12:B2011"), Target) Is Nothing Then
        Set mRg = Target.Item(1)
         mStr = mRg.Value
    End If
    End Sub

ちなみに仰ること"だけ”実現するなら、もっとシンプルに出来ますよ。

 (1) ブックを開いたときに、1回だけシートの保護を解除してから、ユーザーの操作のみ保護の対象にしなおす。
 (2) そのシートでセルの値が書き換えられたら、該当セルをロック状態にする

↑の2点だけ考えればよいので・・・

(もこな2) 2021/03/03(水) 19:58


 >1.最初にこの範囲のロックを解除し、セルを選択して右クリックしてから、 ・・・

 最初に書かれている準備を済ませてから、コードを貼り付けましたか?

(半平太) 2021/03/03(水) 20:17


もこな2さん、ココに注目部分の意味は、データが入力されたらロックするということでしょうか?
だとすると、現状は逆の動きをしているように思いますが。。。

半平太さん、1の説明どおり、範囲指定して保護のチェックを外しているのですが。。。
(お正月) 2021/03/03(水) 20:45


 >そのセルにロックをかけたいのですが、入力してもロックはかからず、
 >入力したデータを消すとそこで初めてロックがかかってくれます。

 何か変だなと思いながら、そこを簡単に読み飛ばしてしまった。

 >1.最初にこの範囲のロックを解除し、セルを選択して右クリックしてから、 ・・・下のマクロを実行してから、
      ↑
 試しに上の準備はやらないで、代わりに下記マクロを実行してから、テストしてみてください。

 Sub init()
     Me.Unprotect Password:="12345"
     Range("B12:B2011").Locked = True
     Range("B12:B2011").SpecialCells(xlCellTypeBlanks).Locked = False
     Me.Protect Password:="12345"
 End Sub

(半平太) 2021/03/03(水) 22:37


繰り返しになりますが、分からないものを改造はできないですからちゃん【理解】してからにしたほうがよいですよ。

>もこな2さん、ココに注目部分の意味は、データが入力されたらロックするということでしょうか?
注目としたのは【どのような条件で】ロックするか判定している部分です。

>現状は逆の動きをしているように思いますが。。。
なんの逆だと言っているは理解しかねますが、逆ならさらにその逆にすればよろしいのでは?

ちなみに私ならこんな感じにします。
(理解していただきたいので、何も考えずにコピペして完成というのはNGとします)
【ThisWorkbookモジュール】

    Private Sub Workbook_Open()
        With Worksheets("Sheet1")
            .Unprotect Password:="12345"
            .Protect UserInterfaceOnly:=True, Password:="12345"
        End With
    End Sub

【Sheet1のモジュール】

    Private Sub Worksheet_Change(ByVal Target As Range)
        Dim xRg As Range, MyRNG As Range

        Set xRg = Intersect(Range("B12:B2011"), Target)
        If Not xRg Is Nothing Then
            For Each MyRNG In xRg
                If MyRNG.Value <> "" Then
                    MyRNG.Locked = True  'セルの値が""以外ならロックする
                Else
                    MyRNG.Locked = False 'セルの値が""ならロックを解除する(BeforeDoubleClickイベントでセルの値がクリアされるとこれが作動)
                End If
            Next MyRNG
        End If

    End Sub
    '-----------------------------------------------------------------
    Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
        '▼B12〜B2011でダブルクリックをしたときはそのセルの値をクリアする
        ' (UserInterfaceOnly:=True で保護した場合、ロックされているセルであってもマクロからであれば操作可能)
        If Not Intersect(Range("B12:B2011"), Target) Is Nothing Then
            Cancel = True
            Target.ClearContents
        End If
    End Sub

(もこな2 ) 2021/03/03(水) 23:41


おはようございます。
半平太さん、もこな2さん、ありがとうございます。
まず、半平太さんにお尋ねします。
これは、もともとのコードを消して置き換えるのでしょうか?
(お正月) 2021/03/04(木) 07:54

 >もともとのコードを消して置き換えるのでしょうか?

 元のコードは消す必要ないです。

 シートモジュールに追加で貼り付けて、1回だけ実行です。
 (準備作業の代わりとなるものです)

(半平太) 2021/03/04(木) 09:54


半平太さん、ありがとうございます。
ご指示どおりシートモジュールに追加・実行して、対象セルにリストから選択してもロックはかからず、
それを消すとそこでロックがかかります。
希望としては、リストから選択したタイミングでロックがかかってほしいのですが…
(お正月) 2021/03/04(木) 10:12

 ちょっと分からないです。

 >対象セルにリストから選択してもロックはかからず、
 リストから選択できたんですね。 
 それは当然ですよね。じゃないと、何も入力出来ないので。。

 選択できた段階で(=データが入力できた段階で)、ロックが掛かるハズです。

 そこで、もしロックが掛かってなければ、別のデータを入れられることになりますが、
 その現象が生じていると言うことなんですか?

 >それを消すとそこでロックがかかります。
 消すとはどう言うことを意図しているのですか?
 上の流れからすると、消すことも出来ないハズですが、消すことまでは出来てしまったんですか?

(半平太) 2021/03/04(木) 17:54


どうも、ご自身でステップ実行して解明する気はなさそうですが。
>B12:B2011にリストで選択して入力すると〜
って言っているので、多分B列には【入力規則】のリストが設定されているんですよね。たぶん
そうすると、例えばブランク状態のB13セルに入力規則をつかって、値を設定しようとすると
 (1)B13セルを選択した時点で、SelectionChangeイベントが発生して【mStr】に「""」が格納される
 (2)B13セルの値を変えたことによりChangeイベントが発生して【mStr】に格納されている「""」とB13セルの値を比較 → 異なっているからロックする

という流れになってるのは直ぐに理解できると思います。

で、提示のものをこちらで試すとロックがちゃんとかかる(入力規則のリストは出てくるものの選択して値を書き換えようとするとはじかれる)ので、何か説明してないことが他にあったりしませんかね。
(例えば入力規則が設定されている(値が書き換えられている)のはA列でB列にはVlookup関数で表示させてるだけとか・・)

(もこな2 ) 2021/03/04(木) 18:53


半平太さん、もこな2さん、ありがとうございます。
もこな2さんのご指摘である、解明する気はない、ということは決してありませんので、ご理解ください。

お二人からの質問にご返事させていただきます。
半平太さんへ

選択できた段階で(=データが入力できた段階で)、ロックが掛かるハズです。 選択出来た段階でロックが掛からないのです。
そこで、もしロックが掛かってなければ、別のデータを入れられることになりますが、
 その現象が生じていると言うことなんですか?
はい、そのとおりです。
消すとはどう言うことを意図しているのですか? リストから選択してデータをDeleteキーで消すということです。
上の流れからすると、消すことも出来ないハズですが、消すことまでは出来てしまったんですか? はい。消すことができます。消すとロックがそこではじめて掛かります。

もこな2さんへ

多分B列には【入力規則】のリストが設定されているんですよね。 はい。リストとは言っても「許可」という文字列一つだけです。
〜流れになってるのは直ぐに理解できると思います。 すみません。よくは理解できていません。
何か説明してないことが他にあったりしませんかね。 いいえ、単純に入力規則のリスト一つだけで、関数など設定していません。B列の許可を選択することで、C列に日付が入るようにはしています。

補足説明です。
理想としましては、B列に許可という文字をセルにリストで入力することで、C列にいわゆる許可の日付が入るという形にしたいのです。つまり、このB列は上司だけが扱うセルにしたいので、本当は、リストを選択する時点でロックを掛けておいて、セルをクリックすることで、解除パスワードが要求されて、そのパスワードを入力したら、リストが選択可能になり、セルに許可が入力されると再びロックが掛かるという流れに出来たらといのうが理想です。要は、部下にこのB列を扱わせないようにしたいのです。シートのパスワードではなく、セル(B列)のみにパスワードをかけるようにできないでしょうか?もちろん、シート自体にも保護はかけています。必要な箇所のみを入力させるようにしたいので。

説明がきちんと出来ておらず、すみません。
今一度、半平太さん、もこな2さん、どうぞよろしくお願いいたします。

(お正月) 2021/03/04(木) 19:53


 こちらでは、当該現象が再現しないですねぇ。

 これ以上考えられないので、私は降ります。 m(__)m

(半平太) 2021/03/04(木) 20:31


■1
>セル(B列)のみにパスワードをかけるようにできないでしょうか?
断言しませんが多分できません。
(非表示にしてから保護すれば、再表示できないということくらいはできるかもですが・・・)

■2
>すみません。よくは理解できていません。
うーん。どこらへんが理解できないですか?
ブレークポイントを設定してステップ実行すれば、どの操作で何が実行されているか丸わかりだとおもうんですけど・・・・

■3
多分B列には【入力規則】のリストが設定されているんですよね。
↑あってますか?

■4
「■3」が合ってるとして、リストを選択した"後"にChangeイベント発生してますか?

■5
「■4」の答えがYESだとして、「If xRg.Value <> mStr」の判定が真となっていて「xRg.Locked = True」が実行されてますか?

■6
2021/03/03(水) 23:41 に提示したものは試してみましたか?
試したなら結果を教えてください。

(もこな2 ) 2021/03/05(金) 00:05


■7
以下にそっくり入れ替えてから操作すると、イミディエイトになんと出力されますか?
    Dim mRg As Range
    Dim mStr As String
    '-----------------------------------------------------------
    Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
        If Not Intersect(Range("B12:B2011"), Target) Is Nothing Then
            Set mRg = Target.Item(1)
            Debug.Print "「mRg」に【" & mRg.Address(0, 0) & "】を格納した"
            mStr = mRg.Value
            Debug.Print "「mStr」に【" & mStr & "】を格納した" & vbLf
        End If
    End Sub
    '-----------------------------------------------------------
    Private Sub Worksheet_Change(ByVal Target As Range)
        Debug.Print Target.Address(0, 0) & " の書き換えによりChangeイベントが発生した"
        Dim xRg As Range
        On Error Resume Next
        Set xRg = Intersect(Range("B12:B2011"), Target)
        If xRg Is Nothing Then
            Debug.Print "範囲外のため処理を中止した"
            Exit Sub
        Else
            Debug.Print xRg.Address(0, 0) & " の処理を開始する"
            Target.Worksheet.Unprotect
            Debug.Print "【" & xRg.Value & "】と【" & mStr & "】を比較"
            If xRg.Value <> mStr Then
                Debug.Print "判定結果は「真」→" & xRg.Address(0, 0) & "のロックを実行する"
                xRg.Locked = True
            Else
                Debug.Print "判定結果は「偽」→ロックはしない"
            End If
            Target.Worksheet.Protect
        End If
        Debug.Print "Changeイベントの処理が完了した" & vbLf & vbLf
    End Sub
     '----------------------------------------------------------
    Private Sub Worksheet_SelectionChange(ByVal Target As Range)
   If Not Intersect(Range("B12:B2011"), Target) Is Nothing Then
        Set mRg = Target.Item(1)
        Debug.Print "「mRg」に【" & mRg.Address(0, 0) & "】を格納した"
         mStr = mRg.Value
         Debug.Print "「mStr」に【" & mStr & "】を格納した" & vbLf
    End If
    End Sub

(もこな2 ) 2021/03/05(金) 00:35


もこな2さん、ありがとうございます。
以下のように確認事項について回答します。

■1
>断言しませんが多分できません。
はい、わかりました。

■2
>うーん。どこらへんが理解できないですか?
ブレークポイントを設定してステップ実行すれば、どの操作で何が実行されているか丸わかりだとおもうんですけど・・・・
少しわかってきました。勉強します。

■3
>多分B列には【入力規則】のリストが設定されているんですよね。
↑あってますか?
はい、そのとおりです。

■4
>「■3」が合ってるとして、リストを選択した"後"にChangeイベント発生してますか?
発生していないと思います。

■5
>「■4」の答えがYESだとして、「If xRg.Value <> mStr」の判定が真となっていて「xRg.Locked = True」が実行されてますか?
「■4」はNOです。

■6
>2021/03/03(水) 23:41 に提示したものは試してみましたか?
試したなら結果を教えてください。
(もこな2 ) 2021/03/05(金) 00:05
以下のエラーとなります。
実行時エラー1004 RangeクラスのLockedプロパティを設定できません。
MyRNG.Locked = True 'セルの値が""以外ならロックする

■7
>以下にそっくり入れ替えてから操作すると、イミディエイトになんと出力されますか?

    Dim mRg As Range
すみません。イミディエイトの出力はどこを確認すればよいのかわかりません。
もこな2さんがお示しいただいたこのマクロを試したところ、リストを選択したらロックが掛かりました。ただ、シート自体にはロックをかけない状態で、Bセルの書式設定の保護のチェックを入れておくことが条件ですよね。
私の希望は、もともとシートにはパスワード設定(12345)してロックをかけておき、リストが入っているB列は、保護のチェックを外しておき、リスト選択後にロックをかけたいのです。

(お正月) 2021/03/05(金) 07:33


■8
>発生していないと思います。
"思う"じゃなくてちゃんと確認してください。ここが肝心かなめです

■9
>実行時エラー1004
説明してなかったですが、一旦ブックを開きなおしてくださいね。
(それか「Workbook_Open」を手動で実行してください)
それでも発生してしまいますか?

■10
>イミディエイトの出力はどこを確認すればよいのかわかりません。
↓を読んでみてください

 【ステップ実行】
https://www.239-programing.com/excel-vba/basic/basic023.html
http://plus1excel.web.fc2.com/learning/l301/t405.html

 【イミディエイトウィンドウ】
https://www.239-programing.com/excel-vba/basic/basic024.html
https://excel-ubara.com/excelvba1/EXCELVBA486.html

 【ローカルウィンドウ】
https://excel-ubara.com/excelvba4/EXCEL266.html
http://excelvba.pc-users.net/fol8/8_2.html

■11
>シート自体にはロックをかけない状態で、Bセルの書式設定の保護のチェックを入れておくことが条件ですよね。
>私の希望は、もともとシートにはパスワード設定(12345)

違います。じっくり読めばわかりますが提示されたものとおなじです。(イミディエイトへの出力を足しただけ)
パスワードはテストするときに邪魔くさいので取ってるだけです。いまそこは問題ではありません。

(もこな2 ) 2021/03/05(金) 07:46


■10の補足
既に調べられているとはおもいますが、「ブレークポイント」の説明も追加します
 【ブレークポイント】
https://www.239-programing.com/excel-vba/basic/basic022.html
https://www.tipsfound.com/vba/01010
https://kabu-macro.com/detail.php?dir=word&dir2=ha-ho&uri=breakpoint

■6の補足
言わなくてもわかるとはおもいますが、シート名は適宜ご自身の環境に合わせて変えてくださいね。

 With Worksheets("Sheet1")   ←ココ

■11の補足
>シート自体にはロックをかけない状態で、Bセルの書式設定の保護のチェックを入れておくことが条件ですよね。
ちょっと混乱されているようですが、元のコードも

 1. 対象セルの【ロック】のチェックを外しておく
 2. シートの【保護】をしておく

という設計になっていますよね。

(もこな2 ) 2021/03/05(金) 08:20


■番外1
2021/03/03(水) 23:41に提示したものについてよく確認してみると、入力規則が設定されているとそちらが優先されてしまい、「BeforeDoubleClick」イベントに進まないようなので「BeforeRightClick」イベントに修正してください。

(もこな2) 2021/03/05(金) 13:00


もこな2さん、ありがとうございます。
もこな2さんのご指摘などを考えながら、新しいブックにもともとのマクロを設定して試してみました。
すると、大事なことに気付きました。それを私がお伝えしていなかったことに気付きました。
保護をかけたいB列は、B12:B2011まであるのですが、実際にはこれを10行ずつセルを結合しておりまして、
この新しいブックで結合した場合と結合しない場合で試したところ、結合しないセルだとリスト選択後にしっかり保護がかかりました。
ところが、結合セルだと保護がかからず、リスト選択したデータをDeleteキーで消したら保護がかかるという、お伝えしてきた現象となりました。
B列が結合セルであることを伝えておらず、申し訳ありません。
(お正月) 2021/03/05(金) 13:32

>実際にはこれを10行ずつセルを結合しておりまして
では■7を↓に修正します。イミディエイトにどのように表示されたか教えてください。

    Dim mRg As Range
    Dim mStr As String
    '-----------------------------------------------------------
    Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
        If Not Intersect(Range("B12:B2011"), Target) Is Nothing Then
            Set mRg = Target.Item(1)
            Debug.Print "「mRg」に【" & mRg.Address(0, 0) & "】を格納した"
            mStr = mRg.Value
            Debug.Print "「mStr」に【" & mStr & "】を格納した" & vbLf
        End If
    End Sub
    '-----------------------------------------------------------
    Private Sub Worksheet_Change(ByVal Target As Range)
        Debug.Print Target.Address(0, 0) & " の書き換えによりChangeイベントが発生した"
        Dim xRg As Range
        On Error Resume Next
        Set xRg = Intersect(Range("B12:B2011"), Target)
        If xRg Is Nothing Then
            Debug.Print "範囲外のため処理を中止した"
            Exit Sub
        Else
            Debug.Print xRg.MergeArea.Address(0, 0) & " の処理を開始する"
            Target.Worksheet.Unprotect
            Debug.Print "【" & xRg.MergeArea.Cells(1, 1).Value & "】と【" & mStr & "】を比較"
            If xRg.MergeArea.Cells(1, 1).Value <> mStr Then
                Debug.Print "判定結果は「真」→" & xRg.MergeArea.Address(0, 0) & "のロックを実行する"
                xRg.MergeArea.Locked = True
            Else
                Debug.Print "判定結果は「偽」→ロックはしない"
            End If
            Target.Worksheet.Protect
        End If
        Debug.Print "Changeイベントの処理が完了した" & vbLf & vbLf
    End Sub
     '----------------------------------------------------------
    Private Sub Worksheet_SelectionChange(ByVal Target As Range)
        If Not Intersect(Range("B12:B2011"), Target) Is Nothing Then
            Set mRg = Target.Item(1)
            Debug.Print "「mRg」に【" & mRg.Address(0, 0) & "】を格納した"
             mStr = mRg.Value
             Debug.Print "「mStr」に【" & mStr & "】を格納した" & vbLf
        End If
    End Sub

(もこな2) 2021/03/05(金) 16:38


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.