- 締切済み
VBA 範囲選択時エラー
Private Sub Worksheet_SelectionChangeのVBAでA列B列C列でワンクリックで文字が入力できるように設定致しました。 その後、A列からC列を範囲選択してDeleteするとデバック 「実行時エラー 型が一致しません」と出てしまいます。業務上、そのセルのデータは一気に消したいので困っております。どなたか分かる方よろしくお願い致します。 Private Sub Worksheet_SelectionChange(ByVal Target As Range) Dim rng As Range, rng_1 As Range, rng_2 As Range Application.EnableEvents = False Set rng_1 = Range("H17:H100") Set rng_2 = Range("I17:I100") Set rng_3 = Range("J17:J100") Set rng_4 = Range("K17:K100") Application.EnableEvents = True Set rng = Intersect(Target, rng_1) If Not rng Is Nothing Then Cancel = True If Target.Value = "" Then Target.Value = "(1)" Else Target.Value = "(1)" End If Else Set rng = Intersect(Target, rng_2) If Not rng Is Nothing Then Cancel = True If Target.Value = "(2)" Then Target.Value = Empty Else Target.Value = "(2)" End If Else Set rng = Intersect(Target, rng_3) If Not rng Is Nothing Then Cancel = True If Target.Value = "(3)" Then Target.Value = Empty Else Target.Value = "(3)" End If Else Set rng = Intersect(Target, rng_4) If Not rng Is Nothing Then Cancel = True If Target.Value = "(4)" Then Target.Value = Empty Else Target.Value = "(4)" End If End If End If End If End If End Sub
- みんなの回答 (3)
- 専門家の回答
みんなの回答
- real beatin(@realbeatin)
- ベストアンサー率82% (174/211)
(前の回答からのつづき) 6■代替え案 について 上記1■~5■への対策としてですが、 →1■ Targetが複数セルである場合にも(単一にも)備えて Target内のセルをループする書き方をします。 →2■3■ _BeforeRightClick イベントを使ったものを提案します。 【選択セル範囲の変更時】ではなく 【選択セル範囲を右クリックした時】に替えます。 単一セルについては、左クリックでの選択を省略して 直接右クリックで作動します。 複数セルについては、一旦選択してから右クリックです。 なんで右クリック?という疑問があるかも知れませんが、 _SelectionChange イベントはクリックとは関係ない旨、2■で 説明しました。また、【選択セル範囲の変更】は様々な契機で 起こるので、処理を開始するきっかけとして、意図した通りの タイミングを捉えるのも難しい、ということも。 そこで、趣旨にもっとも近い操作性を実現できるであろうと 思われるのが、_BeforeRightClick イベント、ということです。 →4■ "(1)"でも、-1でも、どちらでもいいように、 セル値の取得については、.Valueプロパティの代りに .Textプロパティを用いて、表示値をみるようにします。 誤作動を防ぐ為の仕組みを用意しましたが、 数値なのか文字列なのかは、そちらでご判断の上、 シート上での統一を、別途実践なさってください。 →5■ たぶん、大体は当たっているような気はしますが、 細かい仕様までは、実際に動かしてみないと 当のご本人も見当つかない面もあるでしょうから、 こちらからも敢えて明示しません。 やってみる内に、ここはこうしたいとか、 新たなニーズも出てくるでしょうから、 模索、検討してみてください。 一応、複数セルに対する処理については、 先頭(左上)セルのみで判別する少し変わったことをしています。 でもこれは、一度間違っても、 再度右クリックすれば、目的値に辿りつくように、という意図です。 以下、試す時は、 既存シートの_SelectionChange をコメントブロック等で一旦無効にするか 新規シートのシートモジュールに貼り付けるか 処理の重複が無い状態を担保するようにしてください。 処理対象として扱うセル範囲の限定の仕方 【Cancel引数】の扱い方 Application.EnableEvents プロパティの扱い方 セルを総当たりする For Each ループの扱い方 などは、努めて教科書的に書いていますので、 ご参考になることあれば、、、。 ' ' // Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean) Dim myTarget As Range, c As Range Dim vTarget, flg As Boolean Set myTarget = Intersect(Range("H17:K100"), Target) If myTarget Is Nothing Then Exit Sub ' 処理対象のセル範囲外なので、処理を止めて、抜ける Cancel = True vTarget = "(" & myTarget(1).Column - 7 & ")" ' [H]:"(1)",[I]:"(2)",[J]:"(3)",[K]:"(4)" flg = myTarget(1).Text = vTarget ' "消す"のか"値を設定する"のか判別したフラグ Application.EnableEvents = False ' Worksheet_Change イベントの発行抑止 For Each c In myTarget vTarget = "(" & c.Column - 7 & ")" ' [H]:"(1)",[I]:"(2)",[J]:"(3)",[K]:"(4)" If flg Then c.Value = Empty Else c.Value = vTarget End If Next Application.EnableEvents = True ' Worksheet_Change イベントの発行抑止を解除 End Sub ' ' //
- real beatin(@realbeatin)
- ベストアンサー率82% (174/211)
こんにちは。 少し長くなりますが、大事なポイントで誤解があるようなので、 ひとつずつ説明して、最後に提案を添えます。 解答者側の都合を気にして急ぐような必要はありませんので、 じっくり考えてみて貰えるといいのですが。 1■実行時エラーの原因 について > その後、A列からC列を範囲選択してDeleteすると > デバック 「実行時エラー 型が一致しません」と出てしまいます。 原因は、↓ここ。 > If Target.Value = "" Then Targetが単一セルである場合、にのみ成立する判別式です。 例えば、[H17]がTargetであれば、 [H17]に設定されたひとつの値を評価しますから、 判別式 Target.Value = "" は[True|False]で結果を返します。 Targetが複数セルである場合には、 左辺 Target.Value は配列になります。 配列 = "" という比較式は、 成り立ちませんから、 (どれと比較するのか意味を成しませんから) 「実行時エラー 型が一致しません」 という結果になります。 続いて、これも誤解が多いのですが、 Targetが[セルの結合]を適用したセルである場合にも Target単一セルではなく、 Targetが複数セルである場合にあたりますので、 同様の結果に陥ります。 このケースで、単にエラーを回避する為だけの対策としては、 If Target(1).Value = "" Then のように、先頭にある単一セル の値をみるようにすることで、 簡易的な対処は可能です。 但しこれは厳密には正しい判別法ではありませんので、 プロは絶対やらないけど、個人的にはこれで十分と思った場合 に限って有効なやり方と捉えていてください。 或いは、Targetが単一セルである場合だけ処理したい、 ということであれば、 If Target.Count > 1 Then Exit Sub と、先頭に1行書き加えるだけで、 複数セル選択時をエスケープさせることはできます。 この方法は初級の教科書にも出てくる基本的なやり方ですが、 複数セル範囲選択時を除外する、ということが妥当な場面なのか、 正しい判断が必要になります。 2■_SelectionChange イベント について > Private Sub Worksheet_SelectionChangeのVBAで > A列B列C列でワンクリックで文字が入力できるように設定致しました。 誤解があるように思うのですが、 _SelectionChange イベント は、 【選択セル範囲の変更】 をトリガーにします。 「セルをクリック」したタイミング を捉えるようなイベントは用意されていませんし、出来ません。 例えば、 [H17]に手入力で、"(1)"を設定する際、Enterキーで確定すると、 選択範囲は、[H18]に移りますので、 [H18]にて_SelectionChange イベントが発行されます。 手入力を一切拒絶するような仕組みを、もしも作ることができたなら、 ある程度の機能を見込むことは出来ますが、 選択セル範囲の変更は他の切っ掛けでも発生するものですので、 正しく管理するのは非常に困難なことです。 3■Worksheet_ イベントの 【Cancel引数】 について Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean) _BeforeDoubleClick イベントには、 (ダブルクリックに追随した)セル[入力モード]状態をキャンセルする 引数 Cancel が用意されています。 Cancel = True を指定すれば、 セル[入力モード]状態を事前にキャンセルします。 Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean) _BeforeRightClick イベントには、 コンテクストメニュー(右クリック時のポップアップ)をキャンセルする 引数 Cancel が用意されています。 Cancel = True を指定すれば、 コンテクストメニューを事前にキャンセルします。 Private Sub Worksheet_SelectionChange(ByVal Target As Range) _SelectionChange イベントには、 ご覧の通り、【Cancel引数】は用意されていません。 従って、_SelectionChange イベントで、 Cancel = True を指定すれば、エラーになるだけですし、 仮にエラーにならないように書いても、何もしてくれません。 想像の話として、 選択セル範囲の変更をキャンセルして、 直前のセル範囲選択状態に戻す というようなニーズはあるのかも知れませんが、 これを実装するには、非常に高度な技術が求められます。 自力で実装する技術を持たなければ、 意図した通りの挙動を確保する為のメンテナンスも出来ませんから、 人に薦められて使ってみる、といった具合にはいかないでしょう。 こちらからは、お勧めできませんので、 他の方法を探ることをお奨めします。 ただ、今回のご質問については、恐らく、 _SelectionChange以外の他の他のWorksheet_イベントを 手本に流用した名残が誤って残っただけのことだろうと思います。 端的に、_SelectionChange イベントでは、 【Cancel引数】は扱いません。 4■セル値またはセル表示値としての -1 "(1)" について これはVBAではなくて、 元々表計算ソフトとして数値を扱う目的で開発された Excel(その他表計算アプリ共通)一般 の仕様に関するお話です。 はっきりと理解した上でのご質問ではないようなので、 一応、触れておきます。 できれば試してみて欲しいのですが、 新しいシートを用意して、どこか適当なセルに (1)とタイプして、Enterキーを押してみてください。 値を設定したセルにカーソルを戻して、数式バーを見てください。 セルに表示されたセル表示値は (1) 数式バーに表示されている実際のセル値は -1 と喰い違いがあることに気が付く筈です。 つまり、特に意識しないで、セルに、 (1) を入力すると、Excelはこれを数値-1と看做し、 値は -1 セルの表示形式は、"0_);(0)"が自動的に割り当てられ [負の値の表示形式]を括弧付数字(1234)として扱います。 端的にいうと、Excelでは(1)は-1として扱うのが普通の仕様なのです。 文字列としての "(1)"をセルに設定する為には、 [セルの書式][表示形式]を[文字列]に 事前に 設定しておく必要があります。 見た目上は、同じ(1)に見えても、 数値-1が(1)と表示されているのか、 文字列"(1)"が設定されているのか ハッキリと使い分けを意識しておかないと (例えば、Excelの数式や[検索]等の代表的な機能でさえ、 2種類の(1)を別々のものとして扱いますので) 先々扱い難いシートになってしまいますから備えが必要です。 5■なさりたいこと について 実の処、思い通りに動いていないVBAの記述を手掛かりに 実際のニーズを的確に理解することは難しいのですけれど、 > .. Worksheet_SelectionChangeのVBAで ... > .. A列B列C列でワンクリックで文字が入力できるように設定 ... ご提示のコードが対象としているセル範囲は、 [H17:K100]ですので、この点喰い違いがあります。 説明文"A列B列C列"は推敲漏れとして[H17:K100]が正しいものとします。 [H17:K100]の列によって、(1)(2)(3)(4)を振り分けたい という意図は解ります。 条件分岐の仕方も > If Target.Value = "" Then > If Target.Value = "(2)" Then のように不統一なのですが、統一するのが自然でしょうから。 それぞれのセルの値が、予定値(n)であれば値を消去し、 それぞれのセルの値が、予定値(n)でなければ予定値(n)を設定 ということをなさりたいのかな、と。 > ... A列からC列を範囲選択してDeleteすると ... > ... 業務上、そのセルのデータは一気に消したい ... Deleteキーを押した場合は、 VBAは何も処理しないように設計を考えた方が宜しいかと。 文字数制限に納まらないので、回答文を分けますが、 次の投稿で、代替え案を提示してみます。 (次の回答へつづく)
- Nouble
- ベストアンサー率18% (330/1783)
間違えて、いたら 済みません エラーですが エラーNo.か、判らない ので、想像ですが #VALUE 辺りが 出て、いるように 思います 出現場所は If Target.Value = 間違いですか? で、 要するに やりたい、事は Private Sub Worksheet_SelectionChange(ByVal Target As Range) Dim rng As Range Set rng = Intersect(Target, Range("H17:K100")) If Not rng Is Nothing _ Then Cancel = True If Target.cells(1,1).column = 8 _ Then Target.Value = "(1)" Else If Target.Value = "( & Target.cells(1,1).column - 7 & ")" _ Then Target.Value = Empty Else Target.Value = "( & Target.cells(1,1).column - 7 & ")" End If End If End Sub ですよね? で、Targetに 単一セル、では なく 領域を、渡した 以前にも 領域を、渡した事は ある の、ですか? 領域を 渡す、場合 Target.Valueが "( & Target.cells(1,1).column - 7 & ")" の、ものと、 そうで、無い ものと、 が、 混在する と、思えますが 如何ですか? 混在した、場合 VBA の、身に なって、考えると 一体、如何したら 良い… ? で、しょう? 悩みませんか? VBAでの、処理は シート関数と、違い 逐次式です 包括的な ファジー処理を、与える のでは、なく 一つづつを 順次、処理します しかし、此は ファジー処理的で、且つ 一意の、判定を 許して、ません 駄目、上司 典型行動、です 領域を、与える 場合で If Target.Value = と、したくば For each で ループさせて セル、一つづつ 見て、いかないと と、思いますが 如何、ですか?
お礼
取り急ぎ、ご回答いただけた事にお礼致します。 ありがとうございました。 私の説明不足ですみません。エラーNO13です。 ただ今、早急な仕事が入り、まだ、きちんとみれておりません・・・ 早急な仕事が終わり次第確認したいと思います。