Cách tránh sử dụng Chọn trong Excel VBA


537

Tôi đã nghe nhiều về sự ghê tởm có thể hiểu được khi sử dụng .Selecttrong Excel VBA, nhưng không chắc chắn về cách tránh sử dụng nó. Tôi thấy rằng mã của tôi sẽ được sử dụng lại nhiều hơn nếu tôi có thể sử dụng các biến thay vì các Selecthàm. Tuy nhiên, tôi không chắc chắn làm thế nào để tham khảo những thứ (như ActiveCellvv) nếu không sử dụng Select.

Tôi đã tìm thấy bài viết này về các phạm viví dụ này về lợi ích của việc không sử dụng select nhưng không thể tìm thấy bất cứ điều gì về cách nào ?


14
Điều quan trọng cần lưu ý là có những trường hợp khi sử dụng Selectvà / hoặc ActiveSheetvv vv là hoàn toàn không thể tránh khỏi. Đây là một ví dụ mà tôi đã tìm thấy: stackoverflow.com/questions/22796286/ Kẻ
Rick hỗ trợ Monica

9
Và có những dịp - chỉnh sửa dữ liệu biểu đồ trong ppt với tệp excel nằm bên dưới là một - trong đó yêu cầu kích hoạt hoặc chọn.
brettdj

@brettdj - đây là một ví dụ gần đây . Để đặt tất cả các trang tính trong sổ làm việc có cùng giá trị, có vẻ như .Select / .Selectionlà bắt buộc.
BruceWayne

3
@bruce từ cùng một QA dường như không phải
chris neilsen

Câu trả lời:


565

Một số ví dụ về cách tránh chọn

Sử dụng Dimbiến 'd

Dim rng as Range

Setbiến đến phạm vi yêu cầu. Có nhiều cách để chỉ một phạm vi đơn

Set rng = Range("A1")
Set rng = Cells(1,1)
Set rng = Range("NamedRange")

hoặc một phạm vi đa ô

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)

Bạn có thể sử dụng phím tắt cho Evaluatephương thức, nhưng điều này ít hiệu quả hơn và thường nên tránh trong mã sản xuất.

Set rng = [A1]
Set rng = [A1:B10]

Tất cả các ví dụ trên đề cập đến các ô trên trang hoạt động . Trừ khi bạn đặc biệt chỉ muốn làm việc với trang tính hoạt động, tốt hơn là nên Worksheetthay đổi một biến

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

Nếu bạn làm muốn làm việc với ActiveSheet, cho rõ ràng nó là tốt nhất để được rõ ràng. Nhưng hãy cẩn thận, vì một số Worksheetphương pháp thay đổi bảng hoạt động.

Set rng = ActiveSheet.Range("A1")

Một lần nữa, điều này đề cập đến bảng tính hoạt động . Trừ khi bạn đặc biệt chỉ muốn làm việc với ActiveWorkbookhoặc ThisWorkbook, tốt hơn là nên Workbookthay đổi Dim .

Dim wb As Workbook
Set wb = Application.Workbooks("Book1")
Set rng = wb.Worksheets("Sheet1").Range("A1")

Nếu bạn làm muốn làm việc với ActiveWorkbook, cho rõ ràng nó là tốt nhất để được rõ ràng. Nhưng hãy cẩn thận, vì nhiều WorkBookphương pháp thay đổi cuốn sách hoạt động.

Set rng = ActiveWorkbook.Worksheets("Sheet1").Range("A1")

Bạn cũng có thể sử dụng ThisWorkbookđối tượng để tham khảo cuốn sách có chứa mã đang chạy.

Set rng = ThisWorkbook.Worksheets("Sheet1").Range("A1")

Một đoạn mã (xấu) phổ biến là mở một cuốn sách, lấy một số dữ liệu rồi đóng lại

Thật tệ:

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

Và sẽ tốt hơn như:

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

Truyền phạm vi cho Subs và Functions của bạn dưới dạng biến Phạm vi

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

Bạn cũng nên áp dụng Phương thức (như FindCopy) cho các biến

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

Nếu bạn đang lặp qua một phạm vi ô, thường thì tốt hơn (nhanh hơn) để sao chép các giá trị phạm vi vào một mảng biến thể trước và lặp qua đó

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

Đây là một taster nhỏ cho những gì có thể.


7
thêm vào câu trả lời tuyệt vời này để làm việc với một phạm vi bạn không cần biết kích thước thực của nó miễn là bạn biết phía trên bên trái ... ví dụ: rng1(12, 12)sẽ hoạt động ngay cả khi rng1 chỉ được đặt thành [A1:A10].
MikeD

3
@chrisneilsen Chris, tôi tin rằng bạn cũng có thể sử dụng tiền tố trang tính trước khi ký hiệu tham chiếu ô viết tắt để cứu bạn khỏi việc gõ Rangenhư thế này: ActiveSheet.[a1:a4]hoặc ws.[b6].
Logan Reed

3
@AndrewWillems ... hoặc 48 lần trong bài đăng này, nhưng ai đang đếm. ☺ ... nhưng nghiêm túc mà nói, đó là một điều dễ quên khi làm việc với các biến giữ các đối tượng. Một variantbiến không yêu cầu Set cho đến khi bạn gán một đối tượng cho nó. Ví dụ: Dim x: x = 1không sao, nhưng Dim x: x = Sheets("Sheet1")sẽ tạo ra Lỗi 438. Tuy nhiên, chỉ cần nhầm lẫn / làm rõ Dim x: x = Range("A1")sẽ không tạo ra lỗi. Tại sao? ... bởi vì nó đã gán giá trị của đối tượng cho biến, không phải là tham chiếu đến chính đối tượng đó (vì nó tương đương với Dim x: x = Range("A1").Value)
ashleedawg

1
@ user3932000 Tôi không biết về một kịch bản trong đó tên trang tính tự động thay đổi. Đối với tên tệp, nó sẽ chỉ làm điều đó nếu đã có một tệp có tên đó trong thư mục. Chỉ cần sử dụng Save ... hoặc mã cứng tên tệp để lưu dưới dạng chuỗi. Nếu bạn không thể giải quyết vấn đề này, bạn nên hỏi một câu hỏi riêng về nó thay vì bình luận.
TylerH

1
@ user3932000 mà có thể làm một Q. thú vị tôi chắc chắn rằng có nhiều cách để xử lý nó .. bạn đã về đủ SO lâu để biết cướp a thread nhận xét về Q cũ không phải là con đường để đi
chris Neilsen

212

Hai lý do chính tại sao .Select/ .Activate/ Selection/ Activecell/ Activesheet/ Activeworkbookv.v ... nên tránh

  1. Nó làm chậm mã của bạn.
  2. Nó thường là nguyên nhân chính của lỗi thời gian chạy.

Làm thế nào để chúng ta tránh nó?

1) Làm việc trực tiếp với các đối tượng liên quan

Xem xét mã này

Sheets("Sheet1").Activate
Range("A1").Select
Selection.Value = "Blah"
Selection.NumberFormat = "@"

Mã này cũng có thể được viết là

With Sheets("Sheet1").Range("A1")
    .Value = "Blah"
    .NumberFormat = "@"
End With

2) Nếu được yêu cầu khai báo các biến của bạn. Mã tương tự ở trên có thể được viết là

Dim ws as worksheet

Set ws = Sheets("Sheet1")

With ws.Range("A1")
    .Value = "Blah"
    .NumberFormat = "@"
End With

17
Đó là một câu trả lời hay, nhưng điều tôi thiếu trong chủ đề này là khi chúng ta thực sự cần Kích hoạt. Mọi người đều nói nó là xấu, nhưng không ai giải thích bất kỳ trường hợp nào có ý nghĩa khi sử dụng nó. Ví dụ, tôi đã làm việc với 2 sổ làm việc và không thể bắt đầu macro trên một trong các sổ làm việc mà không kích hoạt nó trước. Bạn có thể giải thích một chút có thể? Ngoài ra, ví dụ nếu tôi không kích hoạt các trang tính khi sao chép một phạm vi từ trang này sang trang khác, thì khi tôi thực hiện chương trình, dường như nó sẽ kích hoạt các trang tương ứng.
dùng3032689

1
Tôi thấy rằng đôi khi bạn có thể cần phải kích hoạt một trang tính trước nếu bạn cần dán hoặc lọc dữ liệu trên đó. Tôi sẽ nói là tốt nhất để tránh kích hoạt càng nhiều càng tốt nhưng có những trường hợp bạn cần phải làm điều đó. Vì vậy, tiếp tục kích hoạt và lựa chọn ở mức tối thiểu theo câu trả lời ở trên.
Nick

7
Tôi nghĩ rằng vấn đề không phải là hoàn toàn tránh sử dụng chúng, mà là càng nhiều càng tốt. nếu bạn muốn lưu một sổ làm việc, để khi ai đó mở nó, một ô nhất định trong một trang tính nhất định được chọn, thì bạn phải chọn trang tính và ô đó. sao chép / dán là một ví dụ xấu, ít nhất là trong trường hợp giá trị, nó có thể được thực hiện nhanh hơn bằng một mã nhưSheets(2).[C10:D12].Value = Sheets(1).[A1:B3].Value
robotik

1
@Nick Bạn không cần Kích hoạt trang tính để dán vào chúng hoặc lọc chúng. Sử dụng đối tượng sheet trong các lệnh dán hoặc bộ lọc của bạn. Nó trở nên dễ dàng hơn khi bạn tìm hiểu mô hình đối tượng Excel thông qua thực hành. Tôi tin rằng lần duy nhất tôi sử dụng. Hoạt động là khi tôi tạo một trang tính mới, nhưng tôi muốn trang tính ban đầu xuất hiện khi mã được thực hiện.
phrebh

3
@phrebh Bạn không cần sử dụng .Activateđể chuyển sang trang gốc, chỉ cần sử dụngApplication.Goto
GMalc

88

Một điểm nhấn mạnh nhỏ tôi sẽ thêm vào tất cả các câu trả lời xuất sắc ở trên:

Có lẽ điều lớn nhất bạn có thể làm để tránh sử dụng Chọn là càng nhiều càng tốt, sử dụng các phạm vi được đặt tên (kết hợp với các tên biến có ý nghĩa) trong mã VBA của bạn . Điểm này đã được đề cập ở trên, nhưng bóng bẩy hơn một chút; tuy nhiên, nó đáng được quan tâm đặc biệt.

Dưới đây là một vài lý do bổ sung để sử dụng tự do các phạm vi được đặt tên mặc dù tôi chắc chắn rằng tôi có thể nghĩ đến nhiều hơn.

Phạm vi được đặt tên làm cho mã của bạn dễ đọc và dễ hiểu hơn.

Thí dụ:

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

Đó là khá rõ ràng những gì phạm vi được đặt tên MonthsMonthlySaleschứa, và các thủ tục đang làm gì.

Sao nó lại quan trọng? Một phần vì người khác dễ hiểu nó hơn, nhưng ngay cả khi bạn là người duy nhất từng nhìn thấy hoặc sử dụng mã của bạn, bạn vẫn nên sử dụng phạm vi được đặt tên và tên biến tốt vì BẠN S FOR BỎ QUA những gì bạn muốn làm với nó một năm sau, và bạn sẽ lãng phí 30 phút chỉ để tìm ra mã của bạn đang làm gì.

Phạm vi được đặt tên đảm bảo rằng các macro của bạn không bị hỏng khi (chứ không phải!) Cấu hình của bảng tính thay đổi.

Hãy xem xét, nếu ví dụ trên đã được viết như thế này:

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

Mã này sẽ hoạt động tốt lúc đầu - đó là cho đến khi bạn hoặc người dùng tương lai quyết định "gee wiz, tôi nghĩ rằng tôi sẽ thêm một cột mới với năm trong Cột A!", Hoặc đặt một cột chi phí giữa các tháng và cột bán hàng, hoặc thêm một tiêu đề cho mỗi cột. Bây giờ, mã của bạn bị hỏng. Và bởi vì bạn đã sử dụng tên biến khủng khiếp, bạn sẽ mất nhiều thời gian hơn để tìm ra cách khắc phục nó hơn mức cần thiết.

Nếu bạn đã sử dụng các phạm vi được đặt tên để bắt đầu, các cột MonthsSalescột có thể được di chuyển xung quanh tất cả những gì bạn thích và mã của bạn sẽ tiếp tục hoạt động tốt.


6
Cuộc tranh luận về việc liệu các phạm vi được đặt tên là thiết kế bảng tính tốt hay xấu vẫn tiếp tục - Tôi chắc chắn không có trại. Theo kinh nghiệm của tôi, chúng làm tăng lỗi (đối với người dùng chuẩn không có nhu cầu về mã).
brettdj 27/2/2015


12
Tôi đồng ý với triết lý phát triển của bạn; tuy nhiên tôi nghĩ rằng bài báo là vô nghĩa. Nó nói về cách các tên phạm vi có thể gây nhầm lẫn cho người mới đang gỡ lỗi bảng tính, nhưng bất kỳ ai sử dụng người mới để xem bảng tính phức tạp đều nhận được những gì họ xứng đáng! Tôi đã từng làm việc cho một công ty đã xem xét bảng tính tài chính và tôi có thể nói với bạn rằng đó không phải là công việc bạn giao cho người mới.
DeanOC

8
Không có cuộc tranh luận có ý nghĩa. Bất cứ ai lập luận chống lại các tên được xác định đã không dành thời gian để hiểu đầy đủ sự phân nhánh của họ. Các công thức được đặt tên có thể là cấu trúc sâu sắc và hữu ích nhất trong tất cả Excel.
Excel Hero

10
@brettdj: Trích dẫn của bạn là chính xác, nhưng bạn đã quên đề cập rằng nó được theo sau bởi sáu cụm từ "Ngoại trừ ...". Một trong số đó là: " Ngoại trừ thay thế cho tham chiếu ô trong mã hóa macro Luôn sử dụng Tên Excel thay thế cho tham chiếu ô khi xây dựng macro. Điều này là để tránh các lỗi phát sinh từ việc chèn các hàng hoặc cột bổ sung theo đó mã hóa macro không còn chỉ vào dữ liệu nguồn dự định. "
Marcus Mangelsdorf

47

Tôi sẽ đưa ra câu trả lời ngắn vì những người khác đã đưa ra câu trả lời dài.

Bạn sẽ nhận được .select và .activate bất cứ khi nào bạn ghi macro và sử dụng lại chúng. Khi bạn chọn một ô hoặc trang tính, nó chỉ làm cho nó hoạt động. Từ thời điểm đó, bất cứ khi nào bạn sử dụng các tài liệu tham khảo không đủ tiêu chuẩn như Range.Valuehọ chỉ sử dụng ô và trang hoạt động. Điều này cũng có thể có vấn đề nếu bạn không xem mã của bạn được đặt ở đâu hoặc người dùng nhấp vào sổ làm việc.

Vì vậy, bạn có thể loại bỏ những vấn đề này bằng cách tham khảo trực tiếp các tế bào của bạn. Chọn nơi nào để đến:

'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)

Hoặc bạn có thể

'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"

Có nhiều cách kết hợp các phương pháp này, nhưng đó sẽ là ý tưởng chung được thể hiện càng sớm càng tốt cho những người thiếu kiên nhẫn như tôi.


33

"... và tôi thấy rằng mã của tôi sẽ có thể sử dụng lại nhiều hơn nếu tôi có thể sử dụng các biến thay vì Chọn hàm."

Mặc dù tôi không thể nghĩ ra nhiều hơn một số tình huống bị cô lập, trong đó .Selectsẽ là lựa chọn tốt hơn so với tham chiếu tế bào trực tiếp, tôi sẽ đề phòng Selectionvà chỉ ra rằng không nên vứt bỏ nó vì những lý do tương tự .Selectnên tránh.

Đôi khi, các thói quen phụ macro ngắn, tiết kiệm thời gian được gán cho các tổ hợp phím nóng có sẵn với một vài lần nhấn sẽ tiết kiệm rất nhiều thời gian. Có thể chọn một nhóm các ô để ban hành mã hoạt động trên các công việc kỳ diệu khi xử lý dữ liệu bỏ túi không phù hợp với định dạng dữ liệu trên toàn bảng tính. Cũng giống như cách bạn có thể chọn một nhóm ô và áp dụng thay đổi định dạng, chọn một nhóm ô để chạy mã macro đặc biệt có thể là một trình tiết kiệm thời gian chính.

Ví dụ về khung phụ dựa trên Lựa chọn:

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

Mã thực tế để xử lý có thể là bất cứ thứ gì từ một dòng đến nhiều mô-đun. Tôi đã sử dụng phương pháp này để bắt đầu các thói quen chạy dài trên một lựa chọn các ô bị rách có chứa tên tệp của sổ làm việc bên ngoài.

Nói tóm lại, đừng loại bỏ Selectiondo sự liên kết chặt chẽ với .SelectActiveCell. Là một tài sản bảng tính, nó có nhiều mục đích khác.

(Vâng, tôi biết câu hỏi này là về .Select, Selectionnhưng tôi không muốn xóa bỏ bất kỳ quan niệm sai lầm nào mà các lập trình viên VBA mới có thể suy ra.)


13
Selectioncó thể là bất cứ thứ gì trong bảng tính vì vậy cũng có thể kiểm tra loại đối tượng trước khi gán nó cho một biến vì bạn đã khai báo rõ ràng là Range.
L42

29

Xin lưu ý rằng trong phần sau tôi đang so sánh cách tiếp cận Chọn (cách mà OP muốn tránh), với cách tiếp cận Phạm vi (và đây là câu trả lời cho câu hỏi). Vì vậy, đừng ngừng đọc khi bạn thấy Chọn đầu tiên.

Nó thực sự phụ thuộc vào những gì bạn đang cố gắng làm. Dù sao một ví dụ đơn giản có thể hữu ích. Giả sử bạn muốn đặt giá trị của ô hiện hoạt thành "foo". Sử dụng ActiveCell bạn sẽ viết một cái gì đó như thế này:

Sub Macro1()
    ActiveCell.Value = "foo"
End Sub

Nếu bạn muốn sử dụng nó cho một ô không phải là ô đang hoạt động, ví dụ như "B2", bạn nên chọn nó trước, như thế này:

Sub Macro2()
    Range("B2").Select
    Macro1
End Sub

Sử dụng Phạm vi, bạn có thể viết một macro chung hơn có thể được sử dụng để đặt giá trị của bất kỳ ô nào bạn muốn thành bất cứ thứ gì bạn muốn:

Sub SetValue(cellAddress As String, aVal As Variant)
    Range(cellAddress).Value = aVal
End Sub

Sau đó, bạn có thể viết lại Macro2 dưới dạng:

Sub Macro2()
    SetCellValue "B2", "foo"
End Sub

Và Macro1 là:

Sub Macro1()
    SetValue ActiveCell.Address, "foo"
End Sub

Hy vọng điều này sẽ giúp làm sáng tỏ mọi thứ một chút.


1
Cảm ơn cho phản ứng tuyệt vời rất nhanh chóng. Vì vậy, điều đó có nghĩa là nếu tôi thường thêm các ô vào phạm vi, đặt tên cho phạm vi và lặp qua nó, tôi có nên nhảy thẳng để tạo một mảng không?
BiGXERO

Tôi không chắc là tôi hiểu ý của bạn, nhưng bạn có thể tạo Phạm vi bằng một lệnh duy nhất (ví dụ: Phạm vi ("B5: C14")) và bạn thậm chí có thể đặt giá trị của nó ngay lập tức (nếu nó phải giống nhau cho mọi ô trong phạm vi), ví dụ Phạm vi ("B5: C14"). Value = "abc"
Francesco Baruchelli

29

Tránh SelectActivatelà động thái giúp bạn phát triển VBA tốt hơn một chút. Nói chung, SelectActivateđược sử dụng khi macro được ghi lại, do đó, Parentbảng tính hoặc phạm vi luôn được coi là hoạt động.

Đây là cách bạn có thể tránh SelectActivatetrong các trường hợp sau:


Thêm một Bảng tính mới và sao chép một ô trên đó:

Từ (mã được tạo bằng trình ghi macro):

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

Đến:

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

Khi bạn muốn sao chép phạm vi giữa các bảng tính:

Từ:

Sheets("Source").Select
Columns("A:D").Select
Selection.Copy
Sheets("Target").Select
Columns("A:D").Select
ActiveSheet.Paste

Đến:

Worksheets("Source").Columns("A:D").Copy Destination:=Worksheets("Target").Range("a1")

Sử dụng phạm vi được đặt tên ưa thích

Bạn có thể truy cập chúng với [], điều này thực sự đẹp, so với cách khác. Tự kiểm tra:

Dim Months As Range
Dim MonthlySales As Range

Set Months = Range("Months")    
Set MonthlySales = Range("MonthlySales")

Set Months =[Months]
Set MonthlySales = [MonthlySales]

Ví dụ từ trên sẽ trông như thế này:

Worksheets("Source").Columns("A:D").Copy Destination:=Worksheets("Target").[A1]

Không sao chép giá trị, nhưng lấy chúng

Thông thường, nếu bạn sẵn sàng select, hầu hết có lẽ bạn đang sao chép một cái gì đó. Nếu bạn chỉ quan tâm đến các giá trị, đây là một lựa chọn tốt để tránh chọn:

Range("B1:B6").Value = Range("A1:A6").Value


Luôn luôn cố gắng tham khảo Bảng tính

Đây có lẽ là lỗi phổ biến nhất trong . Bất cứ khi nào bạn sao chép phạm vi, đôi khi bảng tính không được tham chiếu và do đó VBA xem xét trang tính sai 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

Tôi thực sự có thể không bao giờ sử dụng .Selecthoặc .Activatecho bất cứ điều gì?

  • Một ví dụ điển hình về thời điểm bạn có thể được biện minh khi sử dụng .Activate.Selectlà khi bạn muốn đảm bảo rằng một Bảng tính cụ thể được chọn vì lý do trực quan. Ví dụ: Excel của bạn sẽ luôn mở với bảng tính được chọn trước, không quan tâm đến đó là ActiveSheet khi tệp được đóng.

Vì vậy, một cái gì đó giống như mã dưới đây là hoàn toàn OK:

Private Sub Workbook_Open()
    Worksheets("Cover").Activate
End Sub

Bạn có thể sử dụng Application.Goto thay vì Worksheet.Activate. Một chút ít rủi ro.
Geoff Griswald

1
Trả lời muộn FYI - một ví dụ hay và hơi bất ngờ cho một điều cần thiết .Select- cũng như công việc của tôi - có thể được tìm thấy tại Cách viết thông tin giống hệt nhau cho tất cả các tờ - @Vityata :)
TM

1
@TM - thực sự đó là một ví dụ thú vị và có thể tiết kiệm một vài phần nghìn giây cho hơn 100 bảng tính, nhưng tôi có thể sẽ không khuyến khích nó, nếu tôi thấy nó ở đâu đó. Dù sao, Lựa chọn ở đó không được viết rõ ràng mà là kết quả của .FillAcrossSheets, vì vậy đây là một nơi nào đó ở giữa (ít nhất là trong ý tưởng của tôi về phân loại VBA)
Vityata

17

Luôn ghi rõ sổ làm việc, bảng tính và ô / phạm vi.

Ví dụ:

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

Bởi vì người dùng cuối sẽ luôn chỉ nhấp vào các nút và ngay khi tiêu điểm di chuyển khỏi sổ làm việc, mã muốn làm việc sau đó mọi thứ sẽ hoàn toàn sai.

Và không bao giờ sử dụng chỉ mục của một bảng tính.

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

Bạn không biết những gì các sổ làm việc khác sẽ được mở khi người dùng chạy mã của bạn.


7
Tên của bảng tính cũng có thể thay đổi. Sử dụng tên mã thay thế.
Rick hỗ trợ Monica

Tên bảng tính có thể thay đổi, chắc chắn. Nhưng tôi không đồng ý rằng bạn nên quá phức tạp mã của mình để thử và giảm thiểu điều đó. Nếu người dùng thay đổi tên của trang tính và macro của họ ngừng hoạt động, đó là trên trang đó. Tôi thường chỉ cho rằng tên bảng tính sẽ giống nhau. Đối với các macro đặc biệt quan trọng, tôi chạy một chút kiểm tra trước chuyến bay trước khi khởi chạy vào macro phù hợp, nó chỉ kiểm tra để đảm bảo tất cả các trang tính mà nó mong muốn tìm thấy thực sự ở đó và nếu có thiếu nó sẽ thông báo cho người dùng biết cái nào.
Geoff Griswald

10

Các phương pháp này khá kỳ thị, do đó, dẫn đầu @Vityata và @Jeeped vì mục đích vẽ một đường thẳng trên cát:

Tại sao không gọi .Activate, .Select, Selection, ActiveSomethingphương pháp / tài sản

Về cơ bản vì chúng được gọi chủ yếu để xử lý đầu vào của người dùng thông qua UI Ứng dụng. Vì chúng là các phương thức được gọi khi người dùng xử lý các đối tượng thông qua giao diện người dùng, chúng là những phương thức được ghi bởi máy ghi vĩ mô và đó là lý do tại sao gọi chúng là dễ vỡ hoặc dư thừa trong hầu hết các tình huống: bạn không phải chọn một đối tượng để thực hiện một hành động với Selectionngay sau đó.

Tuy nhiên, định nghĩa này giải quyết các tình huống mà chúng được gọi là:

Khi nào thì gọi .Activate, .Select, .Selection, .ActiveSomethingphương pháp / tài sản

Về cơ bản khi bạn mong đợi người dùng cuối cùng đóng vai trò trong việc thực thi.

Nếu bạn đang phát triển và mong muốn người dùng chọn các thể hiện đối tượng cho mã của bạn để xử lý, thì .Selectionhoặc .ActiveObjectlà phù hợp.

Mặt khác, .Select.Activateđược sử dụng khi bạn có thể suy ra hành động tiếp theo của người dùng và bạn muốn mã của mình hướng dẫn người dùng, có thể giúp anh ta tiết kiệm thời gian và nhấp chuột. Ví dụ: nếu mã của bạn vừa tạo một phiên bản hoàn toàn mới của biểu đồ hoặc cập nhật biểu đồ, người dùng có thể muốn kiểm tra và bạn có thể gọi .Activatenó hoặc trang tính của nó để tiết kiệm thời gian tìm kiếm cho người dùng; hoặc nếu bạn biết người dùng sẽ cần cập nhật một số giá trị phạm vi, bạn có thể lập trình chọn phạm vi đó.


6

Việc sử dụng IMHO .selectđến từ những người, những người như tôi bắt đầu học VBA bằng cách cần thiết thông qua ghi macro và sau đó sửa đổi mã mà không nhận ra rằng .selectvà sau đó selectionchỉ là một người trung gian không cần thiết.

.select có thể tránh được, như nhiều người đã đăng, bằng cách làm việc trực tiếp với các đối tượng đã có, cho phép tham chiếu gián tiếp khác nhau như tính i và j theo cách phức tạp và sau đó chỉnh sửa ô (i, j), v.v.

Mặt khác, không có gì sai hoàn toàn với .selectchính nó và bạn có thể tìm thấy việc sử dụng cho việc này một cách dễ dàng, ví dụ: tôi có một bảng tính mà tôi điền ngày tháng, kích hoạt macro thực hiện một số phép thuật với nó và xuất nó ở định dạng có thể chấp nhận được trên một trang riêng, tuy nhiên, yêu cầu một số đầu vào thủ công cuối cùng (không thể đoán trước) vào một ô liền kề. Vì vậy, đây là thời điểm .selectgiúp tôi tiết kiệm thêm chuyển động chuột và nhấp chuột.


2
Trong khi bạn đúng, có ít nhất một điều hoàn toàn sai với lựa chọn: đó là chậm. Thực sự rất chậm so với mọi thứ khác xảy ra trong một macro.
vacip

4

Câu trả lời nhanh:

Để tránh sử dụng .Selectphương thức, bạn có thể đặt một biến bằng với thuộc tính mà bạn muốn.

► Ví dụ: nếu bạn muốn giá trị trong Cell A1bạn có thể đặt một biến bằng với thuộc tính giá trị của ô đó.

  • Thí dụ valOne = Range("A1").Value

► Ví dụ: nếu bạn muốn tên mã của 'Sheet3`, bạn có thể đặt một biến bằng với thuộc tính tên mã của bảng tính đó.

  • Thí dụ valTwo = Sheets("Sheet3").Codename

Tôi hy vọng điều đó sẽ giúp. Hãy cho tôi biết nếu bạn có bất kỳ câu hỏi.


3

Tôi nhận thấy rằng không có câu trả lời nào trong số này đề cập đến Tài sản . Offerset . Điều này cũng có thể được sử dụng để tránh sử dụng Selecthành động khi thao tác một số ô nhất định, đặc biệt là tham chiếu đến một ô đã chọn (như OP đề cập ActiveCell).

Dưới đây là một vài ví dụ.

Tôi cũng sẽ giả sử "ActiveCell" là J4 .

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

  • Điều này sẽ thay đổi ô J6thành giá trị 12
  • Một điểm trừ -2 sẽ có tham chiếu J2

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

  • Điều này sẽ sao chép các tế bào trong k4để L4.
  • Lưu ý rằng "0" là không cần thiết trong tham số bù nếu không cần thiết (, 2)
  • Tương tự như ví dụ trước, trừ 1 sẽ là i4

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

  • Điều này sẽ xóa các giá trị trong tất cả các ô trong cột k.

Những điều này không có nghĩa là chúng "tốt hơn" so với các tùy chọn trên, mà chỉ liệt kê các lựa chọn thay thế.


0

Làm việc với tính năng .Parent. Ví dụ này cho thấy cách chỉ thiết lập một tham chiếu myRng cho phép truy cập động vào toàn bộ môi trường mà không có .Select, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet, v.v. (Không có tính năng genereic .Child)

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 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

Rất đẹp, nhưng không chắc điều này có liên quan gì đến câu hỏi của OP. Bạn hoàn toàn không cần "Parent" để hoạt động trong VBA mà không cần sử dụng Chọn hoặc ActiveSheet
Geoff Griswald

0

Lý do chính không bao giờ sử dụng Chọn hoặc Activesheet là vì hầu hết mọi người sẽ có ít nhất một vài sổ làm việc khác mở (đôi khi là hàng chục) khi họ chạy macro của bạn và nếu họ nhấp vào trang tính của bạn trong khi macro của bạn đang chạy và nhấp vào một số khác cuốn sách họ đã mở, sau đó "Activesheet" thay đổi và sổ làm việc đích cho lệnh "Chọn" không đủ tiêu chuẩn cũng thay đổi.

Tốt nhất, macro của bạn sẽ sụp đổ, tệ nhất là bạn có thể kết thúc việc viết các giá trị hoặc thay đổi các ô trong sổ làm việc sai mà không có cách nào để "Hoàn tác" chúng.

Tôi có một quy tắc vàng đơn giản mà tôi tuân theo: Thêm các biến có tên "wb" và "ws" cho đối tượng Workbook và đối tượng Worksheet và luôn sử dụng các biến đó để tham khảo sách macro của tôi. Nếu tôi cần tham khảo nhiều hơn một cuốn sách, hoặc nhiều hơn một tờ, tôi thêm nhiều biến.

ví dụ

Dim wb as Workbook
Dim ws as Worksheet
Set wb = ThisWorkBook
Set ws = wb.sheets("Output")

Lệnh "Đặt wb = ThisWorkbook" là hoàn toàn chính. "ThisWorkbook" là một giá trị đặc biệt trong Excel và có nghĩa là sổ làm việc mà mã VBA của bạn hiện đang chạy . Một phím tắt rất hữu ích để đặt biến Workbook của bạn với.

Sau khi bạn đã hoàn thành việc đó ở đầu Sub, việc sử dụng chúng không thể đơn giản hơn, chỉ cần sử dụng chúng ở bất cứ đâu bạn sẽ sử dụng "Lựa chọn":

Vì vậy, để thay đổi giá trị của ô "A1" trong "Đầu ra" thành "Xin chào", thay vì:

Sheets("Output").Activate
ActiveSheet.Range("A1").Select
Selection.Value = "Hello"

Bây giờ chúng ta có thể làm điều này:

ws.Range("A1").Value = "Hello"

Điều này không chỉ đáng tin cậy hơn và ít có khả năng bị sập hơn nếu người dùng đang làm việc với nhiều bảng tính, nó cũng ngắn hơn nhiều, nhanh hơn và dễ viết hơn.

Là một phần thưởng bổ sung, nếu bạn luôn đặt tên cho biến của mình là "wb" và "ws", bạn có thể sao chép và dán mã từ cuốn sách này sang cuốn sách khác và nó thường sẽ hoạt động với những thay đổi tối thiểu cần thiết, nếu có.


1
Không phải downvote của tôi, nhưng tôi không chắc điều này sẽ thêm bất cứ điều gì mới vào những gì đã được đề xuất trong các câu trả lời hiện có.
BigBen

Vâng, câu trả lời của tôi hơi dư thừa, nhưng các câu trả lời khác quá dài, chứa quá nhiều thứ thừa thãi và không ai đề cập đến việc sử dụng ThisWorkbook để đặt trả trước biến bảng tính của bạn. Đó là điều mà nếu ai đó chỉ cho tôi lần đầu tiên tôi nhúng ngón chân vào VBA, tôi sẽ thấy vô cùng hữu ích. Những người khác đã đề cập đến việc sử dụng biến Worksheet nhưng không thực sự giải thích lý do tại sao rất tốt và không đưa ra ví dụ về mã có và không sử dụng biến bảng tính và bảng tính.
Geoff Griswald

Nhưng câu trả lời được chấp nhận chắc chắn thảo luận ThisWorkbook... Tôi không chắc nhận xét của bạn là chính xác.
BigBen

Nó không, bạn không sai. Nhưng không phải trong bối cảnh sử dụng nó để đặt biến sổ làm việc và sử dụng biến sổ làm việc đó trong tương lai hoặc sử dụng biến sổ làm việc đó để đặt biến bảng tính, như tôi đang đề xuất. Câu trả lời của tôi ngắn hơn, đơn giản và dễ tiếp cận hơn cho người mới bắt đầu so với câu trả lời được chấp nhận.
Geoff Griswald

-3

Đây là một ví dụ sẽ xóa nội dung của ô "A1" (hoặc nhiều hơn nếu loại lựa chọn là xllastcell, v.v.). Tất cả được thực hiện mà không phải chọn các tế bào.

Application.GoTo Reference:=Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1")
Range(Selection,selection(selectiontype)).clearcontents 

Tôi hi vọng điêu nay se giup được ai đo.


1
Không xin lỗi. Đó không phải là những gì bạn đã làm ở đó. Những gì bạn thực sự đã làm là Chọn ô "A1" bằng lệnh "Application.GoTo", không khác gì sử dụng "Chọn" thực sự, sau đó sử dụng các điều khiển rõ ràng trong lựa chọn của bạn. cách để làm điều đó mà không chọn các ô sẽ Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1").ClearContentslà một dòng chứ không phải hai và thực sự hoạt động mà không chọn các ô.
Geoff Griswald
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.