アプリ版:「スタンプのみでお礼する」機能のリリースについて

エクセルVBA初心者です。

下のように、同じシート内のコードとして2つのサブプロシージャがある状態なのですが、
シート内のどこかのセルを左クリック→指示(1)
シート内のどこかのセルを右クリック→指示(2)
のようにするにはどうしたらいいのでしょうか?
(この状態だと、右クリックすると選択範囲が変わったことが優先的に認識されて指示(1)のほうが実行されてしまうようです。)

説明不足かもしれませんが、何を説明していいのか分からないので、補足が必要だったら言ってください。すみませんがよろしくお願いします。

---------------------------------------------------------------
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
指示(1)
End Sub
---------------------------------------------------------------
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
指示(2)
End Sub
---------------------------------------------------------------

A 回答 (8件)

直接の回答ではないです。



>SelectionChange

これはいま選択しているセルから
別のセルを選択したときに発生するイベントです。
セルにはクリックイベントというのはありません。
    • good
    • 0
この回答へのお礼

迅速な回答ありがとうございます。
”右クリックしたこと”が”選択範囲が変わったこと”よりも優先的に認識されてくれればいいのですが、何かしらの方法で出来ないものですかねぇ・・・。
たとえば、
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
のすぐ下の行に、「右クリックの場合は終了」みたいな文章を入れられればOKなのかな~と思うのですが・・・う~ん。

お礼日時:2008/12/30 13:56

こんにちは。



別に、SelectionChangeとRightClick の共存は、コードの書き方で解決はしますが、大事なことは、「何をするのか」ということです。右クリックと左クリックで範囲を選択するのか、まったく別のマクロの処理をするのか、とか。

>「右クリックの場合は終了」
どうやら、関連性があるようにも思えるのですが、右が先で、左が後だとか、どちらに優先性があるのか、そういうことが分からないですね。

もうVBAも10年以上、多くの人たちに研究尽くされていますから、だいたいのことは、可能かどうかは分かります。思いつきは良いけれども、また、その代案というか、ある程度決まったパターンというのもあるかと思うのです。入門レベルの人ほど、難問が多いように思います。

この回答への補足

多分私はまだまだ慣れや経験がないせいで、代案を思いつくことができないのでしょう・・・。申し訳ないですが以下の通りということでよろしくお願いします。

各サブプロシージャ内にある指示は100行以上の複雑なものになってしまっているので、自分なりに簡単な例にして提示してみます。

前提:エクセルのデフォルトである「背景色なし」の状態から、ある条件のセルだけ背景色が赤に変更されている状態からスタートします。

任意のセルを左クリックしたとき、そのセルの背景色が赤なら、文字色を黄にして背景色xlNoneにする。
任意のセルを右クリックしたとき、そのセルの背景色が赤なら、文字色を青にして背景色xlNoneにする。

以上のことをしたいのですが、右クリックしても、セルが選択されたことが優先されてしまい、文字色は黄になってしまいます。

補足日時:2008/12/30 15:05
    • good
    • 0

こんにちは。



>多分私はまだまだ慣れや経験がないせいで、代案を思いつくことができないのでしょう・・・。

ご質問者さんに、そうしようとは言っておりません。今の私のVBAのある程度は、掲示板のベテランの方たちから教わったことです。そういうことは、順番に伝わっていくものだと思いますし、また、私なども提供できるものがあるかと思います。

>デフォルトである「背景色なし」の状態
>任意のセルを左クリックしたとき、そのセルの背景色が赤なら、文字色を黄にして背景色xlNoneにする。
>任意のセルを右クリックしたとき、そのセルの背景色が赤なら、文字色を青にして背景色xlNoneにする。

これは、単なるロジックの問題ではないでしょうか。クリックのどちらでも、背景色を消して、文字の色を変えるということではありませんか?

文字の色を赤にした後は、背景色は関係なくなってしまいますが、結局は背景色を消してしまうことなら、赤の文字色なら、青に変えることでも同じことではないでしょうか。

Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
  Cancel = True
  If Target.Interior.ColorIndex = 3 Then
   Target.Interior.ColorIndex = xlNone
  End If
  If Target.Font.ColorIndex <> 3 And _
    Target.Font.ColorIndex <> 5 Then
    Target.Font.ColorIndex = 3
  ElseIf Target.Font.ColorIndex = 3 Then
    Target.Font.ColorIndex = 5
  Else
    Target.Font.ColorIndex = 1 '最後は黒に戻す
  End If
End Sub

この回答への補足

少し違うように思います。

>結局は背景色を消してしまうことなら、赤の文字色なら、青に変えることでも同じことではないでしょうか。

いいえ、No.2の補足で書いた内容を書き換えると

背景色が赤であるセルが選択された時、
まず背景色を消す。
 それが左クリックによる選択であったならば、
  文字色を黄に。
 あるいはそれが右クリックによる選択であったならば、
  文字色を青に。
(背景色が赤以外であった場合は何もしない)

ということになります。が、私には「それが左クリックによる選択であったならば、」を表すような「If ~ then」が思いつきませんでした。
なので代案として、「SelectionChange」と「BeforeRightClick」を使って2つのサブプロシージャに分けてみたら、右クリックの場合も「SelectionChange」として認識されてしまうため困ってしまった、というわけです。

適切な回答なのに解釈が間違っていたらごめんなさい。

補足日時:2008/12/30 17:27
    • good
    • 0

指示(1)と指示(2)を実行するための条件が共通であるなら


(例題「セルの色が赤であれば」のように)

セルが条件を満たしているかどうかのフラグを別に定義し、

SelectionChangeイベントで
1.まず、フラグをOFFにする
2.セルが条件を満たしている場合、指示(1)を行う
3.フラグをONにする

BeforeRightClickイベントで
1.フラグがONの場合、指示(2)を行う
2.フラグをOFFにする

というふうに組み立ててみてはどうでしょうか。

質問者様の挙げた例だと、以下のようなコードになります。

Private Flg As Boolean '←フラグ
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Flg = False 'フラグをクリア
If Target.Interior.ColorIndex = 3 Then 'セルの背景が赤の場合
Target.Interior.ColorIndex = xlNone '背景を無色にする
Target.Font.ColorIndex = 6 'フォントを黄色にする
Flg = True 'フラグを立てる
End If
End Sub
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
Cancel = True
If Flg = True Then 'フラグが立っている場合
Target.Font.ColorIndex = 5 'フォントを青にする
End If
Flg = False 'フラグをクリア
End Sub

厳密に言えば
左クリック時→指示(1)を実行する
右クリック時→指示(1)を実行した後、指示(2)を実行する
(例で言うと、右クリック時は一旦黄色にした後、青にする)
ということになりますので、
指示(1)(2)の内容次第では使えない方法かもしれませんが…
    • good
    • 0
この回答へのお礼

お礼が大変遅くなってしまい申し訳ありません。
なるほど、私の挙げた例の場合、確かに教えていただいた方法で出来そうです。知恵を絞っていただきありがとうございました。

ただ・・・実は今回作成しているのは、Windowsに付属の単純なゲーム「マインスイーパ」です。このゲームをエクセルで再現してみようと思い、VBAの勉強がてら、楽しみ半分で作っているものです。(最初から言えばよかったのですが・・・。)
マインスイーパをそのまま再現しようとすると、

左クリック時・・・
そのセルが爆弾だったら、背景を赤にして”◎”を入力し、他の爆弾セルにも”◎”を入力する。
そのセルが爆弾の隣だったら、背景を白にして周囲にある爆弾の個数を入力する。←A
そのセルが爆弾の隣でなかったら、背景を白にして、周囲の8セルも背景を白にする。←B
周囲の8セルについてAとBの作業を繰り返す。

右クリック時・・・
そのセルの値がemptyなら”☆”(旗のマークの代わり)を入力する。
そのセルの値が”☆”なら”?”を入力する。
そのセルの値が”?”ならemptyにする。

という複雑な命令を実行させなければなりません。
う~ん、ムリなのでしょうか・・・。

お礼日時:2009/01/08 14:06

こんばんは。



BeforeRightClick イベントとSelectionChange イベントとは、もともと、その目的の趣旨が違いますから、等価のアクションの意味を持たせることはできません。勘違いされている方が多いと思いますが、必ずしも、SelectionChange イベントが先に立って、次に、BeforeRightClick イベントに来るということでもありません。クリックされる側が、純粋のControl オブジェクトなら、こんなことはないのですが。

以下の場合は、SelectionChangeで、マウスを監視しているだけです。右クリック・イベント自体は、あくまでも、ダミーで、右クリックを押したときに、Cancel を入れるためだけです。#1さんのおっしゃるとおり、Excelワークシートのセルには、クリックイベントはありませんから、厳密には、無理があります。

'シートモジュール

Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
 Cancel = True
End Sub

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim fL As Long
Dim fR As Long
Dim LR As Integer
 If GetAsyncKeyState(vbKeyRButton) < 0 Then
  LR = 0
 Else
  LR = 1
 End If
 ColorChange Target, LR
End Sub

Function ColorChange(rng As Range, LR As Integer)
If rng.Count > 1 Then Exit Function
If rng.Value = "" Then Exit Function '文字がないときは、除外
If rng.Interior.ColorIndex = 3 Then
  rng.Interior.ColorIndex = xlNone
  If LR = 1 Then
   rng.Font.ColorIndex = 6
  Else
   rng.Font.ColorIndex = 5
  End If
End If
End Function


なお、私個人ならは、単に、Ctrl +1 , Ctrl+2 のショートカットを作っているでしょうね。
    • good
    • 0
この回答へのお礼

お礼が大変遅くなってしまい申し訳ありません。

GetAsyncKeyStateという関数について初めて知りました!
これでうまくいきそうな気がしますが、まだ使いこなせなそうなので、GetAsyncKeyStateについてよく勉強してからチャレンジしてみようと思います。

No.4さんへのお礼にも書きましたが、実は遊び半分でエクセルでマインスイーパを作っていたらこんな問題に出くわしてしまった次第です。ですのでなるべくマウス1つで動くような、そのまんまなマインスイーパを作ろうと、無駄に努力しているところでしたが、これで完成するかもしれません。どうもありがとうございました。

お礼日時:2009/01/08 14:13

こんにちは。



ここの掲示板では、多くは質問は仕事や家庭で、実用で使うためのものと想定して解答しています。ですから、実験コードなど、それ以外の目的の時は、あらかじめ、最初におっしゃっていたほうが良かったです。マインスイーパということを言わないと、回答者をミスリードさせることになってしまいます。

それに、マインスイーパを良く観察したほうがよいです。どういう構造になっているのか、きちんと観察していれば、私たちが書いていたようなコードにはなりません。まず、できる・できないをお聞きになって、設計の方法からお尋ねになったほうがよいです。まったく違った状態にあるものを似せるほど難しいものはありません。

マインスイーパは、そのボタンひとつひとつが、コントロール(Active X)であり、右クリック、左クリック・イベントを持ったものです。そのクリックによって表示を変えたりします。VBAでも、同じようなものは可能です。しかし、VBAの上級の知識を持っている必要があります。探せば、コードもVB系で手に入ったはずです。(VBAの上級レベルでも、他の言語では初級レベルです。)

この回答への補足

大変申し訳ありませんでした。最初から伝えるべきでした。次回以降質問するときには、回答者の皆様のミスリードを招かないようもっと気をつけます。

もともと、自分の仕事で使うような初級のVBAは学習し終わったので、今度はマインスイーパなどの複雑そうなコードを、Active Xを使わずに書くことはできないかな~?などと思い、始めたことでした。

現段階で、↓のような状態にあるのですが・・・
(キケンなものは含まれていませんが、自己責任で実行してください。)
http://www.dotup.org/uploda/www.dotup.org0289.xl …
今のところ、右クリックで旗を立てることは出来ません。
あと、爆弾以外の全部のパネルを開いてもゲームが終了したことを伝える機能はまだ入れていません。

補足日時:2009/01/09 13:34
    • good
    • 0
この回答へのお礼

大変参考になりました。
どうもありがとうございました。
お礼が遅くなり、大変失礼いたしました。

お礼日時:2014/12/17 14:25

こんばんは。



直接の回答ではありませんが、一般的なアドバイスをさせていただきます。これは押し付けがましいので、不愉快になったら、申し訳ありません。もし、よかったら、私の経験的なものですが、納得するものだけを採用してくださったら良いかと思います。

・暗黙のプロパティは、書かないほうがよいです。
 Cells(1,1) や Range("A1") と書くと、自動的に.Value プロパティが選択されますが、8年くらい前のテキストには、それでよかったのですが、入れたほうがよいです。Office TANAKAの田中亨氏のサイトでは、曖昧な書き方になっていますが、おそらく、内容が古いままになっているのだろうと思います。別に、VBAのスピードを上げることと、暗黙のプロパティでよいということは、直接関係ありません。

今回は、関係がありませんが、もし英語が読めるのでしたら、以下のサイトを参考にしてください。非常にためになります。(以前は、日本版がありましたが、現在は、日本語がなくなりました。この内容は、プログラマーズガイドという本のダイジェスト版です)

http://msdn.microsoft.com/en-us/library/aa189065 …
 
 いろんなコードを書いていると、Value, Value2, Text という、使い方によってプロパティの違いを付ける必要がある時があります。
 
・わたし流の書き方では、Borders は、Array(8,9,10,11) などとして、数値でループさせます。( For Each --- In Array(***) )

・ここの掲示板では、区別の付かない人がほとんどですが、Sheets と Worksheets の違いは、つけたほうがよいです。ふだん、その違いでトラブルが発生することはありませんが、区別は付けたほうがよいです。

・VBAエキスパート試験などには良く出てくる内容ですが、
 Dim a, b, c, d As Integer

 というような書き方は、新しい VB.Net の書き方で、VBAでは使われません。a, b, c までは、Variant 型です。すべて、Integer にする場合は、
 
 a As Integer, b As Integer, c As Integer, d As Integer という書き方にします。

 ・長いコードは、標準モジュールにおいて、ローカルモジュールから Call させます。
  それは、ローカルモジュールとグローバルモジュールのメモリ負担の分散だと、私は思っています。多少みにくくなりますし、かなり大きなシステムですと、補助ツールが必要になってしまいます。(例:COMアドイン:MZ-Tools 3.0 http://www.mztools.com/index.aspx ドイツ)
  
 ・Range("A1").Value = Empty と IsEmpty(Range("A1").Value) =True の違いは分かりますか?私は、ワークシートのセルでは、最近は、Range("A1").Value ="" と書いています。理由は、Variant 型には、Empty値, Null値 などがありますが、それは、VBAネイティヴで使うものものだと考えているからなのです。スタイルは自由ですが、私は差別化して使っています。 
 ・イベントの引数のTarget は、ほとんどは、ActiveCell と同等です。ただし、Target.Count =1 とするか、または、Target.Cells(1) で、ActiveCell と同じになります。通常は、引数、Target を利用させます。

 ・私は、通常は、Win32 APIは避けて使っています。それは、新しい資料がなく、元は、C言語からで、なかなか本当に理解できないのです。また、代表的なWebサイトはなくなってしまいました。今後、.Net Framework に以降することが決まっているのと、WMI スクリプト が応用できるので、使えるものは、なるべくそちらを使うようにしています。しかし、.Net Framework は、Office 2003 以降ですが、Office 2007 でもまだ完全対応していません。しかし、次の次世代のOffice は、対応していくことは間違いありません。
 
    • good
    • 0
この回答へのお礼

大変参考になりました。
どうもありがとうございました。
お礼が遅くなり、大変失礼いたしました。

お礼日時:2014/12/17 14:25

補足、修正



同様の内容がありましたので、以下は修正します。

×現在は、日本語がなくなりました。この内容は、プログラマーズガイドという本のダイジェスト版です)

×http://msdn.microsoft.com/en-us/library/aa189065 …
   ↓
http://msdn.microsoft.com/ja-jp/library/cc326739 …
VBA コードを最適化する
    • good
    • 0
この回答へのお礼

大変参考になりました。
どうもありがとうございました。
お礼が遅くなり、大変失礼いたしました。

お礼日時:2014/12/17 14:24

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています