วิธีหลีกเลี่ยงการใช้ Select ใน Excel VBA

May 23 2012

ฉันได้ยินมามากเกี่ยวกับความเกลียดชังที่เข้าใจได้ในการใช้.SelectExcel VBA แต่ฉันไม่แน่ใจว่าจะหลีกเลี่ยงการใช้งานอย่างไร ฉันพบว่าโค้ดของฉันจะใช้งานได้อีกครั้งหากฉันสามารถใช้ตัวแปรแทนSelectฟังก์ชันได้ แต่ผมไม่แน่ใจว่าวิธีการที่จะอ้างถึงสิ่งที่ (เช่นActiveCellฯลฯ ) Selectถ้าไม่ได้ใช้

ฉันได้พบบทความนี้ในช่วงและตัวอย่างนี้เกี่ยวกับประโยชน์ของการไม่ใช้เลือกแต่ฉันไม่สามารถหาอะไรในวิธีการ

คำตอบ

578 chrisneilsen May 23 2012 at 17:23

ตัวอย่างบางส่วนของวิธีหลีกเลี่ยงการเลือก

ใช้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

ส่งช่วงไปยังSubs และFunctions ของคุณเป็นตัวแปรช่วง:

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

นี่คือการชิมเล็กน้อยสำหรับสิ่งที่เป็นไปได้

217 SiddharthRout May 23 2012 at 17:33

สองเหตุผลหลักว่าทำไม.Select, .Activate, Selection, Activecell, Activesheet, Activeworkbookฯลฯ ควรหลีกเลี่ยง

  1. ทำให้โค้ดของคุณช้าลง
  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
89 RicksupportsMonica May 28 2014 at 21:04

จุดเน้นเล็ก ๆ จุดหนึ่งที่ฉันจะเพิ่มให้กับคำตอบที่ยอดเยี่ยมทั้งหมดที่ให้ไว้ก่อนหน้านี้:

น่าจะเป็นสิ่งที่ยิ่งใหญ่ที่สุดที่คุณสามารถทำได้เพื่อหลีกเลี่ยงการใช้เลือกที่จะมากที่สุดเท่าที่เป็นไปได้ใช้ชื่อช่วง (รวมกับชื่อตัวแปรที่มีความหมาย) ในรหัส จุดนี้ได้กล่าวไว้ข้างต้น แต่มันถูกปัดสวะไปเล็กน้อย อย่างไรก็ตามควรได้รับความสนใจเป็นพิเศษ

ต่อไปนี้เป็นเหตุผลเพิ่มเติมสองสามประการในการใช้ช่วงที่ตั้งชื่ออย่างเสรีแม้ว่าฉันมั่นใจว่าจะคิดได้มากกว่านี้

ช่วงที่ตั้งชื่อช่วยให้อ่านและเข้าใจโค้ดของคุณได้ง่ายขึ้น

ตัวอย่าง:

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คอลัมน์ไปรอบ ๆ สิ่งที่คุณต้องการได้และโค้ดของคุณจะทำงานต่อไปได้ดี

48 MattB Feb 28 2014 at 04:09

ฉันจะให้คำตอบสั้น ๆ เนื่องจากคนอื่นให้คำตอบแบบยาว

คุณจะได้รับ. เลือกและ. เปิดใช้งานทุกครั้งที่คุณบันทึกมาโครและนำมาใช้ใหม่ เมื่อคุณ. เลือกเซลล์หรือแผ่นงานมันก็ทำให้มันใช้งานได้ ตั้งแต่นั้นเป็นต้นมาเมื่อใดก็ตามที่คุณใช้การอ้างอิงที่ไม่มีเงื่อนไขเช่น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"

วิธีการเหล่านี้มีหลากหลายวิธี แต่นั่นจะเป็นความคิดทั่วไปที่แสดงออกมาโดยเร็วที่สุดสำหรับคนใจร้อนอย่างฉัน

34 Noname Feb 24 2015 at 22:41

"... และฉันพบว่าโค้ดของฉันจะสามารถใช้งานได้อีกครั้งหากฉันสามารถใช้ตัวแปรแทนฟังก์ชัน 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 อาจอนุมาน.)

30 Vityata Mar 08 2016 at 17:04

การหลีกเลี่ยง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 เท่านั้น

30 FrancescoBaruchelli May 23 2012 at 13:11

โปรดทราบว่าต่อไปนี้ฉันกำลังเปรียบเทียบแนวทาง 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
18 user1644564 Aug 25 2014 at 16:54

ระบุสมุดงานแผ่นงานและเซลล์ / ช่วงเสมอ

ตัวอย่างเช่น:

Thisworkbook.Worksheets("fred").cells(1,1)
Workbooks("bob").Worksheets("fred").cells(1,1)

เนื่องจากผู้ใช้มักจะคลิกปุ่มเสมอและทันทีที่โฟกัสเคลื่อนออกจากสมุดงานโค้ดที่ต้องการใช้งานจะเกิดความผิดพลาดอย่างสิ้นเชิง

และห้ามใช้ดัชนีของสมุดงานเป็นอันขาด

Workbooks(1).Worksheets("fred").cells(1,1)

คุณไม่ทราบว่าจะเปิดสมุดงานอื่นเมื่อผู้ใช้เรียกใช้รหัสของคุณ

11 LFB Jun 16 2018 at 21:18

วิธีการเหล่านี้ค่อนข้างถูกตีตราดังนั้นการเป็นผู้นำของ Vityataและ Jeeped เพื่อประโยชน์ในการวาดเส้นบนทราย:

โทร.Activate, .Select, Selection, ActiveSomethingวิธีการ / คุณสมบัติ

โดยทั่วไปเป็นเพราะพวกเขาถูกเรียกเพื่อจัดการอินพุตของผู้ใช้ผ่าน UI ของแอปพลิเคชันเป็นหลัก เนื่องจากเป็นวิธีการที่เรียกว่าเมื่อผู้ใช้จัดการกับวัตถุผ่าน UI จึงเป็นวิธีที่บันทึกโดยเครื่องบันทึกมาโครและนั่นเป็นเหตุผลว่าทำไมการเรียกมันจึงเปราะหรือซ้ำซ้อนสำหรับสถานการณ์ส่วนใหญ่: คุณไม่จำเป็นต้องเลือก วัตถุเพื่อดำเนินการSelectionทันทีหลังจากนั้น

อย่างไรก็ตามคำจำกัดความนี้จะตัดสินสถานการณ์ที่พวกเขาถูกเรียกร้อง:

เมื่อมีการโทร.Activate, .Select, .Selection, .ActiveSomethingวิธีการ / คุณสมบัติ

โดยทั่วไปเมื่อคุณคาดหวังให้ผู้ใช้ขั้นสุดท้ายมีบทบาทในการดำเนินการ

หากคุณกำลังพัฒนาและคาดหวังว่าผู้ใช้จะเลือกอินสแตนซ์ออบเจ็กต์สำหรับโค้ดของคุณเพื่อจัดการ.Selectionหรือ.ActiveObjectไม่เหมาะสม

ในทางกลับกัน.Selectและ.Activateมีประโยชน์เมื่อคุณสามารถสรุปการดำเนินการต่อไปของผู้ใช้และคุณต้องการให้รหัสของคุณแนะนำผู้ใช้ซึ่งอาจช่วยประหยัดเวลาและการคลิกเมาส์ได้ ตัวอย่างเช่นหากโค้ดของคุณเพิ่งสร้างอินสแตนซ์ใหม่ของแผนภูมิหรืออัปเดตอินสแตนซ์ผู้ใช้อาจต้องการตรวจสอบและคุณสามารถเรียก.Activateใช้ข้อมูลนั้นหรือแผ่นงานเพื่อช่วยให้ผู้ใช้ประหยัดเวลาในการค้นหา หรือหากคุณทราบว่าผู้ใช้จะต้องอัปเดตค่าบางช่วงคุณสามารถเลือกช่วงนั้นโดยทางโปรแกรมได้

6 Eleshar Nov 20 2016 at 22:02

การใช้ IMHO .selectมาจากคนที่ชอบฉันเริ่มเรียนรู้ VBA ด้วยความจำเป็นผ่านมาโครการบันทึกจากนั้นแก้ไขโค้ดโดยไม่รู้ตัว.selectและต่อมาselectionเป็นเพียงชายวัยกลางคนที่ไม่จำเป็น

.select สามารถหลีกเลี่ยงได้เนื่องจากมีการโพสต์จำนวนมากแล้วโดยการทำงานโดยตรงกับวัตถุที่มีอยู่แล้วซึ่งช่วยให้การอ้างอิงทางอ้อมต่างๆเช่นการคำนวณ i และ j ในวิธีที่ซับซ้อนแล้วแก้ไขเซลล์ (i, j) เป็นต้น

มิฉะนั้นจะไม่มีอะไรผิดปกติโดยปริยายใน.selectตัวมันเองและคุณสามารถค้นหาการใช้งานสำหรับสิ่งนี้ได้อย่างง่ายดายเช่นฉันมีสเปรดชีตที่ฉันเติมวันที่เปิดใช้งานมาโครที่ทำเวทมนตร์กับมันและส่งออกในรูปแบบที่ยอมรับได้ในแผ่นงานแยกต่างหากซึ่ง อย่างไรก็ตามต้องใช้อินพุตขั้นสุดท้ายด้วยตนเอง (คาดเดาไม่ได้) ในเซลล์ที่อยู่ติดกัน มาถึงช่วงเวลานี้.selectที่ช่วยให้ฉันประหยัดการเคลื่อนไหวของเมาส์และการคลิกเพิ่มเติม

4 FinPro.Online Sep 08 2016 at 13:36

เพื่อหลีกเลี่ยงการใช้.Selectวิธีนี้คุณสามารถตั้งค่าตัวแปรให้เท่ากับคุณสมบัติที่คุณต้องการ

►ตัวอย่างเช่นหากคุณต้องการค่าในตัวCell A1คุณสามารถตั้งค่าตัวแปรให้เท่ากับคุณสมบัติค่าของเซลล์นั้น

  • ตัวอย่าง valOne = Range("A1").Value

►ตัวอย่างเช่นถ้าคุณต้องการชื่อรหัสของyou could set a variable equal to theคุณสมบัติ'Sheet3 Codename ' ของแผ่นงานนั้น

  • ตัวอย่าง valTwo = Sheets("Sheet3").Codename
3 PGSystemTester Aug 17 2018 at 03:07

ฉันสังเกตว่าไม่มีคำตอบใดที่กล่าวถึงคุณสมบัติออฟเซ็ต นอกจากนี้ยังสามารถใช้เพื่อหลีกเลี่ยงการใช้การSelectกระทำเมื่อจัดการกับเซลล์บางเซลล์โดยเฉพาะอย่างยิ่งในการอ้างอิงถึงเซลล์ที่เลือก (ตามที่ OP กล่าวถึงด้วยActiveCell)

นี่คือตัวอย่างสองสามตัวอย่าง

ฉันยังจะถือว่า "ActiveCell" เป็นJ4

ActiveCell.Offset(2, 0).Value = 12

  • สิ่งนี้จะเปลี่ยนเซลล์J6เป็นค่า 12
  • ลบ -2 จะอ้างถึง J2

ActiveCell.Offset(0,1).Copy ActiveCell.Offset(,2)

  • นี้จะคัดลอกเซลล์ในการk4L4
  • โปรดทราบว่าไม่จำเป็นต้องใช้ "0" ในพารามิเตอร์ offset หากไม่ต้องการ (, 2)
  • คล้ายกับตัวอย่างก่อนหน้านี้ a ลบ 1 จะเป็น i4

ActiveCell.Offset(, -1).EntireColumn.ClearContents

  • สิ่งนี้จะล้างค่าในเซลล์ทั้งหมดในคอลัมน์ k

สิ่งเหล่านี้ไม่ได้หมายความว่า "ดีกว่า" กว่าตัวเลือกข้างต้น แต่เป็นเพียงรายการทางเลือก

1 Dominique Oct 30 2020 at 22:55

จะหลีกเลี่ยงการคัดลอกวางได้อย่างไร

มาดูกัน: สิ่งนี้จะปรากฏขึ้นมากเมื่อบันทึกมาโคร:

Range("X1").Select
Selection.Copy
Range("Y9).Select
Selection.Paste

ในขณะที่สิ่งเดียวที่คนต้องการคือ:

Range("Y9").Value = Range("X1").Value

ดังนั้นแทนที่จะใช้การคัดลอกวางในมาโคร VBA ฉันขอแนะนำวิธีง่ายๆดังต่อไปนี้:

Destination_Range.Value = Source_Range.Value
barneyos Oct 30 2019 at 16:45

การทำงานกับคุณลักษณะ. 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
GeoffGriswald Mar 02 2020 at 20:04

เหตุผลหลักที่ไม่ควรใช้ 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" คุณสามารถคัดลอกและวางโค้ดจากหนังสือเล่มหนึ่งไปยังอีกและมันมักจะทำงานที่มีการเปลี่ยนแปลงน้อยที่สุดที่จำเป็นถ้ามี