วิธีหลีกเลี่ยงการใช้ Select ใน Excel VBA
ฉันได้ยินมามากเกี่ยวกับความเกลียดชังที่เข้าใจได้ในการใช้.Select
Excel VBA แต่ฉันไม่แน่ใจว่าจะหลีกเลี่ยงการใช้งานอย่างไร ฉันพบว่าโค้ดของฉันจะใช้งานได้อีกครั้งหากฉันสามารถใช้ตัวแปรแทนSelect
ฟังก์ชันได้ แต่ผมไม่แน่ใจว่าวิธีการที่จะอ้างถึงสิ่งที่ (เช่นActiveCell
ฯลฯ ) 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)
คุณสามารถใช้ทางลัดไปยัง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
หากคุณไม่ต้องการที่จะทำงานร่วมกับActiveSheet
เพื่อความชัดเจนดีที่สุดที่จะมีความชัดเจน แต่โปรดระวังเนื่องจากWorksheet
วิธีการบางอย่างเปลี่ยนแผ่นงานที่ใช้งานอยู่
Set rng = ActiveSheet.Range("A1")
อีกครั้งนี่หมายถึงสมุดงานที่ใช้งานอยู่ เว้นแต่ว่าคุณต้องการทำงานเฉพาะกับActiveWorkbook
หรือThisWorkbook
เท่านั้นควรหรี่Workbook
ตัวแปรด้วย
Dim wb As Workbook
Set wb = Application.Workbooks("Book1")
Set rng = wb.Worksheets("Sheet1").Range("A1")
หากคุณไม่ต้องการที่จะทำงานร่วมกับ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
ส่งช่วงไปยังSub
s และFunction
s ของคุณเป็นตัวแปรช่วง:
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
ฯลฯ ควรหลีกเลี่ยง
- ทำให้โค้ดของคุณช้าลง
- มักเป็นสาเหตุหลักของข้อผิดพลาดรันไทม์
เราจะหลีกเลี่ยงได้อย่างไร?
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
จุดเน้นเล็ก ๆ จุดหนึ่งที่ฉันจะเพิ่มให้กับคำตอบที่ยอดเยี่ยมทั้งหมดที่ให้ไว้ก่อนหน้านี้:
น่าจะเป็นสิ่งที่ยิ่งใหญ่ที่สุดที่คุณสามารถทำได้เพื่อหลีกเลี่ยงการใช้เลือกที่จะมากที่สุดเท่าที่เป็นไปได้ใช้ชื่อช่วง (รวมกับชื่อตัวแปรที่มีความหมาย) ในรหัส จุดนี้ได้กล่าวไว้ข้างต้น แต่มันถูกปัดสวะไปเล็กน้อย อย่างไรก็ตามควรได้รับความสนใจเป็นพิเศษ
ต่อไปนี้เป็นเหตุผลเพิ่มเติมสองสามประการในการใช้ช่วงที่ตั้งชื่ออย่างเสรีแม้ว่าฉันมั่นใจว่าจะคิดได้มากกว่านี้
ช่วงที่ตั้งชื่อช่วยให้อ่านและเข้าใจโค้ดของคุณได้ง่ายขึ้น
ตัวอย่าง:
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
มีอะไรบ้างและขั้นตอนกำลังทำอะไรอยู่
เหตุใดสิ่งนี้จึงสำคัญ ส่วนหนึ่งเป็นเพราะคนอื่นเข้าใจง่ายกว่า แต่ถึงแม้ว่าคุณจะเป็นคนเดียวที่เคยเห็นหรือใช้โค้ดของคุณคุณก็ยังควรใช้ช่วงที่ตั้งชื่อและชื่อตัวแปรที่ดีเพราะคุณจะลืมว่าคุณตั้งใจจะทำอะไรกับมัน หนึ่งปีต่อมาและคุณจะเสียเวลา 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 ฉันคิดว่าฉันจะเพิ่มคอลัมน์ใหม่ที่มีปีในคอลัมน์A
!" หรือใส่คอลัมน์ค่าใช้จ่ายระหว่างเดือนและ คอลัมน์การขายหรือเพิ่มส่วนหัวให้กับแต่ละคอลัมน์ ตอนนี้รหัสของคุณเสีย และเนื่องจากคุณใช้ชื่อตัวแปรที่น่ากลัวคุณจึงต้องใช้เวลาในการหาวิธีแก้ไขมากกว่าที่ควรจะเป็น
หากคุณใช้ช่วงที่ตั้งชื่อเพื่อเริ่มต้นด้วยคุณสามารถย้ายคอลัมน์Months
และSales
คอลัมน์ไปรอบ ๆ สิ่งที่คุณต้องการได้และโค้ดของคุณจะทำงานต่อไปได้ดี
ฉันจะให้คำตอบสั้น ๆ เนื่องจากคนอื่นให้คำตอบแบบยาว
คุณจะได้รับ. เลือกและ. เปิดใช้งานทุกครั้งที่คุณบันทึกมาโครและนำมาใช้ใหม่ เมื่อคุณ. เลือกเซลล์หรือแผ่นงานมันก็ทำให้มันใช้งานได้ ตั้งแต่นั้นเป็นต้นมาเมื่อใดก็ตามที่คุณใช้การอ้างอิงที่ไม่มีเงื่อนไขเช่น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 ได้"
ในขณะที่ฉันไม่สามารถนึกถึงสถานการณ์ใด ๆ ที่.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
ในฐานะที่เป็นคุณสมบัติของแผ่นงานมีวัตถุประสงค์อื่น ๆ อีกมากมาย
(ใช่ฉันรู้ว่าคำถามนี้เป็นเรื่องที่.Select
ไม่Selection
แต่ฉันต้องการที่จะลบความเข้าใจผิดใด ๆ ที่ coders สามเณร VBA อาจอนุมาน.)
การหลีกเลี่ยงSelect
และActivate
เป็นการย้ายที่ทำให้คุณเป็นนักพัฒนา VBA ที่ดีขึ้นเล็กน้อย โดยทั่วไปSelect
และActivate
จะใช้เมื่อมีการบันทึกมาโครดังนั้นParent
แผ่นงานหรือช่วงจึงถือว่าเป็นช่วงที่ใช้งานอยู่เสมอ
นี่คือวิธีที่คุณอาจหลีกเลี่ยงSelect
และActivate
ในกรณีต่อไปนี้:
การเพิ่มแผ่นงานใหม่และคัดลอกเซลล์ในนั้น:
จาก (รหัสที่สร้างขึ้นด้วยเครื่องบันทึกแมโคร):
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
ส่วนใหญ่คุณอาจกำลังคัดลอกบางสิ่งบางอย่าง หากคุณสนใจเฉพาะค่านี้เป็นตัวเลือกที่ดีในการหลีกเลี่ยงการเลือก:
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 ของคุณจะเปิดขึ้นโดยมีการเลือกแผ่นงานหน้าปกไว้ก่อนโดยไม่สนใจว่าไฟล์ใดเป็น ActiveSheet เมื่อปิดไฟล์
ดังนั้นโค้ดด้านล่างก็โอเคอย่างแน่นอน:
Private Sub Workbook_Open()
Worksheets("Cover").Activate
End Sub
อีกตัวอย่างที่ดีคือเมื่อคุณต้องการส่งออกแผ่นงานทั้งหมดเป็นไฟล์ PDF เดียวดังที่กล่าวไว้ในกรณีนี้ - จะหลีกเลี่ยงคำสั่ง select / active ใน VBA ในตัวอย่างนี้ได้อย่างไร?
เมื่อคำสั่งใช้ได้กับ
ActiveWindow
เช่นActiveWindow.ZoomหรือActiveWindow.FreezePanes เท่านั้น
โปรดทราบว่าต่อไปนี้ฉันกำลังเปรียบเทียบแนวทาง Select (วิธีที่ OP ต้องการหลีกเลี่ยง) กับแนวทาง Range (และนี่คือคำตอบสำหรับคำถาม) ดังนั้นอย่าหยุดอ่านเมื่อคุณเห็น Select แรก
มันขึ้นอยู่กับสิ่งที่คุณพยายามทำจริงๆ อย่างไรก็ตามตัวอย่างง่ายๆอาจเป็นประโยชน์ สมมติว่าคุณต้องการตั้งค่าของเซลล์ที่ใช้งานอยู่เป็น "foo" เมื่อใช้ ActiveCell คุณจะเขียนสิ่งนี้:
Sub Macro1()
ActiveCell.Value = "foo"
End Sub
หากคุณต้องการใช้สำหรับเซลล์ที่ไม่ใช่เซลล์ที่ใช้งานอยู่ตัวอย่างเช่น "B2" คุณควรเลือกเซลล์นั้นก่อนดังนี้:
Sub Macro2()
Range("B2").Select
Macro1
End Sub
การใช้ Ranges คุณสามารถเขียนมาโครทั่วไปที่สามารถใช้เพื่อตั้งค่าของเซลล์ใด ๆ ที่คุณต้องการให้เป็นอะไรก็ได้ที่คุณต้องการ:
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
ระบุสมุดงานแผ่นงานและเซลล์ / ช่วงเสมอ
ตัวอย่างเช่น:
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
ใช้ข้อมูลนั้นหรือแผ่นงานเพื่อช่วยให้ผู้ใช้ประหยัดเวลาในการค้นหา หรือหากคุณทราบว่าผู้ใช้จะต้องอัปเดตค่าบางช่วงคุณสามารถเลือกช่วงนั้นโดยทางโปรแกรมได้
การใช้ IMHO .select
มาจากคนที่ชอบฉันเริ่มเรียนรู้ VBA ด้วยความจำเป็นผ่านมาโครการบันทึกจากนั้นแก้ไขโค้ดโดยไม่รู้ตัว.select
และต่อมาselection
เป็นเพียงชายวัยกลางคนที่ไม่จำเป็น
.select
สามารถหลีกเลี่ยงได้เนื่องจากมีการโพสต์จำนวนมากแล้วโดยการทำงานโดยตรงกับวัตถุที่มีอยู่แล้วซึ่งช่วยให้การอ้างอิงทางอ้อมต่างๆเช่นการคำนวณ i และ j ในวิธีที่ซับซ้อนแล้วแก้ไขเซลล์ (i, j) เป็นต้น
มิฉะนั้นจะไม่มีอะไรผิดปกติโดยปริยายใน.select
ตัวมันเองและคุณสามารถค้นหาการใช้งานสำหรับสิ่งนี้ได้อย่างง่ายดายเช่นฉันมีสเปรดชีตที่ฉันเติมวันที่เปิดใช้งานมาโครที่ทำเวทมนตร์กับมันและส่งออกในรูปแบบที่ยอมรับได้ในแผ่นงานแยกต่างหากซึ่ง อย่างไรก็ตามต้องใช้อินพุตขั้นสุดท้ายด้วยตนเอง (คาดเดาไม่ได้) ในเซลล์ที่อยู่ติดกัน มาถึงช่วงเวลานี้.select
ที่ช่วยให้ฉันประหยัดการเคลื่อนไหวของเมาส์และการคลิกเพิ่มเติม
เพื่อหลีกเลี่ยงการใช้.Select
วิธีนี้คุณสามารถตั้งค่าตัวแปรให้เท่ากับคุณสมบัติที่คุณต้องการ
►ตัวอย่างเช่นหากคุณต้องการค่าในตัวCell A1
คุณสามารถตั้งค่าตัวแปรให้เท่ากับคุณสมบัติค่าของเซลล์นั้น
- ตัวอย่าง
valOne = Range("A1").Value
►ตัวอย่างเช่นถ้าคุณต้องการชื่อรหัสของyou could set a variable equal to the
คุณสมบัติ'Sheet3 Codename ' ของแผ่นงานนั้น
- ตัวอย่าง
valTwo = Sheets("Sheet3").Codename
ฉันสังเกตว่าไม่มีคำตอบใดที่กล่าวถึงคุณสมบัติออฟเซ็ต นอกจากนี้ยังสามารถใช้เพื่อหลีกเลี่ยงการใช้การSelect
กระทำเมื่อจัดการกับเซลล์บางเซลล์โดยเฉพาะอย่างยิ่งในการอ้างอิงถึงเซลล์ที่เลือก (ตามที่ OP กล่าวถึงด้วยActiveCell
)
นี่คือตัวอย่างสองสามตัวอย่าง
ฉันยังจะถือว่า "ActiveCell" เป็นJ4
ActiveCell.Offset(2, 0).Value = 12
- สิ่งนี้จะเปลี่ยนเซลล์
J6
เป็นค่า 12 - ลบ -2 จะอ้างถึง J2
ActiveCell.Offset(0,1).Copy ActiveCell.Offset(,2)
- นี้จะคัดลอกเซลล์ในการ
k4
L4
- โปรดทราบว่าไม่จำเป็นต้องใช้ "0" ในพารามิเตอร์ offset หากไม่ต้องการ (, 2)
- คล้ายกับตัวอย่างก่อนหน้านี้ a ลบ 1 จะเป็น
i4
ActiveCell.Offset(, -1).EntireColumn.ClearContents
- สิ่งนี้จะล้างค่าในเซลล์ทั้งหมดในคอลัมน์ k
สิ่งเหล่านี้ไม่ได้หมายความว่า "ดีกว่า" กว่าตัวเลือกข้างต้น แต่เป็นเพียงรายการทางเลือก
จะหลีกเลี่ยงการคัดลอกวางได้อย่างไร
มาดูกัน: สิ่งนี้จะปรากฏขึ้นมากเมื่อบันทึกมาโคร:
Range("X1").Select
Selection.Copy
Range("Y9).Select
Selection.Paste
ในขณะที่สิ่งเดียวที่คนต้องการคือ:
Range("Y9").Value = Range("X1").Value
ดังนั้นแทนที่จะใช้การคัดลอกวางในมาโคร VBA ฉันขอแนะนำวิธีง่ายๆดังต่อไปนี้:
Destination_Range.Value = Source_Range.Value
การทำงานกับคุณลักษณะ. Parentตัวอย่างนี้แสดงให้เห็นว่าการตั้งค่าการอ้างอิง myRng เพียงรายการเดียวช่วยให้สามารถเข้าถึงสภาพแวดล้อมทั้งหมดแบบไดนามิกได้อย่างไรโดยไม่ต้องมีการเลือก, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet และอื่น ๆ (ไม่มีคุณลักษณะทั่วไปสำหรับเด็ก )
Sub ShowParents()
Dim myRng As Range
Set myRng = ActiveCell
Debug.Print myRng.Address ' An address of the selected cell
Debug.Print myRng.Parent.name ' The name of sheet, where MyRng is in
Debug.Print myRng.Parent.Parent.name ' The name of workbook, where MyRng is in
Debug.Print myRng.Parent.Parent.Parent.name ' The name of application, where MyRng is in
' You may use this feature to set reference to these objects
Dim mySh As Worksheet
Dim myWbk As Workbook
Dim myApp As Application
Set mySh = myRng.Parent
Set myWbk = myRng.Parent.Parent
Set myApp = myRng.Parent.Parent.Parent
Debug.Print mySh.name, mySh.Cells(10, 1).Value
Debug.Print myWbk.name, myWbk.Sheets.Count
Debug.Print myApp.name, myApp.Workbooks.Count
' You may use dynamically addressing
With myRng
.Copy
' Pastes in D1 on sheet 2 in the same workbook, where the copied cell is
.Parent.Parent.Sheets(2).Range("D1").PasteSpecial xlValues
' Or myWbk.Sheets(2).Range("D1").PasteSpecial xlValues
' We may dynamically call active application too
.Parent.Parent.Parent.CutCopyMode = False
' Or myApp.CutCopyMode = False
End With
End Sub
เหตุผลหลักที่ไม่ควรใช้ Select หรือ Activesheet เป็นเพราะคนส่วนใหญ่จะมีสมุดงานอีกสองเล่มเป็นอย่างน้อยเปิดอยู่ (บางครั้งก็หลายสิบเล่ม) เมื่อพวกเขาเรียกใช้แมโครของคุณและหากพวกเขาคลิกออกจากแผ่นงานของคุณในขณะที่มาโครของคุณกำลังทำงานและคลิกที่อื่น ๆ หนังสือที่พวกเขาเปิดไว้จากนั้น "Activesheet" จะเปลี่ยนไปและสมุดงานเป้าหมายสำหรับคำสั่ง "Select" ที่ไม่มีเงื่อนไขก็เปลี่ยนไปเช่นกัน
อย่างดีที่สุดมาโครของคุณจะขัดข้องอย่างเลวร้ายที่สุดคุณอาจเขียนค่าหรือเปลี่ยนเซลล์ในสมุดงานที่ไม่ถูกต้องโดยไม่มีทาง "เลิกทำ" ได้
ฉันมีกฎทองง่ายๆที่ฉันปฏิบัติตาม: เพิ่มตัวแปรชื่อ "wb" และ "ws" สำหรับวัตถุสมุดงานและวัตถุแผ่นงานและใช้สิ่งเหล่านี้เพื่ออ้างถึงหนังสือมาโครของฉันเสมอ หากฉันต้องการอ้างถึงหนังสือมากกว่าหนึ่งเล่มหรือมากกว่าหนึ่งแผ่นงานฉันจะเพิ่มตัวแปรอื่น ๆ
ตัวอย่างเช่น,
Dim wb as Workbook
Dim ws as Worksheet
Set wb = ThisWorkBook
Set ws = wb.sheets("Output")
คำสั่ง "Set wb = ThisWorkbook" เป็นกุญแจสำคัญอย่างยิ่ง "ThisWorkbook" เป็นค่าพิเศษใน Excel และหมายถึงสมุดงานที่โค้ด VBA ของคุณกำลังทำงานอยู่ ทางลัดที่มีประโยชน์มากในการตั้งค่าตัวแปรสมุดงานของคุณด้วย
หลังจากที่คุณทำเสร็จแล้วที่ด้านบนของ Sub ของคุณการใช้มันอาจไม่ง่ายไปกว่านี้เพียงแค่ใช้ทุกที่ที่คุณจะใช้ "Selection":
ดังนั้นในการเปลี่ยนค่าของเซลล์ "A1" ใน "Output" เป็น "Hello" แทนที่จะเป็น:
Sheets("Output").Activate
ActiveSheet.Range("A1").Select
Selection.Value = "Hello"
ตอนนี้เราสามารถทำได้:
ws.Range("A1").Value = "Hello"
ซึ่งไม่เพียง แต่น่าเชื่อถือมากขึ้นและมีโอกาสน้อยที่จะเกิดข้อผิดพลาดหากผู้ใช้ทำงานกับสเปรดชีตหลายชุด นอกจากนี้ยังสั้นกว่าเร็วกว่าและเขียนง่ายกว่ามาก
เป็นโบนัสเพิ่มถ้าคุณมักจะตั้งชื่อตัวแปรของคุณ "wb" และ "WS" คุณสามารถคัดลอกและวางโค้ดจากหนังสือเล่มหนึ่งไปยังอีกและมันมักจะทำงานที่มีการเปลี่ยนแปลงน้อยที่สุดที่จำเป็นถ้ามี