[[20041126134748]] 『チェンジイベントでのシート保護について』(TTC) ページの最後に飛ぶ

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

 

『チェンジイベントでのシート保護について』(TTC)

 いつもお世話になっております。
 今、過去ログ等を参考にさせていただきながら作っているのですが
 分からないことがあり質問させていただきます。

 やりたいことは、VLookup と 入力規則 の組み合わせなのですが、
 数式を入れずチェンジイベントで行ないます。

 A列に店コードを入力するとB列に並び順の番号、C列に店名 を表示
 させます。D列はダブルクリックで入力リストを表示させます。

 他は変更できないように入力する範囲以外は保護をかけます。

 保護がかかっているのでコードに保護解除を記入したのですが
 以下のところで「実行時エラー」アプリケーション定義または
 オブジェクト定義のエラーです。と出ます。

 UnProtectとProtectを入れない状態では問題なく作動するのですが
 挿入場所がいけないのでしょうか?
 いろいろ試しましたがどうもうまくいきません
 どなたかアドバイスをください。お願いいたします。

 以下、コード表示

 Option Explicit
 Private Sub Worksheet_Change(ByVal Target As Range)

 '宣言 ##########
 Dim DWs As Worksheet, NWs As Worksheet
 Dim データ As Variant

 '設定 ##########
 Set DWs = ThisWorkbook.Sheets("データ")
 Set NWs = ThisWorkbook.Sheets("入力")

     With DWs
         データ = .Range("A2:C62").Value   'VLookupの照合範囲
     End With

         'On Error GoTo エラー   '店舗コード入力間違いの際の飛び先

     With Target

         If .Count > 1 Then Exit Sub   '2以上のセルは無効
         If .Row <= 5 Then Exit Sub   '5行目までは見出し等なので無効
         If .Column >= 5 Then Exit Sub   '5列目より右は無効

 NWs.Unprotect   'シート保護解除

         If .Column = 1 Then   'A列だったら

             If .Value <> "" Then   '空白じゃなかったら"順"と"店舗名"を記入

                 .Offset(0, 1).Value = Application.WorksheetFunction.VLookup(Target, データ, 2, False)
                 .Offset(0, 2).Value = Application.WorksheetFunction.VLookup(Target, データ, 3, False)
                    ↑デバックするとここでとまっています(空白以外のとき)

             End If

             If .Value = "" Then   '空白だったら(消したら)右のデータも空白にする

                 .Offset(0, 1).Value = ""
                 .Offset(0, 2).Value = ""
                   ↑空白のときはここです

             End If

         End If

         If .Column = 4 Then   'D列だったら入力規則を解除
             With Target.Validation
                 .Delete
             End With
         End If

     End With

 NWs.Protect   'シート保護

 Set DWs = Nothing   '変数の開放
 Set NWs = Nothing

 Exit Sub

 エラー:
 MsgBox "そのコードの店舗はありません!" _
         & vbCrLf & vbCrLf & _
         "モ・ウ・イ・チ・ド 店舗コードを確認してください...", _
         vbExclamation + vbOKOnly + vbDefaultButton1, "コードエラー!"

     Target.Activate

 End Sub

 Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)

 ActiveSheet.Unprotect   'シート保護解除

 Cancel = True

     With Target

         If .Count > 1 Then Exit Sub   '2以上のセルは無効
         If .Row <= 5 Then Exit Sub   '5行目までは見出し等なので無効
         If .Column <> 4 Then Exit Sub   'D列以外は無効

             With Target.Validation   '入力規則でリストを設定する
                 .Delete
                 .Add Type:=xlValidateList, Formula1:="○,×,△"
                 .ShowError = False
             End With

     End With

 ActiveSheet.Protect   'シート保護

 End Sub

 PS.ここにコードをコピー&ペーストしたあと、先頭に1行1行空白をいれてます
    なにか他によい方法がありますか?(インデントみたいな...)


 >   なにか他によい方法がありますか?(インデントみたいな...)
取りあえずここだけ反応。
VBAのエディタ上で全選択してからTAB押すと頭に4桁の空白はいるはず。
これを使うか、1文字だけ入れたいなら矩形範囲でテキストをいじれるエディタを使うとか。
ここのリンクで紹介されている
サクラエディタ
http://members.at.infoseek.co.jp/sakura_editor/snapshot.html
なら全選択後にスペース叩くと全行の頭にスペースが入ります。
なんて便利。
それだけ。
(ご近所PG)質問に対する回答ではない罠


 私も書き方だけ・・・(^_^A;
 コード内の空白行をなくしておけば、最初の行だけ半角スペースを入れるだけで
 全部が整形されます。(^_^A;
 (川野鮎太郎)

 ざっと見ただけで検証してませんが問題ありそうなのは
変数「データ」がVariant型なのと
     With DWs
         データ = .Range("A2:C62").Value   'VLookupの照合範囲
     End With
この部分。検索範囲はセル参照でなければいけないはず。
Range型で宣言して
     With DWs
         Set データ = .Range("A2:C62")   'VLookupの照合範囲
     End With
にしてみては?
(みやほりん)


 衝突しました!

 ご近所PGさん、川野さん、ありがとうございました!
 「さくら」まだよく分かりませんがすごいですね!
 あやうく没頭しそうになってしまいました...
 ちなみにこの文章もさくらです!!
 (最初全角空白をいれてて悪戦苦闘...)

 今度からここにノセル?のを前提に一文字開けとくのも
 手ですね...?

 ありがとうございました。(書き方について)

 みやほりんさん、ありがとうございます。
 さっそく試してみます。

 (TTC)

 ああ、試さなくてもいいです。
Variant型とRange型の区別はないみたいですね。
Vlookupはどちらでも動作するようです。
やはり検証してみないとだめでし。
(みやほりん)


 試してしまいました...
 >Variant型とRange型の区別はないみたいですね。
 どちらでもようということでしょうか?
 基本的にはRange型の方がよいのでしょうか?

 (TTC)

 いや、わたしがRange型で習慣的にやっていたというだけです。
原因は大体わかりました。
コード中でシートにデータを入力する前に
 Application.EnableEvents = False
を挿入、Exit Subの前に
 Application.EnableEvents = True
念のためTarget.Activateのあとにも
 Application.EnableEvents = True
を入れてみてください。イベントが交錯しているみたいです。
(みやほりん)


 ありがとうございます。
 Application.EnableEvents = False
 を挿入したら問題なく作動しました。
 シート保護が原因ではなかったのですね?
 データが入力されたときにまたイベントが発生してしまう
 ということでしょうか。

 シート保護かけなかったときにうまくいくのはなぜでしょう?
 スイマセン解決したのですが...

 (TTC)

 順を追いますと、
イベント1発生
→保護解除
→先のVlookupでイベント2発生→イベント2内でシート保護
→イベント1へ復帰
→あとのVlookupでシートが保護されてしまっているので書き込みできない
ということだと思います。
Changeイベント内でシートのデータ変更は連鎖的にChangeイベントが
発生してしまうので、イベントの抑制が必要。わたしもうっかりしてましたね。
(みやほりん)


 とても分かりやすいご説明ありがとうございました。
 If .Column = 2 Then Exit Sub   '2列目より右は無効
 を入れても大丈夫ということですよね...!

 ありがとうございました!

 (TTC)

 Application.EnableEvents = False
の前にExit Subなら問題ないと思います。 
(みやほりん)


 みやほりんさん、ありがとうございました。

 もうひとつ、質問させていただいてよろしいでしょうか?
 同じ内容が過去ログにあったのですが、応用力がなくヒントだけでも
 いただきたいのですが...
 If .Count > 1 Then Exit Sub   '2以上のセルは無効
 のところ、例えば同じコードが続く場合、オートフィルしたとき
 まとめてDeleteした時などに対応するには?という質問です。
 できますでしょうか?

 (TTC)

 確認です。
(1)オートフィルしたときにもVlookupで検索を成立させたい。
(2)A列DeleteしたときはB列C列もクリアしたい、ってことでしょうか。
(みやほりん)

 はい。(1)・(2)共にその通りが希望です。
 (TTC)

 腹案はありますが、ただいま帰り支度中。
寝るまでにやってみますが、
「さくっ」と行かないかもしれないので時間がかかるカモ、です。
それでよろしければ。
(みやほりん)

 下からお邪魔します。
よくみたら、σ(^◇^;)のコードに似てるのがあったので
うれしくなってちょっと考えてみました。
たぶん、こんな感じじゃないかと思うけど、、駄目だったら(・_・ )ノ"    ゜ ポイッ
してください。
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
'宣言 ##########
Dim DWs As Worksheet, NWs As Worksheet
Dim データ As Variant, x As Variant, C As Range
'設定 ##########
Set DWs = ThisWorkbook.Sheets("データ")
Set NWs = ThisWorkbook.Sheets("入力")
With Target
    If .Row <= 5 Then Exit Sub   '5行目までは見出し等なので無効
    If .Column >= 5 Then Exit Sub   '5列目より右は無効
End With
データ = DWs.Range("A2:C62").Value   'VLookupの照合範囲
Application.EnableEvents = False
Me.Protect , , , , True
    With Target
        Select Case .Column
            Case 1    'A列だったら
                If .Count > 1 Then
                    For Each C In Selection
                        If Not IsEmpty(C.Value) Then '空白じゃなかったら"順"と"店舗名"を記入
                            '店舗コードがあるか調べて
                            x = Application.Match(C.Value, DWs.Range("A2:A62"), 0)
                            'あったら
                            If Not IsError(x) Then
                                C.Offset(0, 1).Value = Application.WorksheetFunction.VLookup(C.Value, データ, 2, False)
                                C.Offset(0, 2).Value = Application.WorksheetFunction.VLookup(C.Value, データ, 3, False)
                                '            ↑デバックするとここでとまっています (空白以外のとき)
                            Else 'なかったら
                                MsgBox C.Value & " の店舗はありません!" _
                                    & vbCrLf & vbCrLf & _
                                    "モ・ウ・イ・チ・ド 店舗コードを確認してください...", _
                                    vbExclamation + vbOKOnly + vbDefaultButton1, "コードエラー!"
                            End If
                        Else '空白だったら(消したら)右のデータも空白にする
                            .Offset(0, 1).Value = ""
                            .Offset(0, 2).Value = ""
                            '        ↑空白のときはここです
                        End If
                    Next
                Else
                    If Not IsEmpty(.Value) Then '空白じゃなかったら"順"と"店舗名"を記入
                        '店舗コードがあるか調べて
                        x = Application.Match(.Value, DWs.Range("A2:A62"), 0)
                        'あったら
                        If Not IsError(x) Then
                            .Offset(0, 1).Value = Application.WorksheetFunction.VLookup(.Value, データ, 2, False)
                            .Offset(0, 2).Value = Application.WorksheetFunction.VLookup(.Value, データ, 3, False)
                            '            ↑デバックするとここでとまっています (空白以外のとき)
                        Else 'なかったら
                            MsgBox .Value & " の店舗はありません!" _
                                & vbCrLf & vbCrLf & _
                                "モ・ウ・イ・チ・ド 店舗コードを確認してください...", _
                                vbExclamation + vbOKOnly + vbDefaultButton1, "コードエラー!"
                        End If
                    Else '空白だったら(消したら)右のデータも空白にする
                        .Offset(0, 1).Value = ""
                        .Offset(0, 2).Value = ""
                        '        ↑空白のときはここです
                    End If
                End If
            Case 4     'D列だったら入力規則を解除
                Target.Validation.Delete
        End Select
    End With
Set DWs = Nothing   '変数の開放
Set NWs = Nothing
データ = Empty
Application.EnableEvents = True
Exit Sub
エラー:
'    MsgBox "そのコードの店舗はありません!" _
'        & vbCrLf & vbCrLf & _
'        "モ・ウ・イ・チ・ド 店舗コードを確認してください...", _
'        vbExclamation + vbOKOnly + vbDefaultButton1, "コードエラー!"
        Application.EnableEvents = True
End Sub
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Cancel = True
Me.Protect , True, , , True
    With Target
        If .Count > 1 Then Exit Sub   '2以上のセルは無効
        If .Row <= 5 Then Exit Sub   '5行目までは見出し等なので無効
        If .Column <> 4 Then Exit Sub   'D列以外は無効
    End With
    With Target.Validation   '入力規則でリストを設定する
        .Delete
        .Add Type:=xlValidateList, Formula1:="○,×,△"
        .ShowError = False
    End With
End Sub
http://ryusendo.no-ip.com/cgi-bin/upload/src/up0177.xls
すみません。
データ = Empty
を
Erase データ
に変更してください。おおぼけでした。m(__)m
2004/11/26 22:00
v(=∩_∩=)v
(SoulMan)

 おお、やはり作っていた方が。
BeforeDoubleClickイベントのほうは問題なしと思われるので、
Changeイベントのみ試作してみました。
 
 Option Explicit
 Private Sub Worksheet_Change(ByVal Target As Range)
 '宣言 ##########
 Dim DWs As Worksheet, NWs As Worksheet
 Dim データ As Range
 Dim データチェック As Range
 Dim C As Range
 '設定 ##########
 Set DWs = ThisWorkbook.Sheets("データ")
 Set NWs = ThisWorkbook.Sheets("入力")
 Set データ = DWs.Range("A2:C62")   'VLookupの照合範囲
 Set データチェック = データ.Resize(データ.Rows.Count, 1) '店舗コードのチェック範囲
         'On Error GoTo エラー   '店舗コード入力間違いの際の飛び先

 'チェック ##########
 With Target
     If .Columns.Count >= 2 Then Exit Sub   '2列以上のセルの時は無効
     If .Row <= 5 Then Exit Sub   '5行目までは見出し等なので無効
     If .Column >= 5 Then Exit Sub   '5列目より右は無効
     If .Column = 2 Or .Column = 3 Then Exit Sub    '2列目3列目は無効
 End With

 '処理開始 ##########
 NWs.Unprotect   'シート保護解除
 Application.EnableEvents = False    'イベント発生抑制

 For Each C In Target   'Target中の各セルに対してFor Each間の処理
     With C
         If .Column = 1 Then   'A列だったら

             If .Value <> "" Then   '空白じゃなかったら"順"と"店舗名"を記入
                 If Application.WorksheetFunction.CountIf(データチェック, .Value) = 0 Then GoTo エラー
                 .Offset(0, 1).Value = Application.WorksheetFunction.VLookup(.Value, データ, 2, False)
                 .Offset(0, 2).Value = Application.WorksheetFunction.VLookup(.Value, データ, 3, False)
    '                    ↑デバックするとここでとまっています (空白以外のとき)
             Else   '空白だったら(消したら)右のデータも空白にする
                 .Offset(0, 1).Value = ""
                 .Offset(0, 2).Value = ""
    '                   ↑空白のときはここです
             End If

         ElseIf .Column = 4 Then   'D列だったら入力規則を解除

             With .Validation
                 .Delete
             End With

         End If

     End With
 Next C

 Application.EnableEvents = True    'イベント発生抑制解除
 NWs.Protect   'シート保護

 '変数の解放 ##########
 Set データチェック = Nothing
 Set データ = Nothing
 Set DWs = Nothing
 Set NWs = Nothing
 Exit Sub

エラー:

 MsgBox "そのコードの店舗はありません!" _
         & vbCrLf & vbCrLf & _
         "モ・ウ・イ・チ・ド 店舗コードを確認してください...", _
         vbExclamation + vbOKOnly + vbDefaultButton1, "コードエラー!"

     Application.EnableEvents = True
     Target.Activate

 End Sub
 
なるべく原形を残したつもりでしたが、
やはり使い慣れた方法でないと思考がストップしてしまう。
変数データはやはりRange型にしてしまいました。
いかがなもんでせうか。
(みやほりん)
#2004/11/26 23:40 店舗コードエラーチェック位置修正。
#でもエラーコード検出でループ抜けちゃうので、
#エラーラベルジャンプは再考したほうがいいかもしれません。


 お返事大変遅くなりまして申し訳ございませんでした。

 SoulManさん!みやほりんさん!完璧な回答をいただきありがとうございました。
 いままで入力規則・関数・マクロの組み合わせで7MBくらいになってしまっていたので
 これでだいぶ軽く出来そうです。
 「For Each」の使い方、少し勉強させていただきました。

 ところで、みやほりんさんの
 >#でもエラーコード検出でループ抜けちゃうので、
 >#エラーラベルジャンプは再考したほうがいいかもしれません。
 少し教えていただけますでしょうか?
 動作をみて問題なさそうなのですが...
 それと、
 On Error GoToがコメントでも問題ないのはなぜですか?

 (TTC)

 私が手を加えたコードではCountIf(データチェック・・・の部分で
店舗コード未検出の場合に即ループを抜けてエラーラベルへジャンプして
しまいます。みっつのセルを同時処理したときに二つ目のセルでコードが
見つからない、みっつ目のセルの処理は放置して処理を終えてしまう構造
になっています。
(問題はありませんが、変数解放部分もスキップしてしまう)
基本的には「予想していない」エラーのみエラーラベルへジャンプさせたほうがいいです。
未設定コードの入力は「予想できる」エラーなので、ループ内にエラー処理を
組み込んで処理を継続する方が(私としては)納得できる処理の仕方です。
(SoulManさんのはそうなっているようですね)
(みやほりん)


 大変よく分かりました!
 確かに未設定コードをオートフィルするとメッセージは1つしか出ませんね。
 勉強しながら考えて見たいと思います。

 とても勉強になりました。ありがとうございました。

 (TTC)


 > PS.ここにコードをコピー&ペーストしたあと、先頭に1行1行空白をいれてます
 >   なにか他によい方法がありますか?(インデントみたいな...)

 参考です。ダウンロードしてお試し下さい。右クリックメニューから起動できます。
http://camaro.ddo.jp/Books/xl_school.xls
  (INA)


 INAさん、ありがとうございます!!

 すごのを作っていただきました!
 即、アドインに入れてしまいました。

 活用させていただきます!

 (TTC)

 INAさん、べんりかも。
(みやほりん)


 toみやほりんさん
 感想ありがとうございます。(^^ゞ

 ご近所PGさんにC言語で作るように課題を出されていたのですけど、
 ひとまずVBAで作ってしまいました。(^_^;)

 本当はユーザーフォームを使わないで
 新規に開いたメモ帳を利用しようとしたのですけど、
 無題メモ帳上のテキストの取得が上手く行かずに断念しました。
 無題メモ帳にメッセージは送信できても、取得できなくて・・・
 もしくは無題メモ帳の保存だけでも出来れば、
 一時的に保存して、Openステートメントで処理できたのですけど・・(-_-;)

 メモ帳が使えれば、タスクにも表示されるし、VBEのうえに開けるので、
 断然使いかってがよかったので、かなり妥協してます。
 開発したものは、VBEのショートカットメニューからも開けるようにしたのですが、
 Windowの制御が安定しないし・・・(自己満足度30%です。)(+_+)

 書式の機能を追加してみようと考えているのですが、 
 どなたか、こちらの板のテキスト装飾の種類って分かりませんか?
  (INA)

コメント返信:

[ 一覧(最新更新順) ]


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