Excel VBA'da Select kullanmaktan kaçınma


537

.SelectExcel VBA'da kullanımın anlaşılabilir bir şekilde anlaşılmadığı hakkında çok şey duydum , ancak bunu nasıl kullanacağımdan emin değilim. Selectİşlevler yerine değişkenler kullanabilseydim kodumun daha çok kullanılabilir olacağını görüyorum . Ancak, ActiveCelleğer kullanılmıyorsa nasıl şeyler ( vb gibi) başvurmak emin değilim Select.

Aralıklarla ilgili bu makaleyi ve select kullanmamanın yararlarına ilişkin bu örneği buldum , ancak nasıl bir şey bulamıyorum ?


14
SelectVe / veya ActiveSheetetc vs kullanımının kaçınılmaz olduğunu unutmamak önemlidir . Bulduğum bir örnek: stackoverflow.com/questions/22796286/…
Rick, Monica

9
Ve vekillerde grafik verilerini düzenleme, altta yatan bir excel dosyası bir olmak üzere - etkinleştirme veya seçmenin gerekli olduğu durumlar vardır.
brettdj

@brettdj - İşte yeni bir örnek . Çalışma kitabındaki tüm sayfaları aynı değere ayarlamak .Select / .Selectioniçin gerekli gibi görünüyor .
BruceWayne

3
Aynı KG'den @bruce öyle değil
chris neilsen

Yanıtlar:


565

Seçimden kaçınmanın bazı örnekleri

Dim'D değişkenlerini kullan

Dim rng as Range

Setdeğişkeni istenen aralığa getirin. Tek hücre aralığına başvurmanın birçok yolu vardır

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

veya çok hücreli bir aralık

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)

Sen edebilirsiniz kısayol kullanmak Evaluateyöntemle, ancak bu daha az verimlidir ve genellikle üretim kodunda kaçınılmalıdır.

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

Yukarıdaki örneklerin tümü, etkin sayfadaki hücrelere karşılık gelir . Özellikle etkin sayfa ile çalışmak istemiyorsanız, bir Worksheetdeğişkeni de karartmak daha iyidir

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

Eğer varsa do çalışmak isteyen ActiveSheet, netlik için o açık olmak en iyisidir. Ancak bazı Worksheetyöntemler etkin sayfayı değiştirirken dikkatli olun .

Set rng = ActiveSheet.Range("A1")

Yine, bu aktif çalışma kitabını ifade eder . Özellikle ActiveWorkbookveya ile çalışmak istemediğiniz sürece ThisWorkbook, bir Workbookdeğişkeni de karartmak daha iyidir .

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

Eğer varsa do çalışmak isteyen ActiveWorkbook, netlik için o açık olmak en iyisidir. Ancak WorkBook, aktif kitabı değiştirirken birçok yöntem dikkat edin.

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

ThisWorkbookNesneyi, çalışan kodu içeren kitaba başvurmak için de kullanabilirsiniz .

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

Yaygın (kötü) bir kod parçası bir kitap açmak, bazı verileri almak ve tekrar kapatmaktır

Bu kö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

Ve daha iyi olurdu:

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

Aralıklarınızı aralık değişkenleri olarak Subs ve s'lerinize aktarınFunction

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

Değişkenlere Yöntem ( Findve gibi Copy) de uygulamalısınız

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

Bir hücre aralığında döngü yapıyorsanız, önce aralık değerlerini bir varyant dizisine kopyalamak ve bunun üzerine döngü yapmak genellikle daha iyidir (daha hızlı)

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

Bu mümkün olan için küçük bir çeşnidir.


7
bir dizi ile çalışmak için sol üstünü bildiğiniz sürece gerçek boyutunu bilmeniz gerekmediği bu parlak cevaba ek olarak ... örneğin rng1(12, 12)rng1 [A1:A10]sadece ayarlanmış olsa bile çalışacaktır .
MikeD

3
@chrisneilsen Chris, ben de Rangeböyle yazmaktan kurtarmak için steno hücre referans gösterimi önce çalışma sayfası öneki kullanabilirsiniz inanıyorum : ActiveSheet.[a1:a4]veya ws.[b6].
Logan Reed

3
@AndrewWillems ... veya bu yayında 48 kez, ama kim sayıyor. ☺ ... ama cidden, nesneleri tutan değişkenlerle çalışırken unutması kolay. Bir variantdeğişken, siz ona bir nesne atayana Set kadar gerektirmez . Örneğin, Dim x: x = 1tamam, ama Dim x: x = Sheets("Sheet1")açıklamak / sadece karıştırmamak Ancak Hatası 438. üretecektir Dim x: x = Range("A1")olacak değil bir hata oluşturun. Neden? ... çünkü nesnenin değerine değişkene atandığından, nesnenin kendisine bir referans değil (eşdeğer olduğu için Dim x: x = Range("A1").Value)
ashleedawg

1
@ user3932000 Sayfa adlarının otomatik olarak değiştiği bir senaryodan haberdar değilim. Dosya adlarına gelince, bunu sadece klasörde o adda bir dosya varsa yapar. Bir dize olarak kaydetmek için Kaydet ... veya sabit dosya adını kullanın. Bu sorunu çözemezseniz, yorum yapmak yerine bunun hakkında ayrı bir soru sormalısınız.
TylerH

1
@ user3932000 Bu ilginç bir Q yapacak. Eminim başa çıkmak için yollar vardır .. eski bir Q bir yorum iş parçacığı kaçırmak bilmek için yeterince uzun zamandır gitmek için bir yol değil
chris neilsen

212

.Select/ .Activate/ Selection/ Activecell/ Activesheet/ ActiveworkbookVb ... 'den kaçınılması gereken iki temel neden

  1. Kodunuzu yavaşlatır.
  2. Genellikle çalışma zamanı hatalarının ana nedenidir.

Nasıl önleyebiliriz?

1) İlgili nesnelerle doğrudan çalışma

Bu kodu düşünün

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

Bu kod şu şekilde de yazılabilir:

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

2) Gerekirse değişkenlerinizi beyan edin. Yukarıdaki aynı kod şu şekilde yazılabilir:

Dim ws as worksheet

Set ws = Sheets("Sheet1")

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

17
Bu iyi bir yanıt, ancak bu konuda eksik olduğum şey aslında Etkinleştir'e ihtiyacımız olduğunda. Herkes bunun kötü olduğunu söylüyor, ancak hiç kimse onu kullanmanın mantıklı olduğu durumları açıklamıyor. Örneğin, 2 çalışma kitabıyla çalışıyordum ve ilk olarak etkinleştirmeden çalışma kitaplarından birinde makro başlatamadım. Biraz ayrıntı verebilir misiniz? Ayrıca, örneğin, bir aralığı bir sayfadan diğerine kopyalarken sayfaları etkinleştirmezsem, programı yürüttüğümde, ilgili sayfaları yine de dolaylı olarak etkinleştirir gibi görünüyor.
user3032689

1
Verileri yapıştırmanız veya filtrelemeniz gerekiyorsa, bazen önce bir sayfayı etkinleştirmeniz gerekebilir. Mümkün olduğunca aktif hale gelmekten kaçınmak için elimden geleni söyleyebilirim, ancak bunu yapmanız gereken durumlar var. Bu nedenle, yukarıdaki cevaba göre etkinleştirmeye ve mutlak minimum seviyeyi seçmeye devam edin.
Nick

7
Bence amaç onları kullanmaktan tamamen kaçınmak değil, mümkün olduğu kadar çok. bir çalışma kitabını kaydetmek istiyorsanız, böylece birisi onu açtığında belirli bir sayfada belirli bir hücre seçilirse, o sayfayı ve hücreyi seçmeniz gerekir. kopyala / yapıştır kötü bir örnektir, en azından değerler söz konusu olduğunda, bu gibi bir kodla daha hızlı yapılabilirSheets(2).[C10:D12].Value = Sheets(1).[A1:B3].Value
robotik

1
@Nick Yapıştırmak veya filtrelemek için sayfaları Etkinleştirmeniz gerekmez. Yapıştır veya filtre komutlarınızda sayfa nesnesini kullanın. Uygulama ile Excel nesne modelini öğrendikçe daha kolay hale gelir. Ben kullanıyorum sadece zaman inanıyorum.Activate yeni bir sayfa oluşturmak zaman, ama kod yapıldığında orijinal sayfanın görünmesini istiyorum.
phrebh

3
@phrebh .ActivateOrijinal sayfaya gitmek için kullanmanıza gerek yok , sadece kullanınApplication.Goto
GMalc

88

Yukarıda verilen tüm mükemmel cevaplara küçük bir vurgu ekleyeceğim:

Muhtemelen Select'i kullanmaktan kaçınmak için yapabileceğiniz en büyük şey , VBA kodunuzda adlandırılmış aralıkları (anlamlı değişken adlarıyla birlikte) kullanmaktır . Bu nokta yukarıda zikredilmiştir, fakat bir miktar üzerinde parlanmıştır; ancak, özel dikkat gerektirir.

Daha fazla düşünebileceğimden eminim, ancak adlandırılmış aralıkların liberal kullanımını sağlamak için birkaç ek neden.

Adlandırılmış aralıklar kodunuzun okunmasını ve anlaşılmasını kolaylaştırır.

Misal:

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

Adlandırılmış aralıkların Monthsve MonthlySalesiçeriğin ne olduğu ve prosedürün ne yaptığı oldukça açıktır .

Bu neden önemli? Kısmen diğer insanların anlaması daha kolay olduğu için, ancak kodunuzu görecek veya kullanacak tek kişi siz olsanız bile, yine de adlandırılmış aralıkları ve iyi değişken adlarını kullanmalısınız, çünkü onunla ne demek istediğinizi UNUTULACAKSINIZ bir yıl sonra, kodunuzun ne yaptığını anlayarak 30 dakika harcayacaksınız .

Adlandırılmış aralıklar, e-tablonun yapılandırması değiştiğinde (değilse!) Makrolarınızın bozulmamasını sağlar.

Yukarıdaki örnek şu şekilde yazılmış olsaydı:

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

Bu kod ilk başta gayet iyi çalışacaktır - yani siz veya gelecekteki bir kullanıcı "gee wiz, sanırım Sütun'taki yıl ile yeni bir sütun ekleyeceğim A" ya da aylar arasında bir gider sütunu koyacağım veya her bir sütuna bir başlık ekleyin. Şimdi, kodunuz bozuldu. Ve korkunç değişken isimleri kullandığınız için, nasıl düzeltileceğini anlamanız çok daha fazla zaman alacaktır.

Başlamak için adlandırılmış aralıklar kullandıysanız, Monthsve Salessütunları istediğiniz gibi hareket ettirilebilir ve kodunuz iyi çalışmaya devam eder.


6
Adlandırılmış aralıkların iyi veya kötü e-tablo tasarımı olup olmadığı konusundaki tartışmalar devam ediyor - kesinlikle kampta değilim. Deneyimlerime göre hataları arttırıyorlar (koda ihtiyaç duymayan standart kullanıcılar için).
brettdj


12
Gelişim felsefenize katılıyorum; ancak bence kağıt saçmalık. Aralık adlarının elektronik tablolarda hata ayıklayan acemileri nasıl karıştırabileceğinden bahseder, ancak karmaşık elektronik tablolara bakmak için acemi kullanan herkes hak ettiklerini alır! Eskiden finansal tabloları inceleyen bir firmada çalışıyordum ve bunun bir acemiye verdiğiniz bir iş olmadığını söyleyebilirim.
DeanOC

8
Anlamlı bir tartışma yok. Tanımlı isimlere karşı çıkan herkes sonuçlarının tam olarak anlaşılması için zaman ayırmamıştır. Adlandırılmış formüller, tüm Excel'deki en derin ve kullanışlı yapı olabilir.
Excel Hero

10
@brettdj: Alıntılarınız doğru, ancak altı "Hariç ..." ifadesi geldiğini belirtmeyi unuttunuz . Bunlardan biri: " Makro kodlamada hücre başvurularının yerine geçmesi dışında Makroları oluştururken her zaman hücre adlarının yerine Excel Adlarını kullanın. Bu, makro kodlamanın artık bulunmadığı ek satırların veya sütunların eklenmesinden kaynaklanan hatalardan kaçınmak içindir. amaçlanan kaynak verilere işaret eder. "
Marcus Mangelsdorf

47

Kısa cevabı vereceğim, çünkü herkes uzun cevabı verdi.

Makroları kaydedip yeniden kullandığınızda .select ve .activate alırsınız. Bir hücreyi veya sayfayı seçtiğinizde, onu etkinleştirir. Bu noktadan Range.Valuesonra, yalnızca etkin hücre ve sayfa kullandıkları gibi niteliksiz referanslar kullandığınızda. Bu, kodunuzun nereye yerleştirildiğini izlemezseniz veya bir kullanıcı çalışma kitabını tıklarsa da sorunlu olabilir.

Böylece, doğrudan hücrelerinize başvurarak bu sorunları ortadan kaldırabilirsiniz. Hangi gider:

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

Veya yapabilirsin

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

Bu yöntemlerin çeşitli kombinasyonları vardır, ancak bu benim gibi sabırsız insanlar için mümkün olan en kısa sürede ifade edilen genel fikir olacaktır.


33

"... ve Select işlevleri yerine değişkenler kullanabilseydim kodumun daha kullanılabilir olacağını görüyorum."

.SelectDoğrudan hücre referansından daha iyi bir seçim olacak yalıtılmış bir avuç durumdan daha fazlasını düşünemesem de , savunulmaya Selectionve .Selectkaçınılması gereken aynı nedenlerle atılmaması gerektiğine işaret ediyorum .

Kısa tuş kombinasyonlarına atanan kısa, zaman kazandıran makro alt rutinlerine sahip olduğunuz zamanlar birkaç tuşa dokunulduğunda çok zaman kazandırır. Çalışma sayfası genelinde veri formatına uymayan cepli verilerle uğraşırken çalışma harikaları üzerinde çalışma kodunu etkinleştirmek için bir hücre grubu seçebilme. Bir hücre grubunu seçip bir biçim değişikliği uygulayabileceğiniz gibi, özel makro kodunu çalıştırmak için bir hücre grubu seçmek de büyük bir zaman tasarrufu olabilir.

Seçime Dayalı alt çerçeve örnekleri:

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

İşlenecek gerçek kod, tek bir satırdan birden çok modüle kadar herhangi bir şey olabilir. Dış çalışma kitaplarının dosya adlarını içeren hücre düzensiz bir seçim üzerinde uzun süre çalışan rutinleri başlatmak için bu yöntemi kullandım.

Kısacası, atmayın Selectionnedeniyle ile yakın ilişkiye .Selectve ActiveCell. Çalışma sayfası özelliği olarak başka birçok amacı vardır.

(Evet, bu sorunun hakkında olduğunu biliyorum .Select, değil Selectionama acemi VBA kodlayıcılarının çıkardığı yanlış anlamaları ortadan kaldırmak istedim.)


13
Selectionçalışma sayfasında herhangi bir şey olabilir; bu nedenle, açıkça bir değişken olarak bildirdiğinizden önce bir değişkene atamadan önce nesnenin türünü test edebilirsiniz Range.
L42

29

Aşağıda, Select yaklaşımını (OP'nin kaçınmak istediği yaklaşım) Range yaklaşımı ile karşılaştırdığımı unutmayın (ve bu sorunun cevabıdır). İlk Seçimi gördüğünüzde okumayı bırakmayın.

Gerçekten ne yapmaya çalıştığınıza bağlı. Her neyse, basit bir örnek yararlı olabilir. Aktif hücrenin değerini "foo" olarak ayarlamak istediğinizi varsayalım. ActiveCell'i kullanarak şöyle bir şey yazabilirsiniz:

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

Etkin olmayan bir hücre için, örneğin "B2" için kullanmak istiyorsanız, önce bunu seçmelisiniz, şöyle:

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

Aralıkları kullanarak, istediğiniz hücrenin değerini istediğiniz gibi ayarlamak için kullanılabilecek daha genel bir makro yazabilirsiniz:

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

Ardından Macro2'yi aşağıdaki gibi yeniden yazabilirsiniz:

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

Ve Macro1 şöyle:

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

Umarım bu biraz şeyleri temizlemeye yardımcı olur.


1
Mükemmel yanıt için çok hızlı teşekkürler. Yani bu normalde aralığa hücre eklersem, aralığı adlandırır ve yineleyerek, bir dizi oluşturmak için doğrudan atlamalıyım?
BiGXERO

Ne demek istediğinizi anladığımdan emin değilim, ancak tek bir komutla bir Aralık oluşturabilirsiniz (örneğin, Aralık ("B5: C14")) ve değerini bir kerede bile ayarlayabilirsiniz (eğer aynı olması gerekiyorsa) aralıktaki her hücre), örneğin Aralık ("B5: C14") Değer = "abc"
Francesco Baruchelli 23:12

29

Kaçınmak Selectve Activatebiraz daha iyi VBA geliştirici yapar hamle. Genel olarak Selectve Activatebir makro kaydedildiğinde kullanılır, böylece Parentçalışma sayfası veya aralığı her zaman etkin olan olarak kabul edilir.

Bundan kaçınabilirsiniz Selectve Activateaşağıdaki durumlarda:


Yeni bir Çalışma Sayfası ekleme ve üzerine bir hücre kopyalama:

Kimden (makro kaydedici ile oluşturulan kod):

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

Kime:

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

Aralığı çalışma sayfaları arasında kopyalamak istediğinizde:

Gönderen:

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

Kime:

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

Süslü adlandırılmış aralıkları kullanma

Onlara []diğer yolla karşılaştırıldığında, gerçekten güzel olanlara erişebilirsiniz . Kendini kontrol et:

Dim Months As Range
Dim MonthlySales As Range

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

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

Yukarıdaki örnek şöyle görünecektir:

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

Değerleri kopyalamak değil, almak

Genellikle, eğer istekliyseniz select, muhtemelen bir şey kopyalıyorsunuz demektir. Yalnızca değerlerle ilgileniyorsanız, bu seçim yapmaktan kaçınmak için iyi bir seçenektir:

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


Her zaman Çalışma Sayfasına da başvurmaya çalışın

Bu muhtemelen en yaygın hatadır . Aralıkları kopyaladığınızda, bazen çalışma sayfasına başvuru yapılmaz ve bu nedenle VBA, ActiveWorksheet'in yanlış sayfasını dikkate alır.

'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

Gerçekten hiçbir zaman .Selectveya .Activatehiçbir şey için kullanamaz mıyım?

  • Kullanımda ne zaman gerekçelendirilebileceğinize .Activateve .Selectgörsel nedenlerle belirli bir Çalışma Sayfasının seçildiğinden emin olmak için iyi bir örnek . Örneğin, Excel'iniz her zaman önce kapak çalışma sayfası seçiliyken açılır ve dosya kapatıldığında hangisinin ActiveSheet olduğunu göz ardı eder.

Böylece, aşağıdaki kod gibi bir şey kesinlikle sorun yok:

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

Çalışma Sayfaları yerine Application.Goto komutunu kullanabilirsiniz. Biraz daha az riskli.
Geoff Griswald

1
Geç cevap FYI - bir ihtiyaç için güzel ve biraz beklenmedik bir örnek .Select- ve benim etrafında çalışma - bulunabilir Tüm sayfalara aynı bilgi nasıl yazılır - @Vityata :)
TM

1
@TM - gerçekten ilginç bir örnektir ve muhtemelen 100'den fazla çalışma sayfası için milisaniye tasarruf sağlar, ancak bir yerde görürsem muhtemelen cesaretim kırılır. Her neyse, orada Seçim açıkça yazılmış değil, bir sonucu .FillAcrossSheets, bu arada bu arada bir yerde (en azından VBA taksonomisi hakkında fikrim)
Vityata

17

Her zaman çalışma kitabını, çalışma sayfasını ve hücreyi / aralığı belirtin.

Örneğin:

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

Son kullanıcılar her zaman sadece düğmelere tıklayacak ve odak çalışma kitabından çıkar çıkmaz kod ile çalışmak istediklerinde işler tamamen yanlış gidiyor.

Ve asla çalışma kitabının dizinini kullanmayın.

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

Kullanıcı kodunuzu çalıştırdığında başka hangi çalışma kitaplarının açılacağını bilmiyorsunuz.


7
Çalışma sayfalarının adları da değişebilir. Bunun yerine kod adlarını kullanın.
Rick,

Tabii ki çalışma sayfası adları değişebilir. Ama bunu hafifletmeye çalışmak için kodunuzu aşırı karmaşıklaştırmanız gerektiğini kabul etmiyorum. Bir kullanıcı bir sayfanın adını değiştirirse ve makrosu çalışmayı durdurursa, bu sayfa onlar üzerindedir. Genellikle çalışma sayfası adlarının aynı olacağını varsayarım. Özellikle kritik makrolar için, uygun makroya başlamadan önce küçük bir uçuş öncesi kontrol yapıyorum, bu da bulmayı beklediği tüm sayfaların gerçekten orada olduğundan emin olmak için kontrol ediyor ve eğer eksikse kullanıcıya hangisini bildirir.
Geoff Griswald

10

Bu yöntemler oldukça damgalanmıştır, bu nedenle kumda bir çizgi çizmek için @Vityata ve @Jeeped'in liderliğini almak:

Deme Neden .Activate, .Select, Selection, ActiveSomethingyöntemler / özellikler

Temel olarak, öncelikle Uygulama Arayüzü üzerinden kullanıcı girişini işlemek için çağrıldıklarından. Kullanıcı kullanıcı arabirimi üzerinden nesneleri işlerken çağrılan yöntemler olduğundan, bunlar makro kaydedici tarafından kaydedilen yöntemlerdir ve bu nedenle bunları çağırmak çoğu durum için kırılgan veya gereksizdir: Selectionsonradan bir eylem gerçekleştirmek için .

Ancak, bu tanım, çağrıldıkları durumları belirler:

Çağırmak zaman .Activate, .Select, .Selection, .ActiveSomethingyöntemler / özellikler

Temel olarak, son kullanıcının yürütmede bir rol oynamasını beklediğinizde .

Geliştiriyorsanız ve kullanıcının kodunuzun işleyeceği nesne örneklerini seçmesini bekliyorsanız .Selectionveya .ActiveObjectuygunsa.

Öte yandan, .Selectve .Activatekullanıcının bir sonraki eylemi çıkarabiliriz zaman kullanım ve muhtemelen ona biraz zaman ve fare tıklamaları tasarruf kodunuzu kullanıcıya rehberlik etmek istiyorum. Örneğin, kodunuz yeni bir grafiğin yepyeni bir örneğini oluşturduysa veya güncellenmişse, kullanıcı bunu kontrol etmek isteyebilir .Activateve kullanıcıyı arama zamanından kurtarmak için onu veya sayfasını arayabilir ; veya kullanıcının bazı aralık değerlerini güncellemesi gerektiğini biliyorsanız, bu aralığı programlı olarak seçebilirsiniz.


6

IMHO'nun kullanımı .selectbenim gibi insanlardan geliyor, çünkü VBA öğrenmeye makroları kaydederek ve sonra kodu fark etmeden değiştirerek başladı .selectve daha sonra selectionsadece gereksiz bir orta adam.

.select birçok yayınlanmış haliyle, zaten var olan nesnelerle doğrudan çalışarak, i ve j'nin karmaşık bir şekilde hesaplanması ve daha sonra hücre (i, j) vb. düzenlenmesi gibi çeşitli dolaylı referanslara izin vererek önlenebilir.

Aksi takdirde, .selectkendisi ile dolaylı olarak yanlış bir şey yoktur ve bunun için kullanımları kolayca bulabilirsiniz, örneğin tarihle doldurduğum bir elektronik tablom var, onunla biraz sihir yapan makroyu etkinleştiriyorum ve ayrı bir sayfada kabul edilebilir bir formatta dışa aktarıyorum. ancak, bitişik bir hücreye bazı son manuel (öngörülemeyen) girişler gerektirir. İşte bu an için .selectbana ek fare hareketi ve tıklaması kazandırır.


2
Haklı olsanız da, select ile dolaylı olarak yanlış olan en az bir şey var: yavaş. Bir makroda olan her şeye kıyasla gerçekten çok yavaş.
vacip

4

Hızlı cevap:

.SelectYöntemi kullanmaktan kaçınmak için istediğiniz özelliğe eşit bir değişken ayarlayabilirsiniz.

► Örneğin, içindeki değerin olmasını Cell A1istiyorsanız o hücrenin value özelliğine eşit bir değişken ayarlayabilirsiniz.

  • Misal valOne = Range("A1").Value

► Örneğin, 'Sayfa3' kod adını istiyorsanız, o çalışma sayfasının kod adı özelliğine eşit bir değişken ayarlayabilirsiniz.

  • Misal valTwo = Sheets("Sheet3").Codename

Umarım bu yardımcı olur. Herhangi bir sorunuz olursa bize bildirin.


3

Bu cevapların hiçbirinin .Offset Özelliğinden bahsetmediğini fark ettim . Bu aynı zamanda, Selectbelirli hücreleri manipüle ederken, özellikle seçilen bir hücreye referans olarak (OP'den bahsedildiği gibi ActiveCell) eylemin kullanılmasını önlemek için de kullanılabilir .

İşte birkaç örnek.

Ayrıca "ActiveCell" in J4 olduğunu varsayacağım .

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

  • Bu, hücrenin J6değeri 12 olacak şekilde değiştirir.
  • Eksi -2, J2

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

  • Bu hücreyi kopyalar k4için L4.
  • Gerekmiyorsa ofset parametresinde "0" gerekmediğini unutmayın (, 2)
  • Önceki örneğe benzer şekilde eksi 1 i4

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

  • Bu, k sütunundaki tüm hücrelerdeki değerleri temizler.

Bunlar, yukarıdaki seçeneklerden "daha iyi" oldukları anlamına gelmez, sadece alternatifleri listeler.


0

.Parent özelliği ile çalışma. Bu örnek, yalnızca bir myRng referansının ayarlanmasının .Select, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet ve benzeri olmadan tüm ortama dinamik erişimi nasıl sağladığını gösterir. (Genereik yoktur .Çocuk özelliği)

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

Çok güzel, ama bunun OPs sorusu ile ne ilgisi olduğundan emin değilim. Select veya ActiveSheet kullanmadan VBA'da çalışmak için "Ebeveyn" e ihtiyacınız yoktur
Geoff Griswald

0

Select veya Activesheet kullanmamanın ana nedeni, çoğu insan makronuzu çalıştırdıklarında en azından başka bir çift çalışma kitabının açık (bazen düzinelerce) olması ve makronuz çalışırken sayfanızdan uzağa tıklayıp başka birisine tıklamasıdır. açtıklarında "Etkin Sayfa" değişir ve kalifiye olmayan "Seç" komutunun hedef çalışma kitabı da değişir.

En iyi ihtimalle, makronuz çökecektir, en kötü ihtimalle yanlış çalışma kitabındaki değerleri yazmaya veya hücreleri değiştirmenize neden olabilir.

Ben takip basit bir altın kural var: Çalışma kitabı nesnesi ve bir çalışma sayfası nesnesi için "wb" ve "ws" adlı değişkenleri ekleyin ve her zaman bu benim makro kitap başvurmak için kullanın. Birden fazla kitaba veya birden fazla sayfaya başvurmam gerekirse daha fazla değişken eklerim.

Örneğin

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

"Set wb = ThisWorkbook" komutu kesinlikle anahtardır. "ThisWorkbook" Excel'de özel bir değerdir ve VBA kodunuzun şu anda çalıştığı çalışma kitabı anlamına gelir . Çalışma Kitabı değişkeninizi ayarlamak için çok yararlı bir kısayol.

Bunu Sub'nizin üst kısmında yaptıktan sonra, bunları kullanmak daha kolay olamazdı, sadece "Seçim" i kullandığınız her yerde kullanın:

Bu nedenle, "Çıktı" içindeki "A1" hücresinin değerini "Merhaba" olarak değiştirmek için:

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

Şimdi bunu yapabiliriz:

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

Bu, yalnızca çok daha güvenilir değildir ve kullanıcı birden fazla e-tabloyla çalışıyorsa çökme olasılığı daha düşük değildir, aynı zamanda çok daha kısa, daha hızlı ve daha kolay yazılır.

Ek bir bonus olarak, değişkenlerinize her zaman "wb" ve "ws" adını verirseniz, kodu bir kitaptan diğerine kopyalayıp yapıştırabilirsiniz ve genellikle varsa minimum değişikliklerle çalışır.


1
Benim oyum değil, ama mevcut cevaplarda zaten önerilenlere yeni bir şey eklediğinden emin değilim.
BigBen

Evet, cevabım biraz gereksiz, ancak diğer cevaplar çok uzun, çok fazla gereksiz şey içeriyor ve kimse çalışma sayfası değişkeninizi önceden ayarlamak için ThisWorkbook'u kullanmadan bahsetmedi. Bu, eğer birisi bana ilk kez bir ayak parmağını VBA'ya batırdığımda, inanılmaz derecede yararlı bulacağım bir şeydi. Diğerleri bir Çalışma Sayfası değişkeni kullandığından bahsetmişlerdir, ancak nedenini çok iyi açıklamamaktadır ve çalışma sayfası ve çalışma kitabı değişkenlerini kullanarak ve kullanmadan bir kod örneği sunmamaktadır.
Geoff Griswald

Ama kabul edilen cevap kesinlikle tartışıyor ThisWorkbook... Yorumunuzun doğru olduğundan emin değilim.
BigBen

Öyle, yanlış değilsin. Ancak, bir çalışma kitabı değişkeni ayarlamak ve ileriye doğru bu çalışma kitabı değişkenini kullanmak veya önerdiğim gibi bir çalışma sayfası değişkeni ayarlamak için bu çalışma kitabı değişkenini kullanmak bağlamında değil. Cevabım yeni başlayanlar için kabul edilen cevaptan daha kısa, daha basit ve daha erişilebilir.
Geoff Griswald

-3

Bu, "A1" hücresinin içeriğini (veya seçim türü xllastcell vb. İse daha fazlasını) temizleyecek bir örnektir. Hepsi hücreleri seçmek zorunda kalmadan yapılır.

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

Umarım bu birine yardımcı olur.


1
Hayır, özür dilerim. Orada yaptığınız şey bu değil. Gerçekte yaptığınız şey, "Select" kullanmaktan farklı olmayan "Application.GoTo" komutunu kullanarak "A1" hücresini seçmektir, ardından seçiminizde clearcontents kullanılır. Hücreleri seçmeden bunu yapmanın yolu, Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1").ClearContentsiki değil, bir satırdır ve aslında hücreleri seçmeden çalışır.
Geoff Griswald
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.