Bu siteye giriş yaparak Çerez kullanımını kabul etmiş oluyorsunuz. İşbu sitede; çerez olarak, sadece son giriş tarihiniz ve eğer üye olursanız oturum statünüz tutulacaktır. Bunlar dışında başka hiçbir bilgi tutulmamaktadır. Çerezler için detaylı bilgi için buraya tıklayınız.
ANLADIM

DUYURULAR

Yeni eklenen ve/veya güncellenen sayfaları görmek için buraya tıklayınız.

Güncel ödev ve test listesini görmek için buraya tıklayınız.

Sitede yapılan iyileştirmeAer ve hata düzeltmelerine ait tüm bilgilendirmeleri görmek içinburaya tıklayınız.

Baş
Udemy
Konular
Son
Konular
PivotTableChartveSlicernesneleri
Ileriseviyekonular
VBAMakro
VBAMakro İleri seviye konular 4

PivotTable, Slicer ve Chart nesneleriyle çalışmak

Pivot Tablolar

Giriş

Pivot(Özet) tablolarla çalışmaya başlamadan önce Excel tarafında konuya hakim olmak önemlidir. Buraya kadar geldiğinize göre konuyu bildiğinizi varsayıyorum ancak bilgilerinizi tazlemek isterseniz sizi şöyle alayım.

Baştan belirtmekte fayda var; Pivot tablo(bundan sonra PT diye anılacak) konusu VBA tarafında biraz alengirli bir hal alır, zira Excel önyüzde görmediğimiz bir nesneyle çalışmamız gerekmektedir. Bunun adı PivotCachedir. Onun dışında aslında recorder ile üreteceğiniz birkaç kodu elden geçirmekle kolaylıkla öğrenebileceğiniz bir konudur. O yüzden ben bu sayfada gereksiz yere recorderla elde edebileceğiniz detayları koymamaya karar verdim. Onun yerine refresh işlemleri, birkaç önemli işlem, birkaç event ve sonrasında da slicerlarla olan ilişkisini anlatmak istiyorum. Tabiki otomasyon projelerinizde data üretimi sonrasında kodun bir PT da üretmesi gerekiyorsa PT yaratmayı bilmeniz lazım ama dediğim gibi bunu recorderla çok rahat görebilirsiniz, gereksiz fazlalıkları atmak size kalıyor.

Size tavsiyem öncelikle birkaç işlemi(pivot oluşturma, filtre koyma, filtre kaldırma, repeat all items yapma v.s) recorder ile yapın ve kodu inceleyin. Yine de "ben recorderla uğraşamam diyorsanız MSDN'den detay bilgi edinebilirsiniz. İngilizceniz yoksa ve pivot detaylarını yine de yazmamı isterseniz bu sayfanın altına yorum bırakın, uygun bir zamanda bunları da eklerim, ama dediğim gibi gerçekten burada anlatacaklarım dışında nerdeyse herşeyi recorderla kolaylıkla elde edebilirsiniz ve bunlarla ilgili olarak dikkat çekecek bir husus bulunmamaktadır.

Veri Seti

Üzerinde çalışacağımız data seti, Excel konularındaki örnek data setidir, buradan indirebilirsiniz.

Mesela aşağıda, bu dosyanın "calculatedlar" sayfasındaki dataya uyguladığımız PT'ın recorderla elde edilmiş kodu bulunuyor. Tabi bu kodu şuan çalıştırsanız sizde çalışmaz zira sayfa isimleri değişmiş olacak. Onun yerine kendiniz yeni bir recorder çalıştırıp görün derim.

Sub Macro1()
'
' Macro1 Macro
'

'
    Sheets.Add
    ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, SourceData:= _
        "calculatedlar!R1C1:R175C6", Version:=6).CreatePivotTable TableDestination _
        :="Sheet1!R3C1", TableName:="PivotTable1", DefaultVersion:=6
    Sheets("Sheet1").Select
    Cells(3, 1).Select
    With ActiveSheet.PivotTables("PivotTable1")
        .ColumnGrand = True
        .HasAutoFormat = True
        .DisplayErrorString = False
        .DisplayNullString = True
        .EnableDrilldown = True
        .ErrorString = ""
        .MergeLabels = False
        .NullString = ""
        .PageFieldOrder = 2
        .PageFieldWrapCount = 0
        .PreserveFormatting = True
        .RowGrand = True
        .SaveData = True
        .PrintTitles = False
        .RepeatItemsOnEachPrintedPage = True
        .TotalsAnnotation = False
        .CompactRowIndent = 1
        .InGridDropZones = False
        .DisplayFieldCaptions = True
        .DisplayMemberPropertyTooltips = False
        .DisplayContextTooltips = True
        .ShowDrillIndicators = True
        .PrintDrillIndicators = False
        .AllowMultipleFilters = False
        .SortUsingCustomLists = True
        .FieldListSortAscending = False
        .ShowValuesRow = False
        .CalculatedMembersInFilters = False
        .RowAxisLayout xlCompactRow
    End With
    With ActiveSheet.PivotTables("PivotTable1").PivotCache
        .RefreshOnFileOpen = False
        .MissingItemsLimit = xlMissingItemsDefault
    End With
    ActiveSheet.PivotTables("PivotTable1").RepeatAllLabels xlRepeatLabels
    With ActiveSheet.PivotTables("PivotTable1").PivotFields("KANAL")
        .Orientation = xlRowField
        .Position = 1
    End With
    With ActiveSheet.PivotTables("PivotTable1").PivotFields("URUN")
        .Orientation = xlColumnField
        .Position = 1
    End With
    ActiveSheet.PivotTables("PivotTable1").AddDataField ActiveSheet.PivotTables( _
        "PivotTable1").PivotFields("TUTAR"), "Sum of TUTAR", xlSum
End Sub  

Yukarıda da bahsettiğim gibi recorder kodunda gereksiz birsürü kısım var, zira artık bildiğiniz üzere recorder bir nesneye ait tüm propertylere değer atıyor.

PivotCache nesnesi

Yukarıdaki koddan göreceğiniz üzere bir pivot table yaratılmadan önce PivotCache(Bundan sorna PC olarak anılacak) nesnesi yaratılıyor. PC, veri kaynağının bellekte özetlenmiş halidir. Her PT'ın arka planda dayandığı bir PC vardır. Neden böyle bir şeye ihtiyaç var diye sorabilirsiniz. PT'lar bence ve birçok kişi tarafından Excel'in en güçlü aracı olarak kabul edilir. Bunların gücü, birçok gruplama işini çok hızlı yapmasında yatıyor. İşte bu hız, bellekteki PC tarafından sağlanır, aksi halde her defasında veri kaynağına gitmek gerekecekti ki bu da hızın düşüşü anlamına gelecekti. İşte bu yüzden öncelikle PC yaratılır.

Excel 2007'den önce bir veri kaynağından üretilen her PT'ın ayrı bir PC'si olurdu, bu da bellek tüketimi için çok iyi olmazdı ve dosya boyutunu da gereksiz yere şişirirdi. Excel 2007 ile birlikte aynı kaynaktan üretilen PT'ların PC'si ortaklaştırıldı. Bununla beraber bazı durumlarda bunların PC'sini ayrıştrımak gerekebilir, bunu nasıl yapacağımızı aşağıda göreceğiz.

Rakamlarla örnek vermek gerekirse; 2007 öncesinde kaynak datanız 1 MB büyüklüğündeyse ve 3 PT yarattıysanız dosya boyutu 3 MB daha artıyordu. 2007 ve sonrasında sadece 1 MB artıyor. Bu 1 MB'dan da tasarruf etmek için "save source data" yapabilrsiniz.

Aşağıda benin üzerinde çalıştığım birkaç dosyanın boyutunu görebilirsiniz(Edit:Dosya adlarındaki baştaki "pivot"u görmezden gelin):

pivot size

Daha detaylı analiz ettiğimizde;

  • Boş bir dosya 9 KB
  • Sadece kaynak datayı içerdiğinde 57 KB. Demekki datanın getirdiği net yük: 57-9=48 KB
  • Bu dosyada tek bir PT yaratınca boyut 79'a çıkıyor. Demekki, cache + pivot sayfasındaki özet data=79-48=31 KB(Gerçi özet alanında hiç veri koymasam da boyut yine 79, demek özet veri ihmal edilebilir düzeyde)
  • PT sayısını 2ye çıkarınca boyut 3 KB daha artıp 82 oluyor. İki PT da aynı cacheyi kullandığına göre, bu 3 KB'lık artış sadece ikinci PT'ın özet verisinden geliyor
  • İki pivotun cachesini birbirinden ayırırsak boyut 99'a çıkıyor. Demekki ikinci cache katkısı 99-82=17 KB
  • Cacheleri tekrar ortaklaştırıp ancak kaynak datayı dosya ile birlikte kaydetmezsem boyut 66 KB'a düşüyor.

Cacheyi tek mi tutmalı çok mu?

Paylaşımlı PC'ler tabiki hafıza kullanımı açısından ve çoklu PT'ların aynı davranışı göstermesi gereken durumlarda sunduğu verimlilik açısından faydalıdır. Ancak bunun arzu edilmediği durumlar olacaktır:

  • Mesela bir PT refreshlendiğinde diğerleri refreshlenmesin istiyorsunuzdur
  • Bir alan üzerinde gruplama yaptıysanız, bu gruplama tüm PT'larda uygulanacaktır. Halbuki siz bunun sadece birinde olmasını istiyorsunzudur.
  • Keza, bir PT'a calculated item/field koyduğuzuzda tümünde görünecektir. Bunu istemiyorsunuzdur.

İşte bu sebeplerden dolayı cacheleri ayırmayı bilmek önemlidir. Bunu az sonra göreceğiz. Önce PT ve PC'lere biraz daha yakından bakalım.

PC ve PT'larla çalışmak

PC'ler Workbook nesnesinin bir collection'ı iken, PT'lar Worksheet'indir. Yani,  toplam PC sayısını Activeworkbook.PivotCaches.Count diye alabilirken toplam PT sayısını bu şekilde alamayız, her bir sayfada dolaşıp toplam aldırmamız gerekiyor.

Sub pivot_adetler1()
T = 0
Debug.Print "Toplam pivot cache sayısı:" & ActiveWorkbook.PivotCaches.Count

For Each ws In ActiveWorkbook.Worksheets
    T = T + ws.PivotTables.Count
Next ws

Debug.Print "toplam pivottable sayısı:" & T
End Sub

Tabi istersek belli kriterleri sağlayan PT adedini de saydırabiliriz. Mesela adında "Piv" ifadesi geçenleri saydıralım.

Sub pivot_adetler2()
T = 0
For Each ws In ActiveWorkbook.Worksheets
    For Each pt In ws.PivotTables
        If InStr(1, pt.Name, "Piv") > 0 Then
            T = T + 1
        End If
    Next pt
Next ws
Debug.Print "toplam pivottable sayısı:" & T
End Sub   

PC'ler otomatik üretilen nesneler olduğu için bunlarda isim(name propertysi) olmaz, sadece indeks olur. Ancak PT'ların ismi olabilir.
PT'lara doğrudan isim veya indeksiyle ulaşılabileceği gibi pivotun içindeki bir hücreyi referans gösterip onun PivotTable propertysi ile de ulaşabilirsiniz.

Tüm PT'lara hangi PC'den beslendikleri ve hangi sayfada olduklarıyla beraber ulaşmak için şu kodu yazabiliriz.

Sub pivot_ve_cacheleri_gör()
    For Each ws In ActiveWorkbook.Worksheets
        For Each pt In ws.PivotTables
            Debug.Print pt.Name, pt.CacheIndex, pt.Parent.Name
        Next pt
    Next ws
End Sub  

Tabi böyle bir kodu çalıştırmanın bir epsirisi yok. Bu haliyle olsa olsa test/kontrol yapmakta kullanılır. Onun yerine ihtiyacınıza göre modifiye edip öyle çalıştırırsınız. Ör: Adında "Mevduat" geçen PT'ları refreshlemek gibi.

Bazen de özellikle test/kontrol amacıyla üzerinde çalıştığınız pivottableların indekslerini öğrenmek istersiniz. Malesef Pivottablelar için bir Index/Id gibi property yok, o yüzden aşağıdaki çözümle ilerleyebiliriz.

Sub pivotlarınİndeksleriniGör()
    Dim ws As Worksheet
    Set ws = ActiveSheet
    For i = 1 To ws.PivotTables.Count
        Debug.Print i, ws.PivotTables(i).Name
    Next i
End Sub	

NOT: Bu çözüm sadece bir oturuma ait bir çözüm olabilir. Zira dosyayı sonraki açışlarınızda bu indekslerin değiştiğini görebilirsiniz. O yüzden PT'lara indeks numaralarıyla ulaşmak yerine isimleriyle ulaşmak daha güvenlidir.

Refreshleme

Şunu akılda bulundurmak lazım ki, hem PC'ler hem PT'lar refreshlenbilir ama PC'leri refreshlemek her zaman daha hızlıdır, zira bunlar kaynağa doğrudan bağlıyken PT'lar PC'ler üzerinden ikinci seviyeden bağlıdır. Üstelik bir PC'ye bağlı 3 PT varsa döngüsel olarak tümünü refreshlemek anlamsızdır, zira biri refreshlenince zaten hepsi reflesnir, ancak döngü içinde dönerken mecburen hepsi de refreshlenmek durumunda kalır, ki bu da kodun gereksiz uzun çalışmasına neden olur.

Refresh yöntemleri aşağıdaki gibidir:

1) wb.Refreshall

Workbook_Open içinde yazıyorsak asenkron çalışma konusunsa dikkat edilmelidir. Bu metod ile sadece PT'lar değil tüm connectionlar da refreshlenir. O yüzden bu her zaman tercihiniz olmayabilir.

2) PC.Refresh veya birden çok PC'de döngüsel refresh
ActiveWorkbook.PivotCaches(1).Refresh
'veya
Sub cahceleri_refreshle()
    For Each pc In ActiveWorkbook.PivotCaches
      pc.Refresh
    Next pc
End Sub
3) PT.RefreshTable veya birden çok PT'da döngüsel refresh

PT'larda döngüsel dolaşılarak tek tek refresh edilir.

Sub ptleri_refresh()
    For Each ws In ActiveWorkbook.Worksheets
        For Each pt In ws.PivotTables
            pt.RefreshTable
        Next pt
    Next ws
End Sub        
'veya tek bir PT için: ActiveWorkbook.Worksheets.PivotTable(1).RefreshTable
4)Tüm cacheler loop ile RefreshOnFileOpen = True veya bunları oluştururken manuel işaretleme.

Paylaşımlı(Ortaklaştırılmış) PC'leri ayırmak:

Biraz yukarıda bahsettiğimiz gerekçelerden ötürü bazen PT'ların PC'lerini ayrıştırmak gerekebilir. Bu manuel yapılabileceği gibi VBA ile de yapılabilir.

Genelde bu ihtiyaç, rutin değil de tek seferlik bir ihtiyaçtan doğacağı için Excel ön yüzden yapılması yeterlidir ancak otomasyona bağlanmış bir işle her gün PT oluşturuluyor ve bunların cahcelerinin ayrışması gerekiyorsa bu kod faydalı olacaktır.

Sub CacheAyrıştır()

Set pt = ActiveCell.PivotTable
Set ws = Worksheets.Add

ActiveWorkbook.PivotCaches.Create( _
    SourceType:=xlDatabase, _
    SourceData:=pt.SourceData).CreatePivotTable _
    TableDestination:=ws.Range("a3"), _
    TableName:="GeçiciPT"

pt.CacheIndex = ws.PivotTables(1).CacheIndex
Application.DisplayAlerts = False 'sayfa silerken uyarı çıkmasın diye
ws.Delete
Application.DisplayAlerts = True

End Sub        

Bir şekilde ayrıştırılmış bu cacheleri tekrar birleştirmek için de aşağıdaki kod yazılabilir. Mantık şu:Tüm PT'larda dolaşıyoruz, Temel PT'nın cachesiyle aynı kaynağa sahip olanların cachesini Temel PT'nın cachesine eşitliyoruz.

Sub CacheBirleştir()
    For Each ws In ActiveWorkbook.Worksheets
        For Each pt In ws.PivotTables
            If pt.SourceData = Worksheets(1).PivotTables("TemelPivot").SourceData Then
                pt.CacheIndex = Worksheets(1).PivotTables("TemelPivot").CacheIndex
            End If
        Next pt
    Next ws
End Sub

Nesneler hiyerarşisi

PT ve PC dışında elimizde PivotField(s) ve PivotItem(s) collectionları/nesneleri var. Bunlardan PivotField PT'ın alt nesnesi, PivotItem da PivotField'ın alt nesnesi oluyor.

Yani itemlara ait bir bilgiyi şu sırayla elde etmelyiz. PT.PF.PI. Mesela aşağıdaki kod ile aktif sayfadaki 1 numaralı PT'nin Bölge isimli fieldındaki tüm seçili değerleri alırız.

ActiveSheet.PivotTables(1).PivotFields("Bölge").VisibleItems

PivotField olarak belirttiğimiz alanlar RowField olabileceği ColumnField veya DataField da olabilir. Ancak biz spesifik olarak bunlardan birini almak istiyorsak o zaman bunlara ait propertyleri kullanmamız gerekir.(ColumnFields, RowFields vs.)

ActiveSheet.PivotTables(1).ColumnFields(1).Name

Bir de PivotFilter nesnesi var, adından anlaşılacağı üzere, belirli fieldlara filtre uygulmaya yarıyor. Bununla ilgili örnekleri de aşağıda göreceğiz.

Çeşitli işlemler

Peki biz bu nesnelerle neler yapabiliyoruz, kısaca bi onlara bakalım. Öncelikle şunu belirtmekte fayda var. Recorder ile baktığınızda tıpkı yeni bir PT yaratmada olduğu gibi VBA her bir değer için Seçili/SeçiliDeğil şeklinde bir değer üretir. Biz bunu döngülerle daha pratik yapabileceğiz.

PT'daki bir Field'da filtreli elemanları elde etme

Bir field'da seçili olan elemanları elde etmek VisibleItems özelliği ile oldukça kolaydır. Aşağıda, parametre olarak sayfa, PT adı ve field adını alan bir fonksiyonumuz ve bu fonksiyonu çağıran bir test prosedürümüz bulunuyor. Bu arada fonksiyonumuzun dönüş tipi PivotItems olup döngüsel olarak içinde dönerek elemanları yazdırıyoruz.(Dönüş tipini collection olarak da belirleyebilirdik, ancak bu sefer elemanları tek tek geçici bir Collection nesnesine atmak gerekirdi. Bu örneği Slicerdaki seçili elemenları getirme örneğinde yapacağız.)

Function Pivotun1FieldındakileriGetir(ws As Worksheet, ptName As String, ptfield As String) As PivotItems
'tek bir field için basit örnek
    Dim pt As PivotTable
    Dim pf As PivotField
    
    Set pt = ws.PivotTables(ptName)
    Set pf = pt.PivotFields(ptfield)
    Set Pivotun1FieldındakileriGetir = pf.VisibleItems

End Function
    Sub test_pivot_seçili_elemanlar()
    'Örnek olarak, Tarihsl Datanın Pivot ve Slcr sayfasındayken çalıştırabilirsiniz. Pivotta bikaç bölge filtreli olsun
    Dim kimlerseçili As PivotItems
    
    Set kimlerseçili = Pivotun1FieldındakileriGetir(ActiveSheet, "ptBolgekodUrun", "Bölge Kodu")
    For Each vi In kimlerseçili
        Debug.Print vi.Name
    Next vi
    End Sub

PT'daki birden fazla fieldda seçili olan elemanları elde etme

Bu sefer, hangi fieldda hangi değerlere filtre konmuş, bunları elde edeceğiz. Tabi şimdi işimiz biraz daha zor, o yüzden farklı bir bakış açısıyla yaklaşmak lazım. Aslında bu paragrafın ilk cümlesi bize bir dictioanry'nin gerekliliğini gösteriyor: Hangi fieldda ne seçili, yani bildiğimz Key ve Value ikilisi. O yüzden fonksiyonumuzun dönüş tipini bu sefer Dictionary olarak tanımlayacağız.

Tekil field örneğinden farklı olarak bu sefer Field isimlerini bir collection'a atayıp gönderiyoruz. Fonksiyonumuzdan gelen bilgileri yazdırırken de "birleştir" isimli  yardımcı bir fonksiyon ile her fielddaki itemları yanyanya birleştirip öyle yazdırıyoruz.

Function PivotunÇokluFieldındakileriGetir(ws As Worksheet, ptName As String, ptfields As Collection) As Dictionary
    Dim pt As PivotTable
    Dim pf As PivotField
    Dim dict As New Dictionary
    
    Set pt = ws.PivotTables(ptName)
    For Each p In ptfields
        Set pf = pt.PivotFields(p)
        dict.Add p, pf.VisibleItems
    Next p
    
    Set PivotunÇokluFieldındakileriGetir = dict
End Function
    Sub test_pivot_seçili_elemanlar_çoklufield()
    'Örnek olarak, Tarihsl Datanın Pivot ve Slcr sayfasındayken çalıştırabilirsiniz. Pivotta bikaç bölge ve ürün filtreli olsun
        Dim mydict As Dictionary
        Dim alanlar As New Collection
        alanlar.Add "Bölge Kodu"
        alanlar.Add "Ürün"
        
        Set mydict = PivotunÇokluFieldındakileriGetir(ActiveSheet, "ptBolgekodUrun", alanlar)
        For Each k In mydict.Keys
            Debug.Print k, birleştir(mydict(k))
        Next k
    End Sub
    Function birleştir(pis As PivotItems)
        For Each pi In pis
            geçici = geçici & "-" & pi
        Next pi
        birleştir = Mid(geçici, 2)
    End Function

Aşağıdaki örnek üzerinden çalıştırdığımızda dönen sonuç da şöyle olmaktadır.

Bölge Kodu Başkent 1-Ege 2
Ürün ÜÜrün Ürün4-Ürün3-Ürün2

pivotrönek

Bir fieldda filtre var mı kontrolü

Burda da AllItemsVisible şeklinde boolean tipli bir property ile sorgulamamızı yapabilyoruz.

Function pivot_field_filtrelimi(ws As Worksheet, ptName As String, ptfield As String) As Boolean
    pivot_field_filtrelimi = Not ws.PivotTables(ptName).PivotFields(ptfield).AllItemsVisible
End Function
    Sub test_pivot_field_filtrelimi()
        'data sayfasında çalışıtırın
        Debug.Print pivot_field_filtrelimi(ActiveSheet, "PivotTable1", "Bölge")
        Debug.Print pivot_field_filtrelimi(ActiveSheet, "PivotTable1", "Şube Adı")
    End Sub	

Bir fieldda 1 adet filtre kriteri uygulamak

Tek bir field üzerinde 1 adet filtre uygulamanın en pratik yolu PivotFilters nesnesini kullanmaktır. Syntaxı aşağıdaki örnekten görüldüğü üzere oldukça pratiktir. Type argümanının alacağı değerlerin intellisense ile çıkmasını isterseniz PivotFilters nesnesini en başta Dim ile tanımlayıp öyle ilerleyebilirsiniz. Aşağıdaki örnekte spesifik bir tarih ile iki tarih arası değer uygulama örneği bulunmaktadır. Bunların alabileceği değerlerin bir listesin şu safyadan görebileceğiniz gibi, makro recorder ile denemeler sırasında da elde edebilirsinz.

Sub pivotta_filtrekoy()
'Tek fieldfda tek değer filtreleme
    Sheets("Tarihsl Datanın Pivot ve Slcr").Select
    Application.EnableEvents = False 'ilgili sayfadaki eventler tetiklenmesin
    ActiveSheet.PivotTables("ptAy").ClearAllFilters

    ActiveSheet.PivotTables("ptAy").PivotFields("Ay").PivotFilters.Add2 _
        Type:=xlSpecificDate, Value1:="31.01.2016"
    ActiveSheet.PivotTables("ptAy").ClearAllFilters
    ActiveSheet.PivotTables("ptAy").PivotFields("Ay").PivotFilters.Add2 _
        Type:=xlDateBetween, Value1:="31.01.2016", Value2:="31.05.2016"
    Application.EnableEvents = True
End Sub

Diğer PivotFilter örnekleri aşağıdaki gibi olabilir.

ActiveCell.PivotField.PivotFilters.Add FilterType := xlThisWeek 
ActiveCell.PivotField.PivotFilters.Add FilterType := xlTopCount 
	DataField := MyPivotField2 Value1 := 10 
ActiveCell.PivotField.PivotFilters.Add FilterType := 
	xlCaptionIsNotBetween Value1 := "A" Value2 := "G" 
ActiveCell.PivotField.PivotFilters.Add FilterType := 
	xlValueIsGreaterThanOrEqualTo DataField := MyPivotField2 Value1 := 10000 

1 fieldda 1 filtre uygulamanın alternatifi

1 fielda sadece 1 adet kriter gireceksek PivotFilter gayet pratik ve yeterlidir. Bununla birlikte bunun başka bir yolu daha var, bu tekniği öğrenerek daha kompleks filtreleri uygularken rahat ederiz.

Bu yöntemde, PT'ın ilgili fieldındaki tüm elemanlarda dolaşıp istediğimiz eleman dışındakilerin hepsinin Visible özelliğine False atıyorum. Bunu yaparken eleman formatını da opsiyonel olarak gönderdiğime dikkat edin. Zira tarihsel bir alan Amerikan formatı olan ay/gün/yıl(m/dd/yyyy) formatında olduğu için biz tarihsel alanlara yerel formatımız olana gün.ay.yıl(dd.mm.yyyy) gönderiyoruz. Metinsel ve sayısal alanlar için bu parametreyi göndermemize gerek bulunmuyor.

Sub Pivotun1Fieldında1ElemanFiltrele(ws As Worksheet, ptName As String, ptfield As String, eleman As Variant, Optional elemanFormat)
    Dim item As PivotItem
    Application.EnableEvents = False 'ilgili sayfadaki eventler tetiklenmesin
    
    ws.PivotTables(ptName).PivotFields(ptfield).ClearAllFilters 'Önce tüm filtreleri kaldıralım ki, hiç elamanın seçili olmaadığı mantıksız bi durum oluşmasın
    For Each pi In ws.PivotTables(ptName).PivotFields(ptfield).PivotItems
        deger = IIf(IsMissing(elemanFormat), pi, Format(pi, elemanFormat))
        If deger <> eleman Then
            pi.Visible = False
        End If
    Next pi
    
    Application.EnableEvents = True
End Sub
    Sub test_pivotta_tek_eleman_seç()
    'Tarihsl Datanın Pivot ve Slicerı sayfasındayken çalıştırın
        Pivotun1Fieldında1ElemanFiltrele ActiveSheet, "ptBolgekodUrun", "Bölge Kodu", "Ege 1"
        Pivotun1Fieldında1ElemanFiltrele ActiveSheet, "ptAy", "Ay", "31.03.2016", "dd.mm.yyyy"
    End Sub

Birden çok fieldda filtre uygulama

Bi önceki örneğimizde bir fielda bir adet filtre uygulamanın PivotFilter nesnesi kullanımına bir alternatifini görmüş ve bunun daha kompleks uygulamalara baz teşkil edeceğini söylemiştik.

İşte bu örneğimizde benzer yaklaşımı kullanarak birden çok kolonda bir eleman veya bir kolonda çok eleman veya çok kolonda çok eleman seçimlerine bakacağız.

Tabi önceki örneğe ana yaklaşım olarak benzemekle birlikte parametrelerimizde değişklikler olacaktır. Mesela elemanları fieldıyla birlikte tutabilmek için Dictionary kullandım.

Test prosedüründe de her bir alandaki değerleri bir Collection'da tutmak yerine yine Dictionary kullandım. Dictionary'leri çok sevdiğimden mi bu kadar çok dictionary kullanıyorum? Sevdiğim doğrudur, ama neden collection kullanmadığımı siz bi düşünün, hatta deneyin ve neden olamdığını kendiniz görün. Cevabı bulamazsanız aşağıdaki kod bloğunun bi altına bakın.

Test prosedüründe kriterler dictionarysini sıfırladığımız satıra dikkat. Burada bi önceki elemanlarla karışmasın diye içeriği boşaltmak lazım ama bunu RemoveAll diyerek yapmamalıyız, zira dictionary'yi komple baştan yaratmamız gerekiyor. İsterseniz bu satırda removeall yazıp ne olduğunu kendiniz kontrol edin.

NOT: Bu örnekte eleman formatı kontrol etmedim. Bunu ödev olarak size bırakıyorum. (Çözemezseniz, Slicer'lara bakabilirsiniz, orda çok eleman seçme işinde kullandığım yöntem kullanılabilir)

Sub PivotunÇokFieldındaÇokElemanFiltrele(ws As Worksheet, ptName As String, pFieldveItems As Dictionary)
    Dim pi As PivotItem
    
    Application.EnableEvents = False
    ws.PivotTables(ptName).ClearAllFilters
    For Each pf In pFieldveItems
        For Each pi In ws.PivotTables(ptName).PivotFields(pf).PivotItems
            If Not pFieldveItems(pf).Exists(pi.Name) Then
                    pi.Visible = False
            End If
        Next pi
    Next pf
    Application.EnableEvents = True
End Sub
    Sub test_PivotunÇokFieldındaÇokElemanFiltrele()
        Dim kriterler As New Scripting.Dictionary
        Dim dict As New Scripting.Dictionary
        
        kriterler.Add "Başkent 1", 100
        kriterler.Add "Ege 2", 200
        dict.Add "Bölge Kodu", kriterler
        
        Set kriterler = New Dictionary 'sıfırlıyoruz ki ürünlerin içine bölgeler karışmasın. ama dikkat removeall değil, new dict diyoruz
        
        kriterler.Add "Ürün4", 300
        kriterler.Add "Ürün2", 400
        kriterler.Add "Ürün3", 500
        dict.Add "Ürün", kriterler
        
        PivotunÇokFieldındaÇokElemanFiltrele ActiveSheet, "ptBolgekodUrun", dict
    End Sub

Neden Dictionary: Collection kullanıp elemanlarda tek tek dolaşınca az önce işaretlemediğiniz bir elemanı sonraki loopta işaretleyebiliyorsunuz, bu da işimizi bozuyor. Halbuki bize tek tek dolaşarak kontrol yerine tek seferde kontrol etme özelliği lazım, bunu da Dictionary'nin Exists metodu ile yapabiliyoruz.(NOT:Tabiki Collection da kullanıp collection için custom yazdığımız ColdaVarmı fonksiyonunu da kullanabiliriz ama kodumuzu neden uzatalım, hazır bir metod varken)

Önemli Eventler

PT'larla ilgili olarak iki önemli event var. Event'ler PT'ların, ancak bu eventler devreye girince daha çok diğer nesnelerle iş yapılıyor diye düşünyorum. Ben şahsen bunları daha çok Slicer'larla birlikte kullanıyorum. Mesela Slicer'daki seçim değişkliğini yakalamak için kullanacağız, zira Slicer eventi diye bi event yok, onun yerine PT eventlerinden faydalanacağız.

Bu iki eventimiz şunlardır:

  • Worksheet_PivotTableChangeSync
  • Worksheet_PivotTableUpdate

Bir PT refresh olursa veya uygulanan filtre değişirse tetiklenirler. Refresh olduğunda önce ilki sonra ikincisi tetiklenirken filtre değişiminde ise tam tersi sıra izlenir. Bu sıranın önemli olmadığı caselerde(ki çoğunlukla önemli olmayacak) ilk eventi kullanabilirsiniz. Herkesin önerdiği de budur.

Sıraları görmek için aşağıdaki kodları kullanarak bi PT üzerinde önce bi filtre uygulayın ve sıraya bakın, sonra da refreshteki sıraya bakın.

Private Sub Worksheet_PivotTableChangeSync(ByVal Target As PivotTable)
    Debug.Print "changesync oldu"
End Sub
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable)
    Debug.Print "update oldu"
End Sub

Bu eventlerin örnek bir casede uygulanışına aşağıda Slicerlar bölümünde değineceğiz.

Diğer hususlar

PivotChart

PT yaratırkenki yöntem şöyleydi:

Dim pvt As PivotTable
Dim pvtcch As PivotCache
Set pvt = pvtcch.CreatePivotTable(TableDestination:= )

PivotChart da buna çok benzer,

Dim shp As Shape
Dim pvtcch As PivotCache
Set shp = pvtcch.CreatePivotChart(ChartDestination:= )

Repeat Label yapılmamış bir Pivot Table

Diyelim ki elinizde bir PT'ın copy-paste yapılmış hali var ve malesef repeat label işlemi yapılmamış. Bizim bunu repeat label yapılmış hale getirmemiz lazım. Yani aşağıdaki görüntüde soldaki gibi olan listeyi sağdaki gibi yapmamız gerekiyor.

pivot oto doldur

Bunun için yıllar önce bir kod hazırlamıştım, tabi Excel 2010la birlikte Repeat Label özelliiği gelince bu çöpe gitti gibi oldu. Bununla birlikte size dışarıdan copy-paste halde yani pivot fonksiyonalitesinden kurtulmuş şekilde gelen listeleri dönüştürmek için hala kullanılabilir.

Kodda, işlem yapılacak kolonlar soruluyor. Bu örnek için repeat label yapılacak olan kolonlar A ve B olduğu için A:B seçimini yaparız.

Sub pivot_convert()
Dim kolonlar As Range
Dim baslangic As Range

Set kolonlar = Application.InputBox("İşlem yapılacak kolonları saeçin", Type:=8)
Set baslangic = Range("a2")

enalt = baslangic.CurrentRegion.Rows.Count

baslangic.Select

k = kolonlar.Columns.Count - 1

Do
    baslangic.Offset(0, k).Select
    Do
        ActiveCell.Offset(1, 0).Select
        If IsEmpty(ActiveCell) Then
            ActiveCell.Value2 = ActiveCell.Offset(-1, 0).Value2
        End If

    Loop Until ActiveCell.Row = enalt
    k = k - 1
Loop Until k < 0

End Sub

Slicerlar

Giriş

Slicerlarda da yine PT'larda olduğu gibi bir Slicercache(SC) ve bu cacheden beslenen Slicerın kendisi vardır. Burada da bi recorder çalıştırıp bakalım. Biz tabi PT'da olduğu gibi Slicer eklemeyi de genelde Excel ön yüzde manuel olarak yapmış olacağız. VBA ile daha ziyade Slicerların elemanlarına erişim, bunların seçimini değiştirme, refreshleme v.s gibi işlemler yapacağız ama yine de nasıl bir kod oluşuyor, ona bi bakalım.

Şimdi ilk olarak Data sayfasındaki Table'a(ListObject) Slicer uygulandığında nasıl bir kod oluştuğuna bakalım. Pivot üzerine uygulanan Slicer ile yine pivot üzerine uygulanan Timeline tipli Slicer kodları örnek dosya içinde bulunmaktadır.

Sub Macro2()'
' Macro2 Table Slicerı
'

'
    ActiveWorkbook.SlicerCaches.Add2(ActiveSheet.ListObjects("Table7"), "Bölge"). _
        Slicers.Add ActiveSheet, , "Bölge", "Bölge", 40.5, 414, 144, 198.75
    ActiveWorkbook.SlicerCaches.Add2(ActiveSheet.ListObjects("Table7"), "Ürün Adı") _
        .Slicers.Add ActiveSheet, , "Ürün Adı", "Ürün Adı", 78, 451.5, 144, 198.75
    ActiveSheet.Shapes.Range(Array("Ürün Adı")).Select
    With ActiveWorkbook.SlicerCaches("Slicer_Bölge")
        .SlicerItems("Başkent 1").Selected = True
        .SlicerItems("Başkent 2").Selected = False
        .SlicerItems("Batı Karadeniz").Selected = False
        .SlicerItems("Doğru Karadeniz").Selected = False
        .SlicerItems("Ege 1").Selected = False
        .SlicerItems("Ege 2").Selected = False
        .SlicerItems("Güney Akdeniz").Selected = False
        .SlicerItems("Güneydoğu Anadolu").Selected = False
        .SlicerItems("İç Anadolu").Selected = False
        .SlicerItems("İstanbul 1").Selected = False
        .SlicerItems("İstanbul 2").Selected = False
        .SlicerItems("İstanbul 3").Selected = False
        .SlicerItems("Marmara").Selected = False
    End With
    ActiveWorkbook.SlicerCaches("Slicer_Bölge").ClearManualFilter
    With ActiveWorkbook.SlicerCaches("Slicer_Bölge")
        .SlicerItems("Başkent 2").Selected = True
        .SlicerItems("Başkent 1").Selected = False
        .SlicerItems("Batı Karadeniz").Selected = False
        .SlicerItems("Doğru Karadeniz").Selected = False
        .SlicerItems("Ege 1").Selected = False
        .SlicerItems("Ege 2").Selected = False
        .SlicerItems("Güney Akdeniz").Selected = False
        .SlicerItems("Güneydoğu Anadolu").Selected = False
        .SlicerItems("İç Anadolu").Selected = False
        .SlicerItems("İstanbul 1").Selected = False
        .SlicerItems("İstanbul 2").Selected = False
        .SlicerItems("İstanbul 3").Selected = False
        .SlicerItems("Marmara").Selected = False
    End With
    With ActiveWorkbook.SlicerCaches("Slicer_Ürün_Adı")
        .SlicerItems("Ürün1").Selected = True
        .SlicerItems("Ürün2").Selected = False
        .SlicerItems("Ürün3").Selected = False
        .SlicerItems("Ürün4").Selected = False
    End With
    With ActiveWorkbook.SlicerCaches("Slicer_Bölge")
        .SlicerItems("Doğru Karadeniz").Selected = True
        .SlicerItems("Başkent 1").Selected = False
        .SlicerItems("Başkent 2").Selected = False
        .SlicerItems("Batı Karadeniz").Selected = False
        .SlicerItems("Ege 1").Selected = False
        .SlicerItems("Ege 2").Selected = False
        .SlicerItems("Güney Akdeniz").Selected = False
        .SlicerItems("Güneydoğu Anadolu").Selected = False
        .SlicerItems("İç Anadolu").Selected = False
        .SlicerItems("İstanbul 1").Selected = False
        .SlicerItems("İstanbul 2").Selected = False
        .SlicerItems("İstanbul 3").Selected = False
        .SlicerItems("Marmara").Selected = False
    End With
    With ActiveWorkbook.SlicerCaches("Slicer_Bölge")
        .SlicerItems("Başkent 2").Selected = True
        .SlicerItems("Doğru Karadeniz").Selected = True
        .SlicerItems("Başkent 1").Selected = False
        .SlicerItems("Batı Karadeniz").Selected = False
        .SlicerItems("Ege 1").Selected = False
        .SlicerItems("Ege 2").Selected = False
        .SlicerItems("Güney Akdeniz").Selected = False
        .SlicerItems("Güneydoğu Anadolu").Selected = False
        .SlicerItems("İç Anadolu").Selected = False
        .SlicerItems("İstanbul 1").Selected = False
        .SlicerItems("İstanbul 2").Selected = False
        .SlicerItems("İstanbul 3").Selected = False
        .SlicerItems("Marmara").Selected = False
    End With


    ActiveSheet.Shapes.Range(Array("Bölge")).Select
    ActiveWorkbook.SlicerCaches("Slicer_Bölge").Slicers("Bölge").Name = "BölgeSlc"
    With ActiveWorkbook.SlicerCaches("Slicer_Bölge").Slicers("BölgeSlc")
        .Caption = "Bölge"
        .DisplayHeader = True
        .SlicerCache.CrossFilterType = xlSlicerCrossFilterShowItemsWithDataAtTop
        .SlicerCache.SortItems = xlSlicerSortAscending
        .SlicerCache.SortUsingCustomLists = True
    End With
End Sub        
          

Görüldüğü üzere sadece bir iki adet işaretleme yapsam bile Slicerdaki diğer seçenekler için de otomatikman False değerini üretti. Manuel yazarken biz sadece istediklerimizi True yazarız, diğerleri mevcut değerleriyle kalır.

Mantıken, bir Slicerda belirli bir anda en az bir eleman seçili olmalıdır. Bu eleman seçiliyken başka bir elamanı da seçmek istersek, sadece onu seçen kodu yazmak yeterli. Ör: Slicerda A,B,C,D,E değerleri var olsun. Şuan sadece A seçiliyse, diğerlerinin Selected değeri False'tur. Biz ilaveten B'yi de seçmek istersek, B.Selected=True yazmak yeterli, ama sadece B seçili olsun istersek, A'ya da false atamak gerekir. Kendi bölge örneğimiz üzerinden gidecek olursak:

Sub ilave_secim()
    With ActiveWorkbook.SlicerCaches("Slicer_Bölge")
    'cachelere ya indeks ile ya da "formula name" ile ulaşılır, name ile dğeil. Name Slicerın adıdır, cahcenin değil.
        .SlicerItems("Ege 1").Selected = True
    End With
End Sub
Sub tek_secim()
    With ActiveWorkbook.SlicerCaches("Slicer_Bölge")
        .SlicerItems("Ege 1").Selected = True
        .SlicerItems("Doğru Karadeniz").Selected = False
    End With
End Sub

Bu arada farkettiyseniz SCs collection'ına bir indeks verince elde ettiğim tekil bir SC üzerinden doğrudan Sliceritemlara ulaşabiliyoruz, yani arada Slicerı seçmeye gerek yok. Zira bunlarda, PT'lardan farklı olarak bir SC ile bir Slicer eşleşiyor gibi bir durum var, zaten aksi garip olurdu.

Yukarıda recorder kodu olarak bir Table Slicer'ı vermiştik. Pivot Slicerı oluşturunca da pek farklı bi durum yok, sadece parametlerde ufak bir iki değişiklik var. Bundan başka Table'larda olmayıp sadece PT'larda olan Timeline da eklenebiliyor, ki bunlar da bir tür Slicer olarak kabul görüyor.

Çeşitli propertyler

Şimdi küçük bir kod üzerinde birkaç özelliğe bakalım.

NOT: OLAP yani PowerPivot gibi bir kaynaktan besleneme durumunda kullanılabilen özellikler(level, visibleitemlist v.s) bu bölümlerin kapsamı dışındadır.

Az önce Timelineların da bir tür Slicer olduğunu söylemiştik, bunların property olarak sadece SlicerCacheTypeları farklı oluyror; Microsoft dokümantasyonunda bu type değeri için sadece 1 ve 2 değerleri var; 1 Slicer, 2 Timeline oluyor ama ben aşağıdaki sorguyu çalıştırdığımda 0 değerini de görüyorum. Zannımca bu 2013te Slicer özelliği gelen Table'lardan üretilen SC'leri ifade ediyor. Microsoftun bunu hala(bu sayfanın hazırlandığı tarih olan Haziran 2019'da) dokümante etmemiş olması ilginç.

'Global değişkenler
Dim sl As Slicer
Dim slc As SlicerCache
Dim sli As SlicerItem
Dim ws As Worksheet

Sub tüm_slicerlarda_dolaşma()
For Each slc In ActiveWorkbook.SlicerCaches
    For Each sl In slc.Slicers
        Debug.Print slc.SourceName, slc.Index, slc.SlicerCacheType, sl.Name, sl.Parent.Name, sl.Caption, slc.Name
    Next sl
Next slc
End Sub  

slicer cache type

Çeşitli İşlemler

Seçili olan tek bir elemanı elde etme

Bu örnekte aktif olarak seçili olan elemanı elde edeceğiz.

Sub seçili_eleman()
    'focuslu ise çalışır, aksi halde hata verir
    Debug.Print ActiveWorkbook.SlicerCaches("Slicer_Bölge_Kodu").Slicers(1).ActiveItem.Name
End Sub	

Seçili olan birçok elemanı elde etme

Şimdi de birden çok eleman varsa onları yazdıralım. Tabi burada örnekleri basit tutmak adına elemanları sadece yazdırıyoruz, ihtiyaca göre elemanlar bir collectiona, formdaki listboxa v.s atanabilir.

Sub seçili_elemanlar()
    For Each sI In ActiveWorkbook.SlicerCaches("Slicer_Bölge_Kodu").VisibleSlicerItems
        Debug.Print sI.Name
    Next sI
End Sub

Bu işlem için birçok forumda aşağıdaki koda benzer bir kod sunuluyor. Biz PT kısmında seçili elemanları elde ederken farklı bir yöntem izlemiştik, orda dönüş tipini PivotItems olarak belirlemiştik ve demiştikki Slicerda Collection yöntemini kullanacağız. Pek tabiki burada da dönüş tipini SlicerItems yapabiliriz. Ancak biz geleneğe uyalım ve Collection yöntemini kullanalım, hemen altında da SlicerItems yöntemini deneriz.

Public Function SlicerdaSeçilileriGetir(SlicerName As String) As Collection
    Dim geçici As New Collection
    Set slc = ActiveWorkbook.SlicerCaches(SlicerName)

    For Each sli In slc.SlicerItems
        If sli.Selected Then
            geçici.Add sli.Name
        End If
    Next
    Set SlicerdaSeçilileriGetir = geçici
End Function
    Sub test_SlicerdaSeçilileriGetir()
        Dim seçililer As Collection
        Set seçililer = SlicerdaSeçilileriGetir("Slicer_Bölge_Kodu")
        For Each s In seçililer
            Debug.Print s
        Next s
    End Sub

Bu örnekte tekil ve çoklu eleman döndürme işlemini tek bir prosedürle yapmış olduk. Çoklu eleman seçiminde döngüsel olarak dolaşmayı yukarıdaki kodla görmüş olduk. Tek eleman seçiminde ise collection'ın ilk elemanına ulaşmak yeterlidir.

SlicerdaSeçilileriGetir("Slicer_Bölge_Kodu")(1)

SlicerItems dönüş tipli yönteme göre ise kodumuz daha kısa. Neden bu değil de Collection'lı kullanılıyor gerçekten bilmiyorum, Timer ile performans kontrolü de yaptım, ikisi de aynı sürede çalışıyor gibi görünüyor. Her neyse siz ikisini de bilin.

Public Function SlicerdaSeçilileriGetirSlicerItemslıYöntem(SlicerName As String) As SlicerItems
    Set SlicerdaSeçilileriGetirSlicerItemslıYöntem = ActiveWorkbook.SlicerCaches(SlicerName).VisibleSlicerItems
End Function
    Sub test_SlicerdaSeçilileriGetirSlicerItemslıYöntem()
        Dim kimlerseçili As SlicerItems
    
        Set kimlerseçili = SlicerdaSeçilileriGetirSlicerItemslıYöntem("Slicer_Bölge_Kodu")
        For Each vi In kimlerseçili
            Debug.Print vi.Name 
        Next vi
    End Sub

Tek bir elemanı seçme

Bu yöntemde PT'larda yaptığımız gibi elemanlar üzerinde tek tek dolaşarak ilerleyeceğiz. Parametrelerden son parametreye dikkat, orada elemanların formatı farklı olabilir diye bunu da belirtme seçeceği veriyorum. Eğer bu parametre prosedüre geçirilmezse eleman aynen alınırken, geçirilme durumunda format fonksiyonu ile şekillendirilip öyle seçilir.

Sub slicerda_tek_eleman_seç(cacheName As String, eleman As Variant, Optional elemanFormat)
    Dim item As SlicerItem
    
    Application.EnableEvents = False 'ilgili sayfadaki eventler tetiklenmesin
    ActiveWorkbook.SlicerCaches(cacheName).ClearManualFilter  'Önce tüm filtreleri kaldıralım ki, hiç elamanın seçili olmaadığı mantıksız bi durum oluşmasın
    For Each item In ActiveWorkbook.SlicerCaches(cacheName).SlicerItems
        'pivot örneğinde pivotitem'ı formatlıyorduk, burada filtre değerini
        If item.Name <> IIf(IsMissing(elemanFormat), eleman, Format(eleman, elemanFormat)) Then
            item.Selected = False
        End If
    Next item
    Application.EnableEvents = True
End Sub
    Sub test_slicerda_tek_eleman_seç()
        slicerda_tek_eleman_seç "Slicer_Ürün", "Ürün3"
        slicerda_tek_eleman_seç "Slicer_Ay", "29.02.2016", "Long Date"
    End Sub

Çok eleman seçme

Bu örnekte ise birden çok elemanı seçiyoruz. Birden çok değerin varlığını sorgulamak durumunda olduğum için Dictionary kullanıyorum.

Sub slicerda_çok_eleman_seç(cacheName As String, elemanlar As Dictionary, Optional elemanFormat)
    Dim item As SlicerItem
    
    Application.EnableEvents = False
    If Not IsMissing(elemanFormat) Then
        For Each e In elemanlar.Keys
            elemanlar.Key(e) = Format(e, elemanFormat)
        Next e
    End If
    
    ActiveWorkbook.SlicerCaches(cacheName).ClearManualFilter  'Önce tüm filtreleri kaldıralım ki, hiç elamanın seçili olmaadığı mantıksız bi durum oluşmasın
           
    For Each item In ActiveWorkbook.SlicerCaches(cacheName).SlicerItems
        If Not elemanlar.Exists(item.Value) Then
            item.Selected = False
        End If
    Next item
     Application.EnableEvents = True
End Sub
    Sub test_slicerda_çok_eleman_seç()
        Dim dict As New Scripting.Dictionary
        dict.Add "Ürün2", 1
        dict.Add "Ürün3", 2
        
        slicerda_çok_eleman_seç "Slicer_Ürün", dict
        
        'olmayan veri de gönderelim
        dict.RemoveAll
        dict.Add "29.02.2016", 1
        dict.Add "25.02.2016", 2
        dict.Add "31.03.2016", 3
        slicerda_çok_eleman_seç "Slicer_Ay", dict, "Long Date"
        
    End Sub	

PT eventlerini kullanarak Slicer değişimlerini yakalamak

Yukarıda PT'lar kısmında PT eventlerini görmüştük. Şimdi bu eventleri Slicerlarla birlikte nasıl kullanırız, ona bakacağız.

PT değişimiyle seçili elemanı elde etmek

İster PT üzerinden manuel, ister kod ile, ister Slicer'ın kendisi aracılığı ile PT üzerinde bir seçim yapmış olalım. Bu seçilen elemanı bir hücrede, bir grafik başlığında veya kod içinde her hangibir yerde değişken olarak kullanmak isteyebiliriz.

"Tarihsel Datanın Pivot ve Slicerı" sayfasındaki PT'lardan ptBolgekodUrun isimli olan PT'da bir değişklik olduğunda F1 hücresine ilgil PT'da seçilen elemanı yazdırıyoruz. Birden çok eleman seçilirse ilki yazılır. Bizim kodumuz, özellikle bir eleman seçiminin beklendiği durumlarda anlamlıdır. Zira muhtemelen bunu küçük bir veri bloğunun başlığı halinde kullanacağızdır, "Ege 1 Bölgesi Kredi Rakamları" gibi.

Private Sub Worksheet_PivotTableChangeSync(ByVal Target As PivotTable)
    Debug.Print "changesync oldu"
    If Target.Name = "ptBolgekodUrun" Then
        [F1].Value = SlicerdaSeçilileriGetir("Slicer_Bölge_Kodu")(1) & " Bölgesi"
    End If
End Sub
Dashboard Yönetimi

PT kısmında birden fazla PT aynı PC'yi kullanıyorsa bunlardan birinde yapılan seçimi diğerlerini de otomatikman etkiler demiştik.

Peki ya elimizde PC'leri farklı olan PT'lar varsa ve biz birinde yapılan seçim  hepsinde geçerli olsun istiyorsak?

Mesela aşağıdaki farklı renkle gösterilen bloklardan oluşan bir dashboardda farklı kaynaklardan gelen PT'lar bulunuyor olsun. Bunların her biri için Slicer yapmaya gerek yok. Biz biri için bir Slicer yaparız, bunda seçilen elemanı bi değişkende depolarız(yukarıda gördüğümüz yöntemlerle) ve diğer PT'larda da bu elemanı seçtiririz(yine yukarıdaki yöntemlerle). İkinci kısmı yaptırabilmek için bir haber almamız, yani bir eventin tetiklenmesi lazım, acaba hangi event; tabiki Worksheet_PivotTableChangeSync eventi.

dashboarslicer

Biz örneğimizi basit tutmak adına sadece iki PT kullacağız. Soldakinin adı ptSol, sağdakinin adı ptSağ. Slicer'ımız soldakine aittir.

dashbaoard2

Buna göre kodumuz aşağıdaki gibidir.

Private Sub Worksheet_PivotTableChangeSync(ByVal Target As PivotTable)
If Target.Name = "ptLeft" Then
    If Not pivot_field_filtrelimi(ActiveSheet, "ptLeft", "Bölge") Then
        secilen = SlicerdaSeçilileriGetir("Slicer_Bölge2")(1)
        Pivotun1Fieldında1ElemanFiltrele ActiveSheet, "ptRight", "Bölge Kodu", secilen
    Else
        ActiveSheet.PivotTables("ptRight").ClearAllFilters
    End If
End If
End Sub	 

Dikkat ettiyseniz ilk olarak eventin soldaki PT için tetiklenip tetiklenmediğine bakıyoruz. Sağdaki için manuel bir filtre yapılırsa kod ilk If bloğundan sonra çıkar. Bir diğer kontrolümüz de değişklik sonucunda PT'da filtre var mı yok mu kontrolü, varsa esas kodlarımızı işletiyoruz, filtre yoksa sağdaki için de ClearAllFilters yapıyoruz.

Aşağıda seçim sonrası tabloların genel durumu görünüyor.

dashboard seçim sonrası

Table eventlerini kullanmak

Sadece PT eventi değil bir Table eventini kullanarak da Slicerlarla işlem yapabiliriz. Mesela aşağıdaki kod ile, Tarihsel Data sayfasında varsayalım ki bir veritabanı bağlantısı var ve bu tablo belli zamanlarda(dosya ilk açıldığında, sayfa aktive olduğunda v.s) refresh oluyor olsun. Refresh sonucunda mevcut tarihten daha büyük bi tarih gelirse, Slicer da buna göre güncellensin, yani slicerda en büyük tarih seçili olsun.

Private Sub Worksheet_TableUpdate(ByVal Target As TableObject)
    Dim maxTarih As String
    
    maxTarih = Format(WorksheetFunction.Max(Worksheets("Tarihsel Data").Columns(3)), "dd.mm.yyyy")
    Application.EnableEvents = False
    slicerda_tek_eleman_seç "Slicer_Ay", maxTarih, "Long Date"
    Application.EnableEvents = True
End Sub

Kullanıcılara PT Slicerını Table Slicer'ı gibi kullandırtmak

Bazen olur ki, Table'a ait Slicer üzerinde bir değişklik yaptığınızda bunun bir eventi tetiklemesini istersiniz. Ancak Table'ların filtrelenmesine yönelik böyle bir event yoktur. Sadece TableUpdate eventi var, o da tablo güncellendiğinde devreye girer.

Böyle bir durumda Excel'i kandırma yoluna gidebiliriz. İzleyeceğimiz yollardan biri şudur:

  • Bu tabledan bir PT yaratmak
  • Bu PT üzerine bir Slicer koymak
  • PT' bölgesini gizlemek veya aşağı bir bölgeye almak, yeter ki görünmesin
  • Slicer seçildiğinde PT'ın ChangeSync eventinin tetiklenmesini sağlamak
  • PT eventi tetiklenince seçilen değeri alıp bu değeri Table üzerinde filtre olarak uygulamak(Table üzerinde doğrudan veya ona ait Slicerda dolaylı. Slicer kullanacaksak bunu genelde gizlemeyi tercih ederiz, iki slicer karışmasın diye, ama ben bu sayfadakini gizlemeden bıraktım)
  • ve diğer işlerimizi yaptırmak

Buna ait basit bir örneği aşağıda bulabilirsiniz. Kod Data sayfasına aittir.

Private Sub Worksheet_PivotTableChangeSync(ByVal Target As PivotTable)

'Exit Sub 'Event örneklerini yaptığımızda bu satırı commentleyin, event dışındaki örneklerde açık kalsın

Application.EnableEvents = False

If ActiveWorkbook.SlicerCaches("Slicer_Bölge1").VisibleSlicerItems.Count = ActiveWorkbook.SlicerCaches("Slicer_Bölge1").SlicerItems.Count Then 'pivotun slicerı
    ActiveWorkbook.SlicerCaches("Slicer_Bölge").ClearManualFilter 'Tableınki, görünmezdir, veya aşağılarda görünmez olsa da olur
    GoTo çıkış
Else 'sadece tek seçim yapıldığı varsayılmıştır, çoklu seçimlerde işe yaramaz
    secilen = SlicerdaSeçilileriGetir("Slicer_Bölge1")(1)
    ActiveWorkbook.SlicerCaches("Slicer_Bölge").ClearManualFilter 'önce clear
    slicerda_tek_eleman_seç "Slicer_Bölge", secilen
End If
çıkış:
Application.EnableEvents = True
End Sub	

İzleyeceğimzi bir başka yol da Worksheet_Calculate eventinden faydalanmaktır. Her iki alternatifi de göreceğimiz daha anlamlı bir örneği Chart nesnesinden sonraki Örnek Uygulama bölümünde bulabilirsiniz.

Chart(Grafik) Nesnesi

Dashboard yönetimi kısmında makroların gücünü döngüler ve koşullu yapıları kullanarak çok güzel görmüştük. Grafiklerde de benzer kolaylıklardan yararlanacağız. Tabiki PT'larda olduğu gibi grafik yaratmayla pek uğraşmayacağız. Otomasyon projelerinde kodunuzun bir grafik yaratması gerekse bile bunu recorder ile ürettiğiniz kodu elden geçirerek yapabilirsiniz.

Mesela yukarıda Slicerlar bölümünde 6 farklı renkli gösterilmiş dashboardu düşünün, orada o bloklar PT'lardan oluşuyordu, şimdi varsayalım ki bunlar birer grafik. Bu sefer her birininin tipini, boyutunu, pozisyonunu, lejandını, renkleri v.s tek seferde değiştirmek istiyoruz, tabiki bunu döngülerle yapacağız.

Veya grafiğe konu olan datasetinde seçilen bir değere göre gragiği dinamik olarak güncellemek gerekecek. Bu gibi işleri yine makro ile başaracağız.

Nesneler hiyerarşisi

Şimdi öncelikle elimizdeki nesneler bi bakalım

  • Chart nesnesi: Bunlar bağımsız grafik sayfaları olabileceği gibi normal bir sayfa(worksheet) içindeki gömülü grafikler de olabilir. Ancak bunlardan sadece bağımsız sayfa olarak bulunanlar Charts koleksiyonunun bir üyesidir, gömülü olanlar değil.
  • ChartObject nesnesi: Worksheet içinde bulunan grafiklerin(Worksheet Chartlarının) konteynerıdır. Esasen bir grafik nesnesi değil, onun kabuğudur. Propertylerine falan bakıldığında grafikten ziyade Shape nesnesinin özelliklerini görürsünüz. ChartObjects koleksiyonunun bir üyesidir. Aynı zamanda Shapes koleksiyonunun da bir üyesidir. Hatta macro recorder ile grafik oluşturduğunuzda Shapes üzerinden oluşturur.

Yani aslında 3 tür nesnemiz var diyebilir.

  1. Worksheetlerdeki gömülü Chart: Chart. Parent nesnesi ChartObjecttir.
  2. Bağımsız Chart: Bu da Chart'tır. Parent nesnesi Workbook'tur.
  3. Worksheet Chartı için kontyner: ChartObject. Buna ulaşmadan içindeki Charta ulaşılamaz. Parent nesnesi Worksheet'tir.

Örnek birkaç erişim şekli şöyle olabilir:

'Worksheet içindeki bir Chart'a zincirleme olarak
Sheets("Sheet1").ChartObjects("Chart 1").Chart.ChartTitle.Text = "Yeni başlık"
'veya önce konteynerı seçince aktif olan ActiveChart ile
Sheets("Sheet1").ChartObjects("Chart 1").Activate
ActiveChart.ChartTitle.Text = "Yeni başlık"
'bağımsız chart
Sheets("Chart 1").ChartTitle.Text = "Yeni başlık" 'sayfa adından
ActiveWorkbook.Sheets("Chart1").ChartTitle.Text = "Yeni başlık" 'nesne adından

Bu bilgiler ışığında chartlarda dolaşma şu şekilde olur.

Sub chartlarda_dolaşma()

Dim chto As ChartObject
Dim cht As Chart

'sayfa olan chartlarda dolaşalım
For Each cht In ActiveWorkbook.charts
    Debug.Print cht.Name, TypeName(cht), TypeName(cht.Parent)
Next cht

'gömülü chartlarda dolaşalım
For Each ws In ActiveWorkbook.Worksheets
    For Each chto In ws.ChartObjects
        Debug.Print chto.Name, chto.Index, chto.Parent.Name 'Chart'ın adı biraz anlamsız, ChartObject'in adını kullanalım
    Next chto
Next ws

End Sub

Yaratım ve değişiklik

Yaratım ve özelliklerdeki değişiklikler için hep recorder kullanmanızı tavsiye ediyorum arkadaşlar. O yüzden buna ait kodları buraya koymuyorum.

Diğer hususlar

Grafik datasının olduğu bölgeyi grafikle kaplamak

Özellikle dashboard sayfalarında grafik datasının ortalıkta görünmesini istemeyiz. Gizli satır/kolon veya sayfanın aşağılarında bir yerlere koymak bir çözüm olabileceği gibi, data alanı çok küçük/büyük değilse bu bölgeyi grafiğin kendisiyle kapatmak bir çözüm olabilir.

Aşağıdaki kod ile bunu yapabiliriz. Kodda bir yardımcı fonksiyon kullanıyorum. Bu fonksiyonda F8 ile ilerleyerek "dizi" değişkenini incelerseniz adres1 ve adres2de ne yaptığımı anlayacaksınız.

Dashboard sayfanızdaki tüm grafiklerde döngüsel olarak çalışacak şekilde bu kodu modifiye edebilirsiniz.

Sub RangeiGrafikleKapla()

    Dim cht As ChartObject
    Dim grafikAlan As Range

    Set cht = ActiveChart.Parent
    Set grafikAlan = GrafikRange(cht.Chart)

    cht.Left = grafikAlan.Left
    cht.Width = grafikAlan.Width
    cht.Top = grafikAlan.Top
    cht.Height = grafikAlan.Height
End Sub	
'Yardımcı fonksiyonumuz
	Function GrafikRange(cht As Chart) As Range
		dizi = cht.SeriesCollection(1).Formula
		adres1 = Split(Replace(Split(dizi, ",")(1), cht.Parent.Parent.Name & "!", ""), ":")(0)
		adres2 = Split(Replace(Split(dizi, ",")(2), cht.Parent.Parent.Name & "!", ""), ":")(1)
		
		Set GrafikRange = Range(adres1, adres2)	
	End Function

Grafikleri silme ve gizleme

Silme ve gizleme işlemlerini Shapeler aracılığı ile yapabileceğimiz gibi ChartObject'in kendisiyle de yapabiliriz. Burada önemli olan Chart nesnesini değil ChartObject nesnesini silebildiğimizdir.

ActiveChart.Delete 'çalışmaz
ActiveSheet.ChartObjects(1).Delete 'bu 1 nolu chartı siler
ActiveSheet.ChartObjects.Delete 'bu sayfadaki tüm grafikleri siler

Chart sayfalarını ise normal sayfa siler gibi siliyoruz.

Örnek Uygulama

Excel ana menüsünde Grafikleri incelerken XY(Scatter) grafik türlerinde anlatığımız bir örnek vardı. O örneği ve orada yapılanları temel alarak başka bir örnek yapacağız. Zaten o örneğin sonunda "Dinamik Seçimler" adlı alt başlıkta işlerin biraz karmaşıklaştığı ve VBA'a ihtiyaç olduğunu söyleyerek bu sayfaya link koymuştum. Şimdi bu karmaşık örneğimizi yapalım. (Örnek için iki çözüm metodu olacak, o yüzden dosyanın en sonunda iki sayfa göreceksiniz)

Bu seferki örneğimizde bir bankanın bölge müdürlüklerini Müşteri artışı ve Vadeli Mevduat faizini(maliyetini) bir grafikte göstereceğiz. Bunu yaparken de her seçilen bölge için hem x hem y ekseninde bir ortalama çizgimiz olsun istiyoruz. Böylece grafiği 4 parçaya ayırmış oluyoruz. Sol üst taraf en iyi bölge olurken sağ alt taraf en kötü bölgeleri göstermektedir.

vbapivotxyxcatter

Her seçim sonrasında seçilen bölgeyi bir yere yazdırmam lazım ki, o bölgeye ait değerleri bir yerde hesaplayabileyim. Bunun için A1 hücresine seçilen bölgeyi yazdırıyorum. T1-T6 arasında da hesaplanan bilgiler bulunuyor. Bunlar;

  • Bölge Ort Maliyet : Bölgedeki şubelerin ortalama maliyetidir. AVERAGEIF ile hesaplanır.
  • Bölge Ort Müş Artış : Bölgedeki şubelerin ortalama müşteri artışıdır. AVERAGEIF ile hesaplanır.
  • min maliyet(x) :  Bölgede en düşük maliyete sahip şubenin maliyetidir. SMALL-IF mantığındaki bir dizi formülü ile hesaplanır. Dizi formüllerini bilmiyorsanız buraya bakabilirsiniz. MIN yerine SMALL-IF kullanma sebebim şudur: Eğer şubelerden birinin maliyeti aşırı uç noktalardaysa, bunu grafikte göstermeyelim, gerekirse grafiğin print edilmiş/sunuma alınmış haline elle müdahale ederiz şeklindeki bir görüşten dolayıdır.(Evet bu örnek bir gerçek dünya örneğidir bu arada ve benden istenen de aynen buydu) Formüllerin yanındaki 1 rakamları varsayılan değerdir, istenirse bunlar değiştirilebilir. Tabi bu değişikliği Worksheet_Change eventi ile de yakalayacağız. Bölge4, Bölge12 gibi bölgeleri seçerek uç değerlerin grafiğini ne kadar bozduğunu kendini de görebilirsiniz.
  • min müş(y) : Yukarıdakinin müşteri adedi artışı için olanı
  • max maliyet(x) :Yukarıdakilerin LARGE-IF mantıklı hali
  • max müş(y) :Yukarıdakilerin LARGE-IF mantıklı hali

Data, Grafik ve Ön Hazırlık

Veri setimiz oldukça basit: Bölge, Şube, Maliyet ve Müşteri Kazanımı bilgilerinden oluşuyor. Biz buraya bir formülle bir kolon daha ekleyip, grafikte label'ın nasıl görüneceğini belirledik. Formül karışık gelebilir, ancak dikkatli analiz edilirse oldukça basit bir formül olduğu görülecektir. Formülde tek atraksiyonlu kısım yukarı ve aşağı ok işaretlerini UNICHAR fonksiyonu ile bulmak oldu. (Görüntü çok kalabalık olduğu için sonra sadece şube isimlerini göstermeye karar verdim)

Sonrasındaki adımlar şöyleydi:

  • Veri setini aşağılara aldım ki görünmesin
  • Grafiği manuel oluşturdum,
  • grafikayar prosedüründeki ayarları macro recordar ile ayarladım
  • gridleri manuel sildim
  • labelları "low" pozsiyona aldım
  • Sırasıyla iki eksen için de seçiliyken font wieght(ağırlık)ını 1 yapıp rengini kırmızı yaptım
  • Chart objectin arka planı gri ve bevel>round efekti uygulamadım
  • Plot area için de arkaplanı beyaz ve bevel>round yaptım
  • Add data label>more options>label options> Value from cells diyerek karışık olan formüllü kolondaki datayı seçtim, X ve Y işaretliyse işareti kaldırdım
  • T kolonundaki fomrülleri oluşturdum.
  • Named Range'leri oluşturdum: T3, T4, T5, T6 ve U3:U6 (Değerlerine siz bakarsınız)

Bütün bunlardan sonra ana kodumuz nasıl ona bi bakalım ve girişte bahsettiğim iki çözüm yöntemini ayrı ayrı görelim.

Sub grafikayar()
Dim cht As ChartObject

Set cht = ActiveSheet.ChartObjects("Chart 4") 'Diğer sayfada Chart 2
cht.Activate

With cht.Chart
    .Axes(xlValue).MinimumScale = [min_y] * IIf([min_y] < 0, 1.01, 0.99)
    .Axes(xlValue).MaximumScale = [max_y] * IIf([max_y] > 0, 1.01, 0.99)
    .Axes(xlCategory).MinimumScale = [min_x] * IIf([min_x] < 0, 1.01, 0.99)
    .Axes(xlCategory).MaximumScale = [max_x] * IIf([max_x] > 0, 1.01, 0.99)
    .Axes(xlCategory).CrossesAt = [blgmaliyet]
    .Axes(xlValue).CrossesAt = [blgmusart]
    .ChartTitle.Text = [a1].Value & " Bölgesi Maliyet ve Müşteri Kazanım Performansı"
End With
End Sub

Kodun mantığı oldukça basit. Grafiğin en solundaki değeri tüm bankadaki şubelere göre seçme de, bu bölgenin en düşük maliyetli şubesinden birazcık solda seç. "Birazcık" için de IIf([min_y] < 0, 1.01, 0.99) formülünü kullandım. En sağındaki değeri de o bölgenin en yüksek maliyetli şubesinden biraz büyük seç. Y ekseni yani müşteri artışı için de benzer mantık var.

X ve Y eksenlerinin kesişimi olarak da bölgenin ortalama değerlerini veriyorum. (CrossesAt satırları)

Son olarak da başlığı da seçilen bölgeye göre yazdırıyorum.

Evet şimdi kodun geri kalan kısımları için iki farklı çözüm yöntemine bakabiliriz.

1.Yöntem: PT eventleri ile

İlk yöntem Worksheet_Calculate aklıma gelmediği için düşündüğüm uzun yol yöntemi. Burada PT eventlerinden faydalanacağız. Aslında 2.yöntem olan Calculate yöntemini gördükten sonra "1.yönteme ne gerek var, niye koydun ki?" diye sorabilirsiniz. Şundan; burada naçizane yaratıcı bir çözüm var. Microsfort tarafından dokumante edilmemiş farklı bir yöntem uygulaması var. Calculate eventi diye bir event olmasaydı nasıl bir çözüm üreteceğimize dair programatik bir bakış açısı var. İşte bu bakış açısı başka bir yerde yine lazım olabilir. Nasıl esnek düşünmeniz gerektiğine dair güzel bir örnek bence.

Burada yaptığımız şey şu:

  • Verimizin bulunduğu Table'ın hemen altına Bölge isimlerinden oluşan bir Pivot tablo yarattım.
  • Bu PT'ı kontrol eden de bir Slicer oluşturdum ve bunu en yukarıya koydum. Kullanıcı buna bastığında sanki Table'ın Slicerına bastığını sanacak
  • Table'a ait bir Slicer da oluşturdum(Zorunda değildim ama yukarıda öğrendiklerimizi uygulamak adına iyi olur dedim, yoksa doğrudan Table üzerinde de filtreleme yapabilirdik). Bu slicerı aşağıda bıraktım, Shapes collection'ından yaralanıp gizli hale de getirebilirdim.
  • Burada kullanıcı, PT Slicerından ne zaman eleman seçse Worksheet_PivotTableChangeSync eventi tetikleniyor ve seçilen değeri bulup bunu hem A1'e yazıyor ki böylece grafiğin değerleri yeniden belirlensin, hem de Table'ın Slicerında bu seçimi yaptırıyor ki grafik yeni bölgeye göre güncellensin.
  • Sayfada yapılan değişiklikler Change eventini tetiklemesin diye eventler baskılanıyor
  • Seçim sonucunda tüm filtre kaldırılıyorsa Table Slicerında da aynısını yaptırıyorum, filtre değişiyorsa bunu buldurup esas işlemleri yaptırıyorum.

Şimdi kodumuza bakalım.

Private Sub Worksheet_PivotTableChangeSync(ByVal Target As PivotTable)
    Dim spi As SlicerItem, sti As SlicerItem
    On Error GoTo hata
        
    Application.EnableEvents = False: Application.ScreenUpdating = False
    [minmax].Value = 1 'Bir önceki bölgede manuel oalrak farklı bişey yaılzdıysa baştan tekrar hepsin 1 yapalım
    
    If ActiveWorkbook.SlicerCaches("Slicer_Bölge4").VisibleSlicerItems.Count = ActiveWorkbook.SlicerCaches("Slicer_Bölge4").SlicerItems.Count Then 'pivotun slicerı
        ActiveWorkbook.SlicerCaches("Slicer_Bölge5").ClearManualFilter 'Tableınki, görünmezdir, veya aşağılarda görünmez olsa da olur
        GoTo çıkış
    End If
    
    secilen = SlicerdaSeçilileriGetir("Slicer_Bölge4")(1)
    slicerda_tek_eleman_seç "Slicer_Bölge5", secilen
    Application.EnableEvents = False 'yukardakilerden döndüğünde True oluyor, tekrar false yapalım
çıkış:
    [a1].Value = secilen
    Call grafikayar
    Application.EnableEvents = True: Application.ScreenUpdating = True
    
    Exit Sub
hata:
    Application.EnableEvents = True: Application.ScreenUpdating = True
    MsgBox Err.Description
End Sub

Bir de minmax range'inde SMALLIF ve LARGEIF değerlerini etkileyecek bir değişiklik yapıldığında bunları yakalıyoruz demiştik, onun kodu da aşağıda.

Private Sub Worksheet_Change(ByVal Target As Range)
    On Error GoTo hata
        
    Application.ScreenUpdating = False
    If Not Intersect(Target, [minmax2]) Is Nothing Then 'bölge seçildiğinde de değişiklik oluyor, bu tetiklemsin
        Call grafikayar
    End If
    Application.ScreenUpdating = True
    Exit Sub
hata:
    Application.ScreenUpdating = True
    MsgBox Err.Description
End Sub		

2.Yöntem: Calculate eventi ile

Bu yöntem daha sadedir. Table'da filtre konup konmadığını anlamanın yine doğrudan bir yolu yok ancak bunun için Table'ın altına bir SUBTOTAL formülü koyunca table her filtrelendiğinde bu formül de yeniden hesaplanmış olacak, ki bu da Calculate eventini tetikler, işte bu değişikliği yakalıyoruz ve akabinde seçilen değeri bulup grafikayar prosedürünü çağırıyoruz.

Dikkat:Calculate eventi bazen beklenmedik şekilde çalışabilir. Yani siz o sırada başka sayfada işlem bile yapsanız Calculate eventinin bulunduğu sayfa yine de tetiklenebiliyor. Bunun sebebi ya o sayfada diğer sayfalardan etkilenen formüller vardır ya da volatil bir fonksiyon kullanmışsınızdır. Gerçi bizim sayfamızda ikisi de yok, çok araştırdım ama sebebini bulamadım. Ben de aşağıdaki gibi sayfa adını kontrol etmekle çözdüm bu sorunu. Bunu da yapamasaydım 1.yöntemle ilerlerdim.

Private Sub Worksheet_Calculate()
    If ActiveSheet.Name <> "Örnek UygulamaTableEventli" Then Exit Sub
    
    Application.EnableEvents = False
    secilen = SlicerdaSeçilileriGetir("Slicer_Bölge3")(1)
    [a1].Value = secilen
    Call grafikayar
    Application.EnableEvents = True

End Sub			

Sonuç

Aşağıdaki resimlerden, bölgeler değiştikçe ortalama çizgilerinin (hem x hem y) nasıl değiştiğini görebilirsiniz. 

scatter2

TEST SORULARI

Son Sorumuz şuymuş:Bir metindeki tüm noktaları yoketmek istiyorsunuz. Hangi fonksiyonu kullanırdınız?
Soru:

A şıkkı:

B şıkkı:

C şıkkı:

D şıkkı:

Doğru Cevap Etiketler

İlişkili konuyu seç

207465

Label
* Sorulara verilen yanlış cevaplardaki esprili yorumlarım için hoşgörünüze sığınıyorum.
* Test ve Ödevlerdeki bazı detaylar burada anlatılmamış olabilir. Bunları kendiniz araştırıp bulmalısınız.
* Birden çok konuya ait içeriği olan ödevler var. Algoritmik açıdan bakıldığında o an en uygun konuya adreslenmiştir.
Dikkat! Bir soruya cevap verdikten sonra geri dönemezsiniz.
0
0
0
0

SoruID:... Şu an için bu konu için soru bulunmamaktadır. İletişim menüsünden örnek sorularınızı bana iletebilirsiniz.




ÖDEVLER

0
0
ÖdevNo:... Şu an için bu konu için ödev bulunmamaktadır. İletişim menüsünden örnek ödev sorularını bana iletebilirsin.
Çözüme bakın(Başka türlü de çözülebilir tabi, bu benim çözümüm.)




=YORUMLAR ve SORULAR=


DEVİR UYARISI

Herkese merhaba. Hosting maliyetlerinin aşırı artması yüzünden sitemi yakın zamanda(en geç Mayıs 2023) kapatmaya karar vermiştim. Ancak, siteyi yakından takip eden bir arkadaş siteyi devralmak istemiştir. Siteyi, Mayıs ayında kendisine devir etmeye karar verdim. Üyelik bilgilerini bana güvenerek girdiğiniz için, hepsini silmiş bulunuyorum, yani mail adreslerinizi kimseyle paylaşmamış olacağım. Bilginizi rica ederim.