29.10.2018 tarihinde VBA bölümüne Web'den veri çekme sayfaları eklenmiştir

22.10.2018 tarihinde VBA bölümüne Veritabanarıyla çalışma sayfaları eklenmiştir

30.09.2018 tarihinde Excel bölümüne Dış Verilerle çalışmak sayfası eklenmiştir

04.08.2018 tarihinde VBA bölümüne ObjelerDünyası sayfası eklenmiştir

25.07.2018 tarihinde VBA bölümüne Outlook programlama sayfası eklenmiştir

13.07.2018 tarihinde VBA bölümüne Formlar-Kontroller sayfası eklenmiştir

VBAMakro Olaylar 3

Worksheet Olayları(Eventleri)

Bir dosyadaki çeşitli olaylara tepki vermek adına devreye giren olaylar Worksheet olayları olarak adlandırılır. Bunları da yine Worbook olaylarını seçer gibi seçip içlerini doldurmaya başlayabilirsinz. İlgili combobox seçildiğinde aşağıdaki gibi eventlerin bir kısmı görünür.

Bunlardan en sık kullancaklarımız;

  • Change
  • SheetChange
  • BeforeDoubleClick
  • Activate/Deactivate

eventleridir.

Pivot tablolarla ilgili olanlar da önemli olup bunlara Pivot İşlemleri konusunda değineceğiz. Şimdi sırayla önemli eventlere bakalım.

Temel olaylar

Worksheet_Change Event

Kuşkusuz en önemli sayfa olayı sayfada bir hücrenin değişimiyle meydana gelen Change olayıdır. (Bu eventin adını AfterChange gibi düşünmeniz yerinde olur. Zira olay, hücre içi değiştikten sonra meydana gelir. Microsoft geliştiricileri olayın adını keşke böyle yapsalarmış. Ne de olsa After ve Before ile başlayan bir sürü event var.) Syntax'ı aşağıdaki gibidir.

Private Sub Worksheet_Change(ByVal Target As Range)

End Sub

Küçük bir örnek yapalım. Bu örnekte, her değişim oldukça sayfanın rengi değişsin. Bu örneği alıp istediğiniz bir dosyanın Sheet1 modülüne yapıştırın ve sonra gidip sayfada rasgele hücrelere birşey girin. Her Enter'a basışınızda sayfa rengi değişecektir.

Private Sub Worksheet_Change(ByVal Target As Range)
  x = WorksheetFunction.RandBetween(1, 1000000)
  ws.Cells.Interior.Color = x
End Sub

Tetikleyiciler ve özel hususlar

Change olayı kullanıcının manuel bir işlemi sonucunda tetiklenebileceği gibi bir makro kodu sonucunda da tetiklenebilir.

Bazı özel durumlar da vardır:

  • Manuel hesaplama durumundan otomatik hesaplama durumuna geçildiğinde de hücrelerin içi değişir ama bu durum Change olayını tetiklemez. Yine de yeni duruma göre içerik kontrolü yapacaksanız bu sefer Calculate olayını kullanmanız gerekir. Mantık aynı olup, yazmak istediğiniz şeyi Change yerine Calculate eventi içine yazmanız gerekir. Tabi burda hedef bir hücre(Target) bulunmaz, zira tüm hücreler yeniden hesaplanmıştır.
  • Bir hücrenin içini silmek de değişiklik olduğu için Change olayı tetiklenir.
  • Merge butonu ile hücre birleştirmek tetiklemez.
  • Bir alanı sıralamak tetiklemez
  • Goal Seek kullanarak bir hücrenin değişimi tetiklemez

Target Parametresi

Target parametresi, belli bir hücrenin içeriğini değişip değişmediği öğrenmek amacıyla kullanılabileceği gibi ilgili hedefin tek bir hücre mi yoksa bir range mi olduğunu belirlemek için de kullanılabilir. Aslında Range nesnesinin tüm özelliklerini kontrol etmek için kullanılabilir.

If Target.Address="$A$1" Then 'bu bir adres kontrolüdür
If Target.Cells.Count=1 Then 'bu da tek bir hücre mi yoksa bir range mi kontrolüdür

Target'ın belirli bir aralıkta olup olmadığını öğrenmek için özel bir kullanım şekli vardır: If Not Intersect(Target, Range("..")) Nothing

Aşağıdaki örnekte değişen hücrenin C3 veya C4'te olması beklenmektedir. Bununla ilgili daha detaylı örnek Çeşitli Örnekler bölmünde yapılacaktır.

Private Sub Worksheet_Change(ByVal Target As Range)
    If Not Intersect(Target, Range("C3:C4")) Is Nothing Then
        'ana kod bloğu
    Else
        MsgBox "Yanlış yerden seçim yapıyorsunuz, sadece C3 ve C4 hücrelerini kullanınız"
    End If
End Sub

Aynı hücredeki değişimlerde bir önceki değeri elde etme

Değişen hücrenin bir önceki değerini elde etmek istiyorsak Statik değişken kullanırız.

Private Sub Worksheet_Change(ByVal Target As Range)

Static öncekiDeğer As String
Static öncekiAdres As String

If öncekiDeğer <> "" And öncekiAdres = Target.Address Then
   MsgBox "Önceki:" & öncekiDeğer
End If

öncekiDeğer = Target.Value
öncekiAdres = Target.Address

MsgBox "yenisi:" & Target.Value

End Sub

Bu örnekte statik değişkenlerimiz ilk başta boş olacaktır, zira henüz "öncesi" yoktur. İlk işlemden sonra önceki statik değişkenler dolmaya başlayacaktır. Akabinde, yeni hücre ile öncekinin aynı olup olmadığı kontrol edilir.

Worksheet_SelectionChange

Seçili hücre her değiştiğinde bu event oluşur.

Private Sub Worksheet_SelectionChange(ByVal Target As Range)

End Sub

Target, seçilen hücreyi gösterir.

Aşağıdaki örnekte, seçilen hücre pencerenin sol üst köşesindeki ilk hücre olacak şekilde ayarlanır.

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    With ActiveWindow
        .ScrollRow = Target.Row
        .ScrollColumn = Target.Column
    End With  
End Sub

Önceki seçimi elde etme

Seçimden bir önceki hücreye de ihtiyacımız olacaksa Statik bir değişken kullanırız. İlk seçimde çalışmaz, sonrakilerde çalışır, çünkü ilk seçimde henüz "öncesi" yoktur.

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Static öncekiRange As String

If öncekiRange <> "" Then
  MsgBox "önceki:" & Range(öncekiRange).Address
End If

öncekiRange = Target.Address

MsgBox "yenisi:" & Target.Address
End Sub

Daha farklı bir örnek ise, önceki hücre ile yeni hücre arasındaki alanı kırmızıya boyamak olabilir. "Ne işimize yarayacak" diye sormayın, bu haliyle bir işinize yaramaz, ama farklı bir konuda size fikir verebilir.

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Static öncekiRange As Range

If Not öncekiRange Is Nothing Then
    Range(öncekiRange, Target).Interior.Color = vbRed
End If

Set öncekiRange = Target

End Sub

BeforeDoubleClick

Bir hücreye çift tıklandığında bu olay olur ve Exceli'n o anda nasıl davranmasını istiyorsak bu prosedüre bunları yazarız. Syntaxı aşağıdaki gibidir.

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As BooleEnd Sub

End Sub

Target'ı şimdiye kadar öğrenmiş olmalısınız; kullanım mantığı yine yukardakilerle aynı. Cancel parametresine ise True değerini atayarak eylemi iptal edebiliriz, yani Excele çift tıklama olmamış gibi davrandırtabiliriz.

En sık kullandığım caselerden birisi, toplanmış verileri tutan bir listede ilgili hücreye çift tıklama sonucunda o grubun alt detayını gösteren verilerin uygun miktarda satır açılarak araya eklenmesi; aynı hücreye tekrar çift tıklanması durumunda ise bu kayıtların animasyonlu bir şekilde silinip(sanki bu sitede bordo arkaplanlı başlıklara tıklandığında yavaşça katlanmasını sağlayan Jquery kodlarına benzer) listenin ilk hale gelmesidir. Böyle bir örnek kullanımı ADO içermesi sebebiyle bu sayfada vermeyip bunları veritabanı uygulamaları bölümünde ele alıyor olacağım. İlgili örneğe buradan ulaşabilirsiniz. Aynı örneği ilgili veriyi aynı sayfada gizlenmiş bir şekilde dururken unhide ederek de yapabilirsiniz. Ancak az önceki linkteki örnekteki liste dinamik bir yapıya sahip olduğu için hide etmek bir uygun bir çözüm olmamaktaır.

Başka bir örneği ise burada ele alabiliriz. Bunda da yine gruplu bir liste var. Bu listede bir hücreye çift tıklayınca bu hücreye ait alt veriler ayrı bir dosya olarak açılıyor olsun. Ör:En çok kredi düşüşü yaşayan şube listesinde şube koduna çift tıklayınca bize en çok düşüş yaşayan müşteriler dosyasını açıp bu şubeyi filtrelesin.

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)

adres = "C:\...\"

If Not Intersect(Target, Range("B2:B20")) Is Nothing Then
sb = Target.Value
Workbooks.Open adres + "Kredisi en çok düşen müşteriler - " & Date - 1 & " Sonuçları.xlsm", ReadOnly:=True
ActiveSheet.ListObjects("Query_from_DWH").Range.AutoFilter Field:=2, Criteria:=sb
End If

End Sub

Şahsen ben bu eylemi çok önemsiyorum. Bununla ADO'yu birleştirerek yüksek ücretli programlara alternatif programlar yazabilirsiniz. ADO kısmında diğer detayları bulabilirsiniz.

Activate/Deactivate

Belli bir sayfa (yeniden) aktif(veya inaktif) olduğunda çalışmasını istediğiniz kodları bu olayla tetiklenen olay prosedürleri içine yazabilirsiniz.

Private Sub Worksheet_Activate()

End Sub

Örneğin, ana menü sayfası gibi bir sayfanız var ve buna sadece diğer sayfalardaki Anamenü linki aracılığı ile ulaşmak istiyorsunuz, ve bu sayfalar açıken bu menü sayfası görünmesin istiyorsanız, işte bu menü sayafasından ayrılırken sayfanın gizlenmesini sağlayacak bir kod yazabilirsiniz.

Private Sub Worksheet_Deactivate()
   Me.Visible = xlSheetHidden 
End Sub

'aşağıdaki kodu da diğer sayfalardaki AnaMenü butonunun Click eventine yazarsınız
Sheets("Anamenü").Visible=xlSheetVisible

Diğer Hususlar

Event tetiklenmesini bastırmak(Geçici olarak durdurmak)

Makronuzda, bir yerlerde ilgili eventi tekrar tetikleyecek bir kod varsa bu kod sonsuz döngüye girer ve Excel çökebilir(veya ayarlarınıza göre 100 civarı iterasyon sonucunda durabilir, bende 78.iterasyonda duruyor). Change eventi içinde bir hücrenin içeriği değiştirilmesi veya SelectionChange eventi içinde başka bir hücre seçilmesi gibi.

Mesela aşağıdaki örneği F8 ile deneyip görün, her F8 yapışınızda kod hiç durmadan bir aşağı inecektir.

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
   Target.Offset(1, 0).Select
End Sub

Aşağıdaki kodda ise sürekli olarak Change olayı kendisini tetikliyor.

Private Sub Worksheet_Change(ByVal Target As Range)
   Target.Offset(1, 0).Value = Target.Row
End Sub

İşte bu tür durumları önlemek için eventin başında Application.EnableEvents = False diyerek eventleri geçici olarak askıya alırız, sonra işlemleri yaptırır, sonra da Application.EnableEvents = True diyerek evetnleri tekrar devreye sokarız. Tabi olur da kodumuzda bir hata oluşur da sona gelmeden durursa Eventler askıda kalabilir, bu yüzden bir hata yönetimi bloğu yazıp eventleri burda da tekrar aktive etmeliyiz.

Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo hata
    Application.EnableEvents = False
    'işlemler
    Application.EnableEvents = True
    Exit Sub

hata:
    Application.EnableEvents = True
End Sub

Workbook'un sheet eventleri

Workbook eventleri workbookla ilgili bir eylem gerçekleşince devreye giriyordu, Worksheet eventleri de sayfayla ilgili bir eylem gerçekleşince. Bir de ikisinin karışımı gibi olan ama aslında bir Workbook eventi olan event grubu var.

Bunların bir listesi aşağıdaki gibi olup, belli bir sayfada değil de herhangi bir sayfada bir eylem gerçekleştiğinde tetiklenirler.

Mesela aşağıdaki kod ile hangi sayfa seçilirse onun adı bize MsgBox ile gösterilir.

Private Sub Workbook_SheetActivate(ByVal Sh As Object)
   MsgBox Sh.Name
End Sub

Farklı kullanıcılarda eventlerin tetiklendiğinden emin olmak

Giriş bölümündeki Güvenlik ayarları bölümünü okumadıysanız öncelikle orayı okumanızı öneririm. Orada belirtildiği gibi makro ayarları Disable All şeklindeyse sonuçta bir makro olan Event Prosedürleriniz de devreye girmez.

Örnek senaryo

Hazırladığınız bir dosyanın anlamlı olabilmesi için eventlerin çalışması gerekmekte olsun. Ancak kullanıcının makro ayarları Disabled ise kullanıcı dosyadan istenen verimi alamayacaktır, üstelik sizin istemediğiniz şekilde yetkisi olmayan görüntülemeler bile yapabilecektir.(Farklı şubenin rakamlarını görmek gibi)

Bunu engellemek için benim geliştirdiğim yöntem aşağıdaki gibidir(Daha iyi veya daha kötü yöntemler var olabilir, ben araştırdığımda hiçbirşeyle karşılaşmadığım için kendi çözümümü böyle geliştirmiştim)

Çalışmanın tam üstüne denk gelecek şekilde bir düğme koyarım ve bu düğme için bir kod yazarım. Eğer makrolar enable ise düğme kaybolur, makrolar disabled ise aşağıdaki gibi bi hata alır.

Ayrıca düğmeyi silmesin veya başka bi yere taşımasın diye sayfaya protection da koymamız gerekiyor. Makro sırasında dosyayı gizlerken geçici olarak kaldırıyor, gizledikten sonra tekrar koyuyoruz, ki protection'ı başka amaçlar için de kullanabilelim. Buna ait bir örneği Çeşitli Örnekler bölümünde 2.örnekte bulabilrisinz.

Düğmenin Click eventi ise şöyledir.

Sub Button1_Click()
   Sheets(1).Unprotect Password:="1234" 
   ActiveSheet.Shapes("Button 1").Visible = msoFalse 'düğmeyi gizler
   Sheets(1).Protect Password:="1234"
End Sub

Kısıtlar uygulamak

Sayfanın yazdıırlmasını engellemek

Diyelim ki kullanıcıların belli sayfaları basmasını istemiyorsunuz. Aşağıdaki kodu ilgili dosyanın Workbook_BeforePrint eventine yazmanız gerekir.

Private Sub Workbook_BeforePrint(Cancel As Boolean)

For Each s In ActiveWorkbook.SelectedSheets
If s.Name = "Ham Data" Then
MsgBox ("Bu sayfayı basamazsınız!!!")
Cancel = True
End If
Next
End Sub

Workbook içinde hiçbir sayfanın bastırılmasını istemiyorsanız bu sefer hiç safya kontrolü yapmadan doğrudan MsgBox ve Cancel=True satırları yeterli olacaktır.

Gördüğünüz gibi bu işlemi bir worksheet eventi ile değil workbook eventi ile yapıyoruz.

Sayfada cut/copy engellemek

Bu işlemin tüm dosya bazında yapılmasıyla ilgili örnek şurada olup, sayfa bazında yapmak için Worksheet_Activate ve Worksheet_Deactivate olaylarında kullanılması yeterlidir.

Çeşitli Örnekler

Mevduat fiyatlama hesap makinası(Animasyonlu)

Bu örnekte, 4 parametreden oluşan bir denklemin herhangi 3'ü bilinirken diğer 4.sünün tespit edilmesine yönelik bir kod yazacağız. Klasik Excel yöntemiyle yapmak istediğinizde 4 ayrı çalışma yapmanız gerekirken VBA ile tek bir format ile tüm senaryoları ele alabileceğiz.

Bunun için aşağıdaki gibi bir form hazırladım. Dosyanın kendisine buradan ulaşabilirsiniz.

Çalışmaya ait kodlar şöyle:

Önce Sheet1 modülü:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo çıkış
    Application.EnableEvents = False
    
    If Target = [stopaj] And IsEmpty(Target) Then
        Target.Value = "0,15 (TL, 6 aya kadar)"
    End If
    
    If Not Intersect(Target, [alan]) Is Nothing Then
        ActiveSheet.Unprotect 1234
        Call temizlik([alan])
        If [alan].Cells.SpecialCells(xlCellTypeBlanks).Count = 1 Then            [alan].SpecialCells(xlCellTypeBlanks).Select
            Select Case ActiveCell
                Case [anapara]
                    ActiveCell.Formula = "=365*NetGetiri/(Vade*Faiz*(1-value(left(stopaj,4))))"
                Case [Faiz]
                    ActiveCell.Formula = "=365*NetGetiri/(Vade*Anapara*(1-value(left(stopaj,4))))"
                Case [Vade]
                    ActiveCell.Formula = "=365*NetGetiri/(Anapara*Faiz*(1-value(left(stopaj,4))))"
                Case [NetGetiri]
                    ActiveCell.Formula = "=Anapara*Faiz*Vade*(1-value(left(stopaj,4)))/365"
                Case Else
                    MsgBox "Böyle bir seçenek bulunmamaktadır"
            End Select
            ActiveCell.Font.Color = vbRed
            [uyarı].Value = ""
            Call Fontsizedeğiş(24, 20)
            Call alancopypaste
        End If
    End If
    Application.EnableEvents = True
    ActiveSheet.Protect 1234
    
    Exit Sub
    
çıkış:
If Err.Description = "No cells were found." Then 'blank sayısı 0 ise, count=0 kontrolüne gelmediği için o noktayı kaldırdım
    [uyarı].Select
    ActiveCell.Value = "Lütfen hangi alanın yeniden hesaplanmasını istiyorsanız onu silin."
    Call Fontsizedeğiş(14, 10)
End If
Application.EnableEvents = True
ActiveSheet.Protect 1234
End Sub
'----------------------------------------------------
Sub temizlik(alan As Range)
For Each a In alan
    a.Font.Color = vbBlack
Next a
End Sub
Sub Fontsizedeğiş(x As Integer, s As Integer)
    For i = 1 To 5
        Call Module2.beklet(s)
        DoEvents
        ActiveCell.Font.Size = x + i * 2
    Next i
    
    For i = 1 To 5
        Call Module2.beklet(s)
        DoEvents
        ActiveCell.Font.Size = x + 10 - i * 2
    Next i
End Sub
'----------------------------------------------------
Sub alancopypaste()
    For Each a In [alan]
        a.Value = a.Value
    Next a
End Sub
Standart Modül içeriği

Bunda sleep metodu kullanıldğı için aşağıdaki özel kod en başa eklenmiştir.

#If VBA7 Then
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr) '64 Bit Sistemler için
#Else
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) '32 Bit Sistemler için
#End If

Sub beklet(sure As Integer)
    Sleep sure
End Sub
Temizlik butonunun kodu ise şöyledir.
Sub Button1_Click()
  Range("alan").ClearContents
  ActiveSheet.Unprotect 1234
  [uyarı].Value = ""
  ActiveSheet.Protect 1234
End Sub
Bu hesap makinesinin kullanımı şöyledir:

Kullanıcı diyelim ki, bilinen olarak anapara, faiz ve  vadeyi girip müşterinin net kazancını hesaplamak istiyor olsun. Bu üçünü yazınca net kazanç bilgisi otomatik hesaplanır. Bu hesaplamanın sonucu da bir döngü ile font hacminin önce büyüyüp sonra da küçülmesiyle animasyonlu bir şekilde gösterilir.

Kullanıcı diyelim ki sonradan kazanç bilgisini de manuel değiştirdi, o zaman tüm alanlar dolu olacağı için kodumuz neye göre hesaplama yapacağını bilmez ve kullancıya "Lütfen hangi alanın yeniden hesaplanmasını istiyorsanız onu silin" mesajını yine animasyonlu bi şekilde gösterir.

Çalışma mantığı ise şöyledir:

Sayfada belli name'ler tanımlanmış durumda. Makronun tetiklenmesi için "alan" isimli namede bir hücrenin değişmesi beklenmekte. Tabi değişklikler sonucunda başka tetkilenme olmasın diye eventler geçici olarak baskılanmakta. Değişlik sonucunda alan isimli name'de boş hücre sayısının 1 olup olmadığına bakılmaktadır([alan].Cells.SpecialCells(xlCellTypeBlanks).Count = 1 kodu ile). Böylece bu boş olana uygun formül yazılmakta ve sonuç copy-paste yapılmaktadır.

Alan isimli namede 2 hücre doluyken 3.sünün doldurulması durumunda da, 4 hücre doluyken birinin silinmesi durumunda kontrol sonucu 1 dönecek ve esas işi yapan kod bloğu çalışmış olacaktır.

Data Validation ve Yetki kontrolü

En kısa sürede eklenecek

Seçimlere göre veritabanından sonuç getirmek

Bu işlem veritabanı kodlama bilgisi de gerektirdiği için buraya konulmuştur.

YORUMLAR