• ベストアンサー

エクセルVBAのSetステートメントについて(長文)

Sub PlusA002() Dim myTgCell As Range Dim myCounter As Integer Set myTgCell = Range("E2") Do Until myTgCell.Value = Empty If myTgCell.Value >= 80 Then myTgCell.Offset(0, 1).Value = "優" ElseIf myTgCell.Value < 80 And myTgCell.Value > 50 Then myTgCell.Offset(0, 1).Value = "良" Else myTgCell.Offset(0, 1).Value = "追試" End If Set myTgCell = myTgCell.Offset(1, 0) Loop End Sub このプログラムを例にして、Setステートメントについて質問します。Set myTgCell = Range("E2")を削除して実行すれば実行エラーという文言がでるし、またSet myTgCell = myTgCell.Offset(1, 0)を削除すればエクセルが固まるし、Setがこのプログラムに対して必要なのは判ります。しかし、Setがどのような役割を果たしているのかVisualBasicEditorのヘルプを見ても判りません。 どのような場面で使えるものなのですか?どなたか教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
  • Wendy02
  • ベストアンサー率57% (3570/6232)
回答No.3

こんばんは。 私も、ちょっと参加させてもらいます。 Set myTgCell = Range("E2") Rangeオブジェクト というのは、セルの位置情報など、もろもろを含めたオブジェクトなので、VBAでは、Set で、変数に渡してあげます。 そして、これは、最初のポイントですよね。Loop で条件を検索したら、次は、どうするのかって言ったら、次のセルに進むのですから、本来は、ActiveCell.Offset(1,0).Select なんですね。だけども、Range("E2")からみていくと、ひとつずつ、下に行くなら、単に、Offset で、+1 足せばよいわけです。 Do ~Loop  は、Until 終了条件や、While ループ条件を立てます。  myTgCell.Offset(i, 0).Value = Empty (本来は、こういう書き方はよくありません)  IsEmpty(myTgCell.Offset(i, 0)) の方がよいです。 サンプルコード Sub TestEmpty()  If ActiveCell.Value = Empty Then MsgBox "ActiveCell.Value = Empty"  If IsEmpty(ActiveCell) Then MsgBox "IsEmpty(ActiveCell)"  If ActiveCell.Value = "" Then MsgBox "ActiveCell.Value =""""" End Sub 例えば、セルに「=""」と入れて、試してみると、IsEmpty(ActiveCell) 以外は、True を返していることが分ります。だから、本当に、Emptyではないことが分ります。 私の考えてみたサンプルコードです。 With ステートメントで、最適化を施しています。 '----------------------------------------- Sub PlusA002R()   Dim myTgCell As Range   Dim i As Long   '最初のセルの設定   Set myTgCell = Range("E2")   '画面の動きを押さえて、コードを速くする   Application.ScreenUpdating = False   'Empty になったらループを離脱   Do Until IsEmpty(myTgCell.Offset(i, 0))    With myTgCell.Offset(i, 0)      If .Value >= 80 Then       .Offset(0, 1).Value = "優"      ElseIf .Value < 80 And .Value > 50 Then       .Offset(0, 1).Value = "良"       Else       .Offset(0, 1).Value = "追試"      End If      'インクリメント      i = i + 1    End With   Loop   '画面の動きを戻す   Application.ScreenUpdating = True  '設定を解除する。  Set myTgCell = Nothing End Sub '-----------------------------------------

syoi198985
質問者

お礼

プログラムにも良くない書き方とかもあるんですね。しりませんでした。回答有難うございました。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (3)

  • imogasi
  • ベストアンサー率27% (4737/17068)
回答No.4

色々難しい理論はありますが ソースレベルで、文字列の置き換えをするのだと、要領的に覚えるのはどうでしょう。 Worksheets("Sheet1").Range("A1")と諸所に何度も書き表すかわりに、それを Set Sh1=Worksheets("Sheet1")として、その後は Sh1.Range("A1")と短く表現できるので、便利であるという風に。 質問の例では別名(Alias)をつけたような利用の仕方ですが、私の例では 包含関係・階層関係を含めてオブジェクトの階層構造が顔を出しています。 一段上に本当はワークブックの階層を前に付け加えるべきなのですが。Set Sh1=WorkBooks("aaa.xls").Worksheets("Sheet1") よく例えでいわれるように、東京都「の」渋谷区「の」南平台を「南平台」で表すことにしようと言ったようなことです。 定数に定数名、変数に変数名、オブジェクトにオブジェクト名をつける、 オブジェクトだけはSetが必須です。 DosのBasic以前では変数への値の代入は Let A=1とかく約束でしたね。 これはVBでも使えるようです。LetとSetが対を成しているのかな。

全文を見る
すると、全ての回答が全文表示されます。
  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.2

Integer とか基本的な型以外の型(オブジェクト型?)の場合に 変数にオブジェクト(の参照)をセットする場合に使います。 Range型のような自体にさまざまなプロパティやメソッドを持っているようなものは、単純に代入ができない(それぞれを代入しないといけなくなる)ので、参照(それを見に行く)ように設定するというような感じ。

syoi198985
質問者

お礼

まだまだ未熟なもので、これから勉強が必要だという事を認識しました。回答有難うございました。

全文を見る
すると、全ての回答が全文表示されます。
  • hana-hana3
  • ベストアンサー率31% (4940/15541)
回答No.1

>Set myTgCell = Range("E2") Range("E2") が myTgCell に変身したのです。 set 文以降は、myTgCell と記述すれば Range("E2") の事になります。 >Set myTgCell = Range("E2")を削除して実行 myTgCell が空になっているので、参照先が見つからないのでエラーになります。 >Set myTgCell = myTgCell.Offset(1, 0)を削除すればエクセルが固まるし これは、セルの内容を比較して任意の判定を記入したあと、この文で「myTgCell を一つ下のセルに変更」して「空欄」があるまで実行されていたものが、同じセルを参照し続ける事になったために「無限ループ」に陥っているためです。

syoi198985
質問者

お礼

回答有難うございました。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • VBAについて

    以下のプログラムは、1年間の価格合計を求めるプログラムです。 これを実行するとうまくいくこともありますが、エラーが起きることもあります。 どうやら下記コードが原因のようなのですが、間違いがわかりません。 Target.Offset(0, 1).Value = run * (13 - month) どこが間違っているのでしょうか。 また最終的に、A行かB行のどちらかが更新されたときにこのプログラムを 実行させたいのですが、方法がわかりません。 無知な質問ではありますが、どなたか教えてください。 --------------------------------------------------------- Private Sub Worksheet_Change(ByVal Target As Range) Dim month As Integer Dim run As Integer If Intersect(Target, Range("A25:A35")) Is Nothing Then Exit Sub Else If Target.Offset(0, -2).Value <> "" Then month = Target.Offset(0, -2).Value month = month - 3 If month = -2 Then month = 10 ElseIf month = -1 Then month = 11 ElseIf month = 0 Then month = 12 End If run = Target.Offset(0, 0).Value Target.Offset(0, 1).Value = run * (13 - month) End If End If End Sub

  • エクセルVBAを教えて下さい

    エクセルの表で -AB C D E F 1年月--1801 2------ 3------ 4------ (-)は空欄でセルE1=18、F1=1とします。 コントロールボックスをつかって Private Sub Command登録_Click() Dim d1 As Long Dim d2 As Long Dim ret As Variant Dim FindValue As String Dim TotalAddress As String If Range("E1").Value = "" Or Range("F1").Value = "" Then MsgBox "該当する場所にデータが入っていません。", vbCritical Exit Sub End If d1 = Range("A65536").End(xlUp).Offset(1).Row d2 = Range("B65536").End(xlUp).Offset(1).Row FindValue = """" & Range("E1").Value & Range("F1").Value & """" TotalAddress = Range("A1").Resize(d1).Address & "&" & Range("B1").Resize(d1).Address ret = Evaluate("MATCH(" & FindValue & "," & TotalAddress & ",0)") If IsError(ret) Then Cells(d1, 1) = Range("E1").Value Cells(d2, 2) = Range("F1").Value Else MsgBox "既に同じ組み合せがあります。", vbInformation End If End Sub というものを作ったのですが、E1=18、F1=1及びコマンドボタンを別シートに作成し、上記の表への登録をできるようにしたいのですが、なにかいい方法はありませんか?

  • エクセルVBA

    A列を基準にBC列が空白ならAの数値を入れて、A>BならB列を更新、A<CならC列を更新 A列が数値以外ならその行をスキップ、という処理をしたいのですが Option Explicit Dim A As Range, B As Range, C As Range Dim i As Long Sub test() For i = 1 To 10 Set A = Cells(i, 1) Set B = A.Offset(0, 1) Set C = A.Offset(0, 2) If IsNumeric(A) Then Else Exit Sub End If If B.Value = "" Then B.Value = A.Value If C.Value = "" Then C.Value = A.Value If A.Value > B.Value Then B.Value = A.Value If A.Value < C.Value Then C.Value = A.Value Next i End Sub とすると数値以外の行の時点で停止してしまいます。 その行を飛ばして次の行に進むにはどうしたらいいのでしょうか?

  • エクセルVBA

    Sub PlusA001() Dim a As Range Dim b As Integer Range("e1").Value = "氏名" Range("e2").Value = "甲" Range("e2").AutoFill Destination:=Range("e2:e10"), Type:=xlFillDefault Range("f1:j1").Value = Array("国", "数", "理", "社", "英") Set a = Range("f2") For i = 1 To 5 Do Until b = 9 a.Value = Int(100 * Rnd) + 1 b = b + 1 Set a = a.Offset(1, 0) Loop b = 0 Set a = a.Offset(-9, 1) Next i End Sub サンプルコードの例ですが、どうも実行しても納得できない部分があります。それはSet a=a.offset(-9,1)の部分です。Set a = Range("f2")においてf2を始点としているのは判りますが、f2からであればa=a.offset(-9、5) とすればいいのかと思い実行したのですが、ぐちゃぐちゃになります。なぜ(-9、5)ではなく(-9、1)何ですか?いくら読み解いても判りません。教えてください。

  • エクセル VBA の質問です。

    A2~A20までのセルに文字を入力した段階で、それぞれB2~B20に入力日時を入れるVBAを以下のように組んでいます。 しかし、同様の条件を同一シートのE2~E20・F2~F20にも入力・自動表示できるようにしなければならなくなり、困っています。 どのように記述を変えればよいのか、教えていただけたらと思います。 宜しく御願いいたします。 Sub Worksheet_change(ByVal Target As Range) Dim Rng As Range Dim c As Range Set Rng = Range("A2:A20") If Intersect(Target,Rng) Is Nothing Then Exit Sub For Each c In Intersect(Target,Rng) If Not IsEmpty(c) Then c.Offset(, 1).Value = Now Else c.Offset(, 1).ClearContents End If Next Rng.Offset(, 1).EntireColumn.AutoFit End Sub 申し訳ありませんが、何卒、宜しく御願いいたします。

  • Excel VBA の件で質問です

    照合システムを作ろうとネットを閲覧していたら次のコードが見つかりました。 Private Sub Worksheet_Change(ByVal Target As Range) Dim rr As Range If Not Intersect(Target, Range("C1:D10")) Is Nothing Then For Each rr In Intersect(Target.EntireRow, Range("C:C")) If Not IsEmpty(rr) And Not IsEmpty(rr.Offset(, 1)) Then Application.EnableEvents = False If rr.Value <> rr.Offset(, 1).Value Then Beep rr.Offset(, 2).Value = "NG" Else rr.Offset(, 2).Value = "OK" End If Application.EnableEvents = True End If Next End If End Sub このコードでいくと、C列とD列が同じであればE列にOK、間違っていればNGなのですが、C1とC2が同じであればE1にOK、間違っていればNG。次にC3とC4が同じであればE3にOK、間違っていればNG。…というふうにしたいのですが、どうすれば良いのでしょうか?

  • エクセルVBAのIf ~ Thenステートメントで

    予約フォームの作成に挑戦しています。 予約日が2022年8月1日の時のみ、シート4に結果を記入して行きたいのですが、 Private Sub CommandButton1_Click() If ListBox1.Text = "44774" Then Sheet4.Select Range("C2").End(xlDown).Offset(1, 0).Select ActiveCell.Value = Reservationform.ListBox2.Value ActiveCell.Offset(0, 1).Value = Reservationform.ListBox3.Value ActiveCell.Offset(0, 2).Value = Reservationform.ListBox4.Value ActiveCell.Offset(0, 2).NumberFormat = Range("C2").NumberFormat Exit Sub End If End Sub で、とりあえず成功しています。 "44774" の部分を、"Sheet6のA2"だった時のみ結果を記入するようにしたいです。 " "の中身をそのまま変えて、 If ListBox1.Text = "sheet6.Range("A2")" Then や If ListBox1.Text = "sheet6.Cells(2, 1).Value" Then に変えてみましたがうまく行きませんでした。 どのようにしたら良いでしょうか?

  • Excel VBA 入力規則

    入力規則を利用して、3つのセルを連携させることを考えていますが、 不適合な値を張り付けらられた場合に拒否をする方法があるのでしょうか。 Private Sub Worksheet_Change(ByVal Target As Range) Dim ad As String Dim ma As Range Dim ma2 As Range Dim r As Range Dim r2 As Range Dim r3 As Range Dim r1 As Range Dim m As Long Dim m2 As Long Application.EnableEvents = False If Target = "" Then Range("F7").Validation.Delete Range("F7") = "" If Target.Address(0, 0) = "B7" Then Range("D7").Validation.Delete Range("D7") = "" End If GoTo EXIT_SUB End If With Worksheets("Sheet1") ad = "A4" Set r = .Range(ad) Set ma = r.MergeArea Set r1 = r.Offset(0, 1) m = Application.Match(Range("B7"), .Range(r1, .Cells(r.Row + ma.Count - 1, r1.Column)), 0) Set r2 = .Cells(r.Row + m - 1, r1.Column) Set ma2 = r2.MergeArea If Target.Address(0, 0) = "B7" Then If ma.MergeCells Then setValiS Target.Offset(0, 2), r2 Range("F7").Validation.Delete Target.Offset(0, 2) = "" Target.Offset(0, 4) = "" Else MsgBox "A列が連結されていません。" End If ElseIf Target.Address(0, 0) = "D7" Then Set r3 = r2.Offset(0, 1) m2 = Application.Match(Target, .Range(r3, .Cells(r2.Row + ma2.Count - 1, r3.Column)), 0) setValiS Target.Offset(0, 2), .Cells(r2.Row + m2 - 1, r3.Column) Target.Offset(0, 2) = "" End If End With EXIT_SUB: Application.EnableEvents = True End Sub Sub setVali2() Dim tc As Range Dim c As Range Set tc = Worksheets("登録").Range("D3") Set c = Worksheets("Sheet1").Range("C3") setValiS tc, c End Sub Sub setValiS(tc As Range, c As Range) Dim ss As String Debug.Print tc.Address, c.Address ss = getChildren(c) If ss > "" Then With tc.Validation .Delete .Add Type:=xlValidateList, Formula1:=getChildren(c) End With End If Worksheets("登録").Activate End Sub Function getChildren(c As Range) Dim c1 As Range Dim ss As String Dim s1 As String Worksheets("Sheet1").Activate ss = "" For Each c1 In c.MergeArea s1 = c1.Offset(0, 1) If s1 <> "" Then ss = ss & "," & s1 Next c1 If ss <> "" Then ss = Mid(ss, 2) Else MsgBox "データがありません!" End If getChildren = ss End Function Sub Outline() Dim CheckRow As Long Dim Moji As String Dim TopRow As Long Dim EndRow As Long With ActiveSheet .Range("A2").ClearOutline .Outline.SummaryRow = xlAbove CheckRow0 = .Range("A" & .Rows.Count).End(xlUp).Row CheckRow = CheckRow0 Do If Moji = "" Then Moji = .Cells(CheckRow, 1).Value EndRow = CheckRow ElseIf yy_mm(CDate(.Cells(CheckRow, 1).Value)) = yy_mm(CDate(Moji)) Then TopRow = CheckRow If TopRow = 1 Then .Rows(TopRow + 1 & ":" & EndRow).Rows.Group Exit Do End If Else .Rows(TopRow + 1 & ":" & EndRow).Rows.Group CheckRow = CheckRow + 1 Moji = "" End If CheckRow = CheckRow - 1 Loop Until CheckRow = 1 .Rows(CheckRow + 1 & ":" & EndRow).Rows.Group .Outline.ShowLevels RowLevels:=1 ExecuteExcel4Macro "SHOW.DETAIL(1," & CheckRow0 & ",TRUE)" End With End Sub Function yy_mm(d As Date) yy_mm = Format(d, "yy/mm") End Function

  • Excel  関数をまたいだTargetの使用

    excel2013 OS はwindows8を使用しています。 Excelのマクロで、以下のコードについて質問です。(コード内の・・・は省略の意) Private Sub Worksheet_Change(ByVal Target As Range) Dim ・・・・・・・      ・      ・  If Target.Value = ・・・・・    Call myfunction End If End Sub Sub myfunction() If 条件 Then Target.Offset(0,1).Value = "aaa" End If End Sub このマクロのPrivate Sub Worksheet_Change(ByVal Target As Range)を実行するとエラーが 出ます。エラーの内容は「オブジェクト変数またはwithブロック変数が設定されていません」 となり、 Target.Offset(,1).Value = "aaa" の行が黄色く表示されます。 myfuncton内で使用しているTargetが何なのかがわからないというエラーだと理解しています。 そこで私は以下の2つを試しました。 1つめはうまくいかず、2つめは何か2度手間のような感じがするのですが、正常動作する ようです。 そこで質問なのですが、Targetをプロシージャをまたいで利用したい場合に一般的に用いら れている手法などがあれば教えて頂きたいです。 1つめ・・Targetをプロシージャをまたいで使えるようにコードの一番上の部分 、つまり、Private Sub Worksheet_Change(ByVal Target As Range)の一行上の 部分にDim Target As Range と宣言してみました。が、これはうまくいきませんでした。 2つめ・・適当な(この場合e)変数を宣言し、プロシージャをまたいで使用する。 Dim e As Range Private Sub Worksheet_Change(ByVal Target As Range) Dim ・・・・・・・      ・      ・  If Target.Value = ・・・・・ Set e = Target    Call myfunction End If End Sub Sub myfunction() If 条件 Then e.Offset(0,1).Value = "aaa" End If End Sub 以上何か良い方法があれば教えて頂きたいです。また不明な点があれば ご質問ください     

  • エクセルVBAで表から行の削除

    添付画像のような表があります。 表はB列の名前でソートされています。 D列の比率をみて、100でないものは、必ず同じ名前で複数行にわかれ合計で100になります。この例では名前CとEとHがそうです。 同じ名前が複数行にわかれている場合、最大の比率の行を残し、他の行(例では、埼玉、栃木、長野、新潟の行)を削除したいのです。 複数行にわかれるのが名前CやEのように2行なら、以下のコードで出来ました。 しかし、めったにはありませんが名前Hのような3行以上に分かれるものには対応できません。 どうすればよいでしょうか? Sub test01()   Dim c As Range   Dim Rng As Range   Set Rng = Range(Cells(2, "D"), Cells(Rows.Count, "D").End(xlUp))   For Each c In Rng '2地区の分担の場合、分担比率高い方を残す。(3地区以上は未対応)2012/08/29     If c.Value <> 100 And c.Offset(1).Value <> 100 Then       If c.Offset(, -2).Value = c.Offset(1, -2).Value Then         If c.Value >= c.Offset(1).Value Then           c.Offset(1).Value = False         Else           c.Value = False         End If       End If     End If   Next   If Application.WorksheetFunction.CountIf(Rng, False) > 0 Then     Rng.SpecialCells(xlCellTypeConstants, xlLogical).EntireRow.Delete   End If End Sub

専門家に質問してみよう