エクセル2010です。
Worksheet_Change イベントで、名前の定義で分岐させようと思います。
下記二つの方法は思いつきましたが、ほかにもっと良い方法はないでしょうか?
実際にはもっとたくさんの名前の定義があります。
・Intersectで見る方法
Private Sub Worksheet_Change(ByVal Target As Range)
Select Case True
Case Not Application.Intersect(Target, Range("見積日")) Is Nothing
Range("有効期限").Value = Range("見積日").Value + 60
Case Not Application.Intersect(Target, Range("Trigger")) Is Nothing
If Target(1).Value = "AAAA" Then '(1)は結合セルクリア対策
MsgBox "BBBBを入力してください。"
Range("BBBB").Select
Else
Range("BBBB").MergeArea.ClearContents
End If
Case Not Application.Intersect(Target, Range("BBBB")) Is Nothing
If Target(1).Value = "日付入力" Then
Range("BBBB").Value = InputBox("日付を入力してください。")
End If
End Select
End Sub
・アドレスで見る方法
Private Sub Worksheet_Change(ByVal Target As Range)
Select Case True
Case Target(1).Address = Range("見積日")(1).Address
Range("有効期限").Value = Range("見積日").Value + 60
Case Target(1).Address = Range("Trigger")(1).Address
If Target(1).Value = "AAAA" Then '(1)は結合セルクリア対策
MsgBox "BBBBを入力してください。"
Range("BBBB").Select
Else
Range("BBBB").MergeArea.ClearContents
End If
Case Target(1).Address = Range("BBBB")(1).Address
If Target(1).Value = "日付入力" Then
Range("BBBB").Value = InputBox("日付を入力してください。")
End If
End Select
End Sub
#2.cjです。
....あいかわらず、タイプミスが多くて(^^;スミマセン。
こちらからの補足が2点あります。
またうっかり説明抜きでそのまま書いてしまいましたが、
_Change イベントで、.Value を変更する場合は、
Application.EnableEvents = False
.......、.Value = .......
Application.EnableEvents = True
のように書くのが基本です。
再び、_Change イベントが呼び出されることで無限ループ構造を作ること
を事前に避けておくものです。
ただ、ご提示のコードを見ると、
必ずしも無くてもいいようなケースなのかも?
ご承知の上で書かれたものなのでしょうし、、、
ということで、こちらも省いて書いています。
ただ、回答するものとしては、何の断りもなく省いてはいけませんね。
失礼しました。
> 名前を付けた範囲と完全に一致するセル範囲を指定した場合
や
> 名前を付けたセル範囲が、もしも、単一セルならば
という場合の記述ですが、普通に、
On Error Resume Next
sNameName = Target(1).Name.Name
On Error GoTo 0
If sNameName = "" Then Exit Sub
Select Case sNameName
・・・
という風になります。
ちょっと説明不足だったかなと思いました。
以上です。
質問者
お礼
cj_moverさん、いつもありがとうございます。
とても勉強になるコードを教えていただきました。
IDというのは面白いですね。それでやって見ましたが、よく考えると「名前の定義」を「ID」にするわけですから、「名前の定義」そのものを使っても同じことですよね?
現在、名前の定義が設定されているのが、単一セル(多数)、結合セル(多数)、そしてセル範囲もあります。
それで以下のようにして見ました。
すると一つだけ問題をみつけました。
「名前の定義」が単一セルは問題はありません。
結合セルの場合、セルを結合してから名前ボックスで名前を定義したものは問題ありません。
セル範囲に名前を定義してから結合するとTarget(1).Name.Nameでは名前が取得できません。
名前の管理で参照範囲を変更しなければいけませんでした。
以下のコードのRange("CODE")はセル範囲ですが、結合はしていませんので問題ありません。
ほかになにか問題はあるでしょうか?
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myStr As String
On Error Resume Next
myStr = Target(1).Name.Name
On Error GoTo 0
Application.EnableEvents = False
If myStr <> "" Then
Select Case myStr
Case "見積日"
Range("有効期間").Value = Range("見積日").Value + 60
Case "Trigger"
If Target(1).Value = "AAAA" Then
MsgBox "BBBBを入力してください。"
Range("BBBB").Select
Else
Range("BBBB").MergeArea.ClearContents
End If
Case "BBBB"
If Target(1).Value = "日付入力" Then
Range("BBBB").Value = InputBox("日付を入力してください。")
End If
End Select
Else
If Not Application.Intersect(Target, Range("CODE")) Is Nothing Then
If MsgBox("その表を変更して本当にだいじょうぶですね?", vbYesNo + vbQuestion) = vbNo Then
Application.Undo
End If
End If
End If
Application.EnableEvents = True
End Sub
emaxemaxさん、こんにちは(^^)
んー、結構よく書けてると思いました。
ただ、設計としては少し強引なのかな?とも思います。
といって、こちらも決定的な解決策を提示できるかは、疑問なのですが。
まずは、テスト
Sub test1_8272726()
Dim sRef As String
Debug.Print Range("見積日").Name.Name
sRef = Mid(Range("見積日").Name.RefersTo, 2)
Debug.Print sRef
' ' アドレスで指定したRange、に設定された名前
Debug.Print Range(sRef).Name.Name
End Sub
一応こんな感じで、
名前を付けた範囲と完全に一致するセル範囲を指定した場合は
.Name.Name (.Nameオブジェクト(Rangeの.Nameプロパティ)の.Nameプロパティ)で、
セル範囲に設定された名前を取得できます。
なので、名前を付けたセル範囲が、もしも、単一セルならば、
ここまでの説明で解決しちゃうのかも知れません。
問題は
名前を付けたセル範囲が、単一セルでない場合です。
任意のセル範囲が、名前を付けた範囲に含まれているかどうか、
を返す直接的なプロパティやメソッドは用意されていません。
ちょっと話逸れますが、私個人の方法論としては、
頻繁に呼び出されるイベントプロシージャの記述に、
Intersect メソッドは、遅くて怖いので出来れば使いたくありません、
.Address プロパティも、.Count や .Row や .Column と比較すれば
桁違いに遅いので、優先度を下げて(或いは質問者に合わせて)
限定的に使う位です。
# いや、ご提示のコードは、ある意味、必要に迫られて書いているので、
私的にもオッケーなのですが、、、。
一言でいうと、イベントプロシージャは、速く、抜ける、
というのが、私の日頃からの拘りですし、実務でも実践しています。
そういう考えの元、何か、他に、速そうな方法はないかと、考えた結果、
ID プロパティを用いる方法(システム)を提案することにしました。
まず、標準モジュr-ル
Private Sub LetNamedCellsID() ' ID プロパティ 設定
Dim oNames As Names
Dim oName As Name
Dim r As Range
Dim sNameName
On Error Resume Next
For Each oName In ThisWorkbook.Names
sNameName = oName.Name
' ' シート の名前 の(ブック の名前 じゃない)場合は、
' ' "Sheet1!見積日"という名前が返るので、
' ' "Sheet1!"を除いて、"見積日"にしたい場合は、この行 ↓ イキ
If InStr(sNameName, "!") Then sNameName = Split(sNameName, "!")(1)
' ' 先頭セルにのみID設定 (1/2者)
oName.RefersToRange(1).ID = sNameName
' ' 名前がついたセル範囲すべてにID設定 (2/2者)
' For Each r In oName.RefersToRange
' r.ID = sNameName
' Next
Next
End Sub
Private Sub PrintNamedCellsID() ' ID プロパティ 確認用
Dim oNames As Names
Dim oName As Name
Dim r As Range
Dim sNameName
On Error Resume Next
For Each oName In ThisWorkbook.Names
sNameName = oName.Name
For Each r In oName.RefersToRange
If r.ID <> "" Then Debug.Print r.Address(0, 0), r.ID
Next
Next
End Sub
Private Sub EraseCellsID() ' ID プロパティ 消去
Dim oNames As Names
Dim oName As Name
Dim r As Range
Dim sNameName
On Error Resume Next
For Each oName In ThisWorkbook.Names
For Each r In oName.RefersToRange
r.ID = ""
Next
Next
End Sub
Sub LetNamedCellsID を実行して、
名前の付いたセル範囲の[先頭orすべての]セルについて
ID プロパティ に、名前を設定します。
次に
Sub PrintNamedCellsID を実行して、
ID プロパティ が設定されたセル範囲の
アドレスとID プロパティを確認します。
Sub EraseCellsID は一応、お行儀よく付け足しただけです。
ID プロパティ はブックの保存とは無縁に、
ブックを閉じれば消えて無くなります。
ここまでが準備です。
これをイベントプロシージャでどう活用するか、
イメージし易いように書いたテスト用サンプルが以下、
(シートモジュール)
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target(1).ID <> "" Then MsgBox Target(1).ID
End Sub
名前の付いたセル範囲やその周辺をあちらこちら選択して
試してみてください。
Target.IDで、文字列値が返ります。
結構シンプルだと思いませんか?
テスト用サンプルは破棄してください。
では、本題です。(事前に Sub LetNamedCellsID を実行)
Private Sub Worksheet_Change(ByVal Target As Range)
Select Case Target(1).ID
Case "見積日"
Range("有効期限").Value = Target.Value + 60
Case "Trigger"
If Target(1).Value = "AAAA" Then
MsgBox "BBBBを入力してください。"
Range("BBBB").Select
Else
Range("BBBB").MergeArea.ClearContents
End If
Case "BBBB"
If Target(1).Value = "日付入力" Then
Range("BBBB").Value = InputBox("日付を入力してください。")
End If
End Select
End Sub
といった感じにで、軽くシンプルに速く抜けるように書けたと思います。
ブックを開く度に ID プロパティ を設定し直さないといけないのですが、
処理は速いです。
ThisWorkbookモジュール
Private Sub Workbook_Open()
Application.OnTime Now, "LetNamedCellsID"
End Sub
プロパティが無いならプロパティを作ってしまえ、的なやり方な訳ですが、
如何せん ID プロパティ 自体が、ExcelをExcelファイルとして使ってるうちは
日陰の存在、というか、レガシーなのかも?
なので、堂々と発信するものでもないのかも知れませんが、
工夫で何とかするとしたら、こんなのもアリ、だとは思います。
同じ発想でクラスモジュールを使うって人も、もしかしたら居るかも知れませんが、
却って重くなりそうな予感がして、私は考えてません。
以上、ご参考まで。
では、また。
お礼
こんばんは。 とても丁寧にわかりやすくご教示いただき、本当にありがとうございました。 これでなんとか上手くいきそうです。 これからもご指導をよろしくお願いいたします。