私はExcel VBAで.Select
を使うことの理解し難いことについて多くのことを耳にしましたが、それを使わないようにする方法がわからないです。 Select
関数の代わりに変数を使用することができれば、私のコードはより再利用可能になると思います。ただし、ActiveCell
を使用しない場合、(Select
などの)ものを参照する方法がわかりません。
私は 範囲に関するこの記事 そして selectを使用しないことの利点に関するこの例 しかしhowでは何も見つけることができませんか?
Selectを避ける方法のいくつかの例
Dim
'd個の変数を使用する
Dim rng as Range
Set
は、必要な範囲への変数です。単一セル範囲を参照するには多くの方法があります。
Set rng = Range("A1")
Set rng = Cells(1,1)
Set rng = Range("NamedRange")
または複数セル範囲
Set rng = Range("A1:B10")
Set rng = Range("A1", "B10")
Set rng = Range(Cells(1,1), Cells(10,2))
Set rng = Range("AnotherNamedRange")
Set rng = Range("A1").Resize(10,2)
can Evaluate
メソッドへのショートカットを使用することができますが、これは効率が悪く、通常はプロダクションコードでは避けるべきです。
Set rng = [A1]
Set rng = [A1:B10]
上記の例はすべて、 アクティブシート のセルを参照しています。アクティブシートだけで作業したいのでなければ、Worksheet
変数も薄暗くしたほうがいいでしょう。
Dim ws As Worksheet
Set ws = Worksheets("Sheet1")
Set rng = ws.Cells(1,1)
With ws
Set rng = .Range(.Cells(1,1), .Cells(2,10))
End With
もしあなたが do をActiveSheet
と一緒に使いたいのなら、明確にするために明示的にするのが最善です。しかしWorksheet
メソッドの中にはアクティブシートを変更するものがあるので注意してください。
Set rng = ActiveSheet.Range("A1")
繰り返しますが、これは アクティブなワークブック を指します。特にActiveWorkbook
またはThisWorkbook
のみを使用したい場合を除き、Workbook
変数もDimを使用することをお勧めします。
Dim wb As Workbook
Set wb = Application.Workbooks("Book1")
Set rng = wb.Worksheets("Sheet1").Range("A1")
もしあなたが do をActiveWorkbook
と一緒に使いたいのなら、明確にするために明示的にするのが最善です。しかし、多くのWorkBook
メソッドがアクティブブックを変更するので、注意してください。
Set rng = ActiveWorkbook.Worksheets("Sheet1").Range("A1")
実行コードを含む本を参照するためにThisWorkbook
オブジェクトを使用することもできます。
Set rng = ThisWorkbook.Worksheets("Sheet1").Range("A1")
よくある(悪い)コードは、本を開いてデータを取得してからもう一度閉じることです。
これは悪いです:
Sub foo()
Dim v as Variant
Workbooks("Book1.xlsx").Sheets(1).Range("A1").Clear
Workbooks.Open("C:\Path\To\SomeClosedBook.xlsx")
v = ActiveWorkbook.Sheets(1).Range("A1").Value
Workbooks("SomeAlreadyOpenBook.xlsx").Activate
ActiveWorkbook.Sheets("SomeSheet").Range("A1").Value = v
Workbooks(2).Activate
ActiveWorkbook.Close()
End Sub
そしてより良いようになります:
SUb foo()
Dim v as Variant
Dim wb1 as Workbook
Dim wb2 as Workbook
Set wb1 = Workbooks("SomeAlreadyOpenBook.xlsx")
Set wb2 = Workbooks.Open("C:\Path\To\SomeClosedBook.xlsx")
v = wb2.Sheets("SomeSheet").Range("A1").Value
wb1.Sheets("SomeOtherSheet").Range("A1").Value = v
wb2.Close()
End Sub
Range変数としてあなたのSub
とFunction
に範囲を渡します
Sub ClearRange(r as Range)
r.ClearContents
'....
End Sub
Sub MyMacro()
Dim rng as Range
Set rng = ThisWorkbook.Worksheets("SomeSheet").Range("A1:B10")
ClearRange rng
End Sub
また、メソッドにFind
やCopy
などのメソッドを適用する必要があります。
Dim rng1 As Range
Dim rng2 As Range
Set rng1 = ThisWorkbook.Worksheets("SomeSheet").Range("A1:A10")
Set rng2 = ThisWorkbook.Worksheets("SomeSheet").Range("B1:B10")
rng1.Copy rng2
ある範囲のセルをループしている場合は、最初に範囲の値をバリアント配列にコピーしてからループする方が良い(速い)方法がよくあります。
Dim dat As Variant
Dim rng As Range
Dim i As Long
Set rng = ThisWorkbook.Worksheets("SomeSheet").Range("A1:A10000")
dat = rng.Value ' dat is now array (1 to 10000, 1 to 1)
for i = LBound(dat, 1) to UBound(dat, 1)
dat(i,1) = dat(i,1) * 10 'or whatever operation you need to perform
next
rng.Value = dat ' put new values back on sheet
これは可能なことの小さなテイスターです。
.Select
/.Activate
/Selection
/Activecell
/Activesheet
/Activeworkbook
etc ...を避けるべき2つの主な理由
どうすればそれを回避できますか?
1) 関連するオブジェクトを直接操作する
このコードを考えてください
Sheets("Sheet1").Activate
Range("A1").Select
Selection.Value = "Blah"
Selection.NumberFormat = "@"
このコードは次のように書くこともできます。
With Sheets("Sheet1").Range("A1")
.Value = "Blah"
.NumberFormat = "@"
End With
2) 必要ならば変数を宣言してください。上記と同じコードを次のように書くことができます。
Dim ws as worksheet
Set ws = Sheets("Sheet1")
With ws.Range("A1")
.Value = "Blah"
.NumberFormat = "@"
End With
ちょっとした強調点上記のすべての優れた回答に追加します。
おそらく、Selectを使用しないようにするためにできる最大のことは、できる限り、VBAコードで名前付き範囲を(意味のある変数名と組み合わせて)使用する)にすることです。少し;しかし、それは特別な注意に値する。
ここで、名前付き範囲を自由に使用するための2つの追加の理由がありますが、私はもっと考えることができると確信しています。
例:
Dim Months As Range
Dim MonthlySales As Range
Set Months = Range("Months")
'e.g, "Months" might be a named range referring to A1:A12
Set MonthlySales = Range("MonthlySales")
'e.g, "Monthly Sales" might be a named range referring to B1:B12
Dim Month As Range
For Each Month in Months
Debug.Print MonthlySales(Month.Row)
Next Month
名前付き範囲Months
とMonthlySales
に何が含まれているのか、そしてプロシージャが何をしているのかは明らかです。
何でこれが大切ですか?部分的には他の人にとって理解しやすいためですが、あなたが自分のコードを見たり使ったりする唯一の人であっても、YOU WILL FORGET1年後にそれをどうするつもりだったのか、そしてあなたは無駄になるでしょう30分で、コードが何をしているのか理解することができます。
上記の例が次のように書かれているとします。
Dim rng1 As Range
Dim rng2 As Range
Set rng1 = Range("A1:A12")
Set rng2 = Range("B1:B12")
Dim rng3 As Range
For Each rng3 in rng1
Debug.Print rng2(rng3.Row)
Next rng3
このコードは最初はうまくいくでしょう - それはあなたか将来のユーザーが "gee wiz、私はColumn A
に年の新しいコラムを追加するつもりだ"と決めるまで、あるいは月の間に経費コラムを入れるまでまたはsales列をクリックするか、各列にヘッダーを追加します。今、あなたのコードは壊れています。そして、あなたがひどい変数名を使ったので、それを修正する方法を理解するのにそれが取るべきであるよりはるかに多くの時間がかかります。
最初に名前付き範囲を使用していた場合は、Months
列とSales
列を好きな場所に移動でき、コードは正常に機能し続けます。
他のみんなが長い答えを出したので、私は短い答えを出すつもりです。
マクロを記録してそれらを再利用するときはいつでも.selectと.activateが得られます。セルまたはシートを選択すると、アクティブになります。それ以降は、Range.Value
のような修飾されていない参照を使用するときはいつでも、アクティブセルとシートのみを使用します。コードが配置されている場所やユーザーがブックをクリックした場所を見ていない場合も、これは問題になります。
そのため、セルを直接参照することでこれらの問題を解消できます。それは行きます:
'create and set a range
Dim Rng As Excel.Range
Set Rng = Workbooks("Book1").Worksheets("Sheet1").Range("A1")
'OR
Set Rng = Workbooks(1).Worksheets(1).Cells(1, 1)
それともあなたができる
'Just deal with the cell directly rather than creating a range
'I want to put the string "Hello" in Range A1 of sheet 1
Workbooks("Book1").Worksheets("Sheet1").Range("A1").value = "Hello"
'OR
Workbooks(1).Worksheets(1).Cells(1, 1).value = "Hello"
これらの方法にはさまざまな組み合わせがありますが、それは私のようなせっかちな人にはできるだけ早く表現される一般的な考え方です。
以下では、Selectアプローチ(OPが回避したいアプローチ)とRangeアプローチを比較しています(そしてこれが質問に対する答えです)。それで、あなたが最初のSelectを見たときに読むのを止めないでください。
それは本当にあなたがやろうとしていることによります。とにかく簡単な例が役に立つでしょう。アクティブセルの値を "foo"に設定したいとしましょう。 ActiveCellを使用すると、このようなものを書くでしょう:
Sub Macro1()
ActiveCell.Value = "foo"
End Sub
アクティブではないセル、たとえば "B2"に使用する場合は、最初に選択する必要があります。
Sub Macro2()
Range("B2").Select
Macro1
End Sub
範囲を使用すると、任意のセルの値を任意の値に設定するために使用できる、より一般的なマクロを作成できます。
Sub SetValue(cellAddress As String, aVal As Variant)
Range(cellAddress).Value = aVal
End Sub
その後、Macro2を次のように書き直すことができます。
Sub Macro2()
SetCellValue "B2", "foo"
End Sub
そしてMacro1は次のようになります。
Sub Macro1()
SetValue ActiveCell.Address, "foo"
End Sub
これが少し物事を片付けるのに役立つことを願っています。
「...そして、Select関数の代わりに変数を使用することができれば、私のコードはより再利用可能になるでしょう。」
.Select
が直接セル参照よりも優れた選択であるという、ほんの一握りの状況以上のことを考えることはできませんが、Selection
を防御することにし、.Select
がそうであるのと同じ理由で捨てられるべきではないと指摘します。避けてください。
いくつかのキーをタップするだけで使用できるホットキーの組み合わせに割り当てられた、短くて時間を節約するマクロサブルーチンを使用すると、多くの時間を節約できます。ワークシート全体のデータ形式に準拠していないポケットデータを処理するときに、作業上の操作コードを制定するためにセルのグループを選択できることは驚きです。セルのグループを選択してフォーマットの変更を適用するのと同じように、特別なマクロコードを実行するためのセルのグループを選択することは、大きな時間の節約になります。
選択ベースのサブフレームワークの例:
Public Sub Run_on_Selected()
Dim rng As Range, rSEL As Range
Set rSEL = Selection 'store the current selection in case it changes
For Each rng In rSEL
Debug.Print rng.Address(0, 0)
'cell-by-cell operational code here
Next rng
Set rSEL = Nothing
End Sub
Public Sub Run_on_Selected_Visible()
'this is better for selected ranges on filtered data or containing hidden rows/columns
Dim rng As Range, rSEL As Range
Set rSEL = Selection 'store the current selection in case it changes
For Each rng In rSEL.SpecialCells(xlCellTypeVisible)
Debug.Print rng.Address(0, 0)
'cell-by-cell operational code here
Next rng
Set rSEL = Nothing
End Sub
Public Sub Run_on_Discontiguous_Area()
'this is better for selected ranges of discontiguous areas
Dim ara As Range, rng As Range, rSEL As Range
Set rSEL = Selection 'store the current selection in case it changes
For Each ara In rSEL.Areas
Debug.Print ara.Address(0, 0)
'cell group operational code here
For Each rng In ara.Areas
Debug.Print rng.Address(0, 0)
'cell-by-cell operational code here
Next rng
Next ara
Set rSEL = Nothing
End Sub
実際に処理するコードは、単一行から複数のモジュールまでです。この方法を使用して、外部ワークブックのファイル名を含む不規則なセルの選択で長時間実行されるルーチンを開始しました。
要するに、Selection
は.Select
およびActiveCell
と密接に関連しているため、捨てないでください。ワークシートのプロパティとして、他にも多くの目的があります。
(はい、この質問はSelection
ではなく.Select
に関するものであったことを知っていますが、初心者のVBAコーダーが推測していたかもしれない誤解を取り除きたかったのです。)
Select
とActivate
を避けることは、VBA開発者を少し良くする動きです。一般に、マクロが記録されるときにSelect
とActivate
が使用されます。したがって、Parent
ワークシートまたは範囲は常にアクティブなものと見なされます。
これは、次のような場合にSelect
とActivate
を回避する方法です。
From(マクロレコーダーで生成されたコード):
Sub Makro2()
Range("B2").Select
Sheets.Add After:=ActiveSheet
Sheets("Tabelle1").Select
Sheets("Tabelle1").Name = "NewName"
ActiveCell.FormulaR1C1 = "12"
Range("B2").Select
Selection.Copy
Range("B3").Select
ActiveSheet.Paste
Application.CutCopyMode = False
End Sub
に:
Sub TestMe()
Dim ws As Worksheet
Set ws = Worksheets.Add
With ws
.Name = "NewName"
.Range("B2") = 12
.Range("B2").Copy Destination:=.Range("B3")
End With
End Sub
から:
Sheets("Source").Select
Columns("A:D").Select
Selection.Copy
Sheets("Target").Select
Columns("A:D").Select
ActiveSheet.Paste
に:
Worksheets("Source").Columns("A:D").Copy Destination:=Worksheets("Target").Range("a1")
あなたは[]
でそれらにアクセスすることができます。他の方法と比べて、これは本当に美しいです。自分自身で調べて:
Dim Months As Range
Dim MonthlySales As Range
Set Months = Range("Months")
Set MonthlySales = Range("MonthlySales")
Set Months =[Months]
Set MonthlySales = [MonthlySales]
上記の例は次のようになります。
Worksheets("Source").Columns("A:D").Copy Destination:=Worksheets("Target").[A1]
通常、もしあなたがselect
を喜んで使うのであれば、おそらくあなたは何かをコピーしているでしょう。値にだけ興味があるなら、これはselectを避けるための良い選択肢です:
Range("B1:B6").Value = Range("A1:A6").Value
これはおそらく vba の最も一般的な間違いです。範囲をコピーするときはいつでもワークシートが参照されないため、VBAはActiveWorksheetを考慮します。
'This will work only if the 2. Worksheet is selected!
Public Sub TestMe()
Dim rng As Range
Set rng = Worksheets(2).Range(Cells(1, 1), Cells(2, 2)).Copy
End Sub
'This works always!
Public Sub TestMe2()
Dim rng As Range
With Worksheets(2)
.Range(.Cells(1, 1), .Cells(2, 2)).Copy
End With
End Sub
.Select
や.Activate
を何かのために使うことはできませんか?.Activate
と.Select
を使用することが正当化されるのは、視覚的な理由から特定のワークシートが選択されていることを確認するときだけです。たとえば、Excelは常に最初に選択されたカバーワークシートで開き、ファイルが閉じられたときにどちらがアクティブシートだったかは無視します。したがって、このようなものは絶対に問題ありません。
Private Sub Workbook_Open()
Worksheets("Cover").Activate
End Sub
ワークブック、ワークシート、セル/範囲を必ず明記してください。
例えば:
Thisworkbook.Worksheets("fred").cells(1,1)
Workbooks("bob").Worksheets("fred").cells(1,1)
エンドユーザーは常にボタンをクリックするだけで、フォーカスがワークブックから外れるとすぐにコードが処理したいので、事態は完全に失敗します。
また、ワークブックのインデックスは絶対に使用しないでください。
Workbooks(1).Worksheets("fred").cells(1,1)
ユーザーがコードを実行したときに他のどのブックが開かれるのかわかりません。
これらのメソッドはどちらかというと汚名なので、@ Vityataと@Jeepedのリードを取って砂の中に線を引きます。
.Activate
、.Select
、Selection
、ActiveSomething
の各メソッド/プロパティを呼び出さないでください。基本的に、それらは主にアプリケーションUIを介したユーザー入力を処理するために呼び出されるからです。これらはユーザーがUIを通じてオブジェクトを処理するときに呼び出されるメソッドであるため、マクロレコーダーによって記録されたものであり、ほとんどの場合、それらを呼び出すのは脆弱または冗長です。直後にSelection
でアクションを実行するようにしてください。
しかし、この定義は、それらが求められる状況を解決します。
.Activate
、.Select
、.Selection
、.ActiveSomething
の各メソッド/プロパティを呼び出すタイミング基本的に 最終ユーザー が実行において役割を果たすことを期待するとき。
あなたが開発していて、ユーザがあなたのコードが扱うオブジェクトインスタンスを選ぶことを期待しているなら、.Selection
または.ActiveObject
が適切です。
一方、.Select
と.Activate
は、ユーザーの次の行動を推論することができ、あなたのコードでユーザーをガイドしたいときに役立ちます。たとえば、コードでチャートのまったく新しいインスタンスを作成した場合や更新した場合は、ユーザーがそれをチェックアウトし、そのシートまたはシート上で.Activate
を呼び出して、ユーザーが検索する時間を節約できます。あるいは、ユーザーがいくつかの範囲値を更新する必要があることがわかっている場合は、プログラムでその範囲を選択できます。
素早い回答:
.Select
メソッドを使用しないようにするには、必要なプロパティに等しい変数を設定します。
►たとえば、Cell A1
の値が必要な場合は、そのセルのvalueプロパティに等しい変数を設定できます。
valOne = Range("A1").Value
►たとえば、 'Sheet3`のコードネームが必要な場合は、そのワークシートのcodenameプロパティに等しい変数を設定できます。
valTwo = Sheets("Sheet3").Codename
それが役立つことを願っています。質問があれば教えてください。
私のように.select
の使い方は、マクロを記録し、それから.select
とそれに続くselection
が単なる不必要な仲介者であることに気づかずに必要に応じてVBAを学び始めた人々から来ています。
.select
は、すでに投稿されているように、既存のオブジェクトを直接操作することで回避できます。これにより、複雑な方法でiとjを計算してcell(i、j)を編集するなどの間接参照が可能になります。
そうでなければ、.select
自体に暗黙のうちに間違っているものは何もありません。私は、日付を入れてスプレッドシートを作成し、それに魔法をかけて別のシートに適切な形式でエクスポートするマクロをアクティブにします。ただし、隣接するセルへの最終的な手動の(予測不可能な)入力が必要です。それで、ここに.select
の瞬間が来ました。それは私にその追加のマウスの動きとクリックを救ってくれます。
私はこれらの答えのどれも .Offsetプロパティ に言及していないことに気づいた。特に選択されたセルに関して(OPがSelect
で述べているように)特定のセルを操作するときにActiveCell
アクションを使わないようにするためにこれを使うこともできます。
ここにいくつかの例があります。
また、 "ActiveCell"が J4 であると仮定します。
ActiveCell.Offset(2, 0).Value = 12
J6
を値12に変更しますActiveCell.Offset(0,1).Copy ActiveCell.Offset(,2)
k4
のセルをL4
にコピーします。i4
になります。ActiveCell.Offset(, -1).EntireColumn.ClearContents
これらは上記のオプションよりも「優れている」と言っているのではなく、単に選択肢を列挙しているだけです。
これは、セル "A1"の内容(選択の種類がxllastcellなどの場合はそれ以上)を消去する例です。セルを選択しなくてもすべて完了です。
Application.GoTo Reference:=Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1")
Range(Selection,selection(selectiontype)).clearcontents
これが誰かに役立つことを願っています。