V článku Tabulka aneb Seznam čili ListObject jsme se věnovali Tabulkám z pohledu práce na listu. Dnes si je osaháme prostřednictvím kódu VBA, kde jim není vyhrazen podle očekávání objekt Table, ale ListObject. Následující procedury najdete v přiloženém sešitu a doporučuji je odkrokovat při současném zobrazení listu s Tabulkou.
V kódu najdete hodně poznámek ohledně posunu buněk pod Tabulkou. K němu (ne vždy) dochází, pokud v Tabulce mažete/přidáváte řádky nebo měníte zobrazení Řádku souhrnů. Pokud toto řešit nechcete, tak se držte toho, co bylo řečeno posledně – do prostoru pod Tabulkou již nevkládejte žádné s ní nesouvisející hodnoty.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | Sub TabulkaCasti() Dim wshList As Worksheet Dim loTabulka As ListObject Set wshList = Worksheets("Tabulka a VBA") Set loTabulka = wshList.ListObjects("MojeTabulka") With loTabulka 'celá tabulka .Range.Select 'hlavička tabulky .HeaderRowRange.Select 'datová část tabulky .DataBodyRange.Select 'datová část tabulky (konstanty, tj. bez vzorců) .DataBodyRange.SpecialCells(xlCellTypeConstants).Select 'druhý sloupec tabulky .ListColumns(2).Range.Select 'datová část druhého sloupce tabulky .ListColumns(2).DataBodyRange.Select 'třetí datový řádek tabulky .ListRows(3).Range.Select 'řádek souhrnů (pokud je zobrazen) '.ShowTotals = True .TotalsRowRange.Select End With End Sub |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Sub TabulkaVlastnosti() Dim wshList As Worksheet Dim loTabulka As ListObject Set wshList = Worksheets("Tabulka a VBA") Set loTabulka = wshList.ListObjects("MojeTabulka") With loTabulka 'počet řádků včetně hlavičky (a řádku souhrnů) intPocetRadku = .Range.Rows.Count 'počet datových řádků intPocetZaznamu = .ListRows.Count End With End Sub |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Sub TestBunkaTabulky() Dim rngBunka As Range Dim blnBunkaTabulky As Boolean Set rngBunka = ActiveCell On Error Resume Next 'je buňka součástí Tabulky? blnBunkaTabulky = (rngBunka.ListObject.Name <> "") On Error GoTo 0 If blnBunkaTabulky = True Then 'výběr datové oblasti Tabulky, jíž je buňka součástí rngBunka.ListObject.DataBodyRange.Select End If End Sub |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | Sub PridaniJednohoRadku() 'zobrazení řádku souhrnů není na překážku Dim wshList As Worksheet Dim loTabulka As ListObject Set wshList = Worksheets("Tabulka a VBA") Set loTabulka = wshList.ListObjects("MojeTabulka") With loTabulka 'vložení nového řádku na konec tabulky .ListRows.Add 'vložení nového řádku na konec tabulky 'buňky pod tabulkou se neposunou .ListRows.Add AlwaysInsert:=False 'pozn. Zobrazení/skrytí řádku souhrnů posouvá 'buňky ležící pod Tabulkou vždy... 'vložení nového řádku nad 3. záznam 'tj. definujeme pozici nového řádku .ListRows.Add (3) End With End Sub |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | Sub PridaniDatNaKonec() 'Zack Barresse Dim wshList As Worksheet Dim loTabulka As ListObject Dim arrPridavanaData As Variant Dim inPocetRadku As Integer Dim intPocetSloupcu As Integer Set wshList = Worksheets("Tabulka a VBA") Set loTabulka = wshList.ListObjects("MojeTabulka") 'převzetí dat, zde z oblasti buněk do pole arrPridavanaData = wshList.Range("B20:D23").Value 'rozměry datového bloku (pole) inPocetRadku = UBound(arrPridavanaData, 1) intPocetSloupcu = UBound(arrPridavanaData, 2) 'nutné skrytí řádku souhrnů, pokud je zobrazen 'pozor, dojde k posunu buněk ležících pod Tabulkou loTabulka.ShowTotals = False With loTabulka.DataBodyRange 'přidání dat na konec Tabulky 'neposouvá buňky ležící pod Tabulkou .Resize(inPocetRadku, intPocetSloupcu).Offset(.Rows.Count).Value = _ arrPridavanaData End With 'zobrazení řádku souhrnů 'pozor, dojde k posunu buněk ležících pod Tabulkou 'loTabulka.ShowTotals = True 'oživení filtru If loTabulka.ShowAutoFilter = True Then loTabulka.AutoFilter.ApplyFilter End If End Sub |
Pozn. Řádek souhrnů je sice chytrý, ale někdy nám prostě překáží.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | Sub VlozeniDat() 'Zack Barresse Dim wshList As Worksheet Dim loTabulka As ListObject Dim arrPridavanaData As Variant Dim inPocetRadku As Integer Dim intPocetSloupcu As Integer Set wshList = Worksheets("Tabulka a VBA") Set loTabulka = wshList.ListObjects("MojeTabulka") 'převzetí dat, zde z oblasti buněk do pole arrPridavanaData = wshList.Range("B20:D23").Value 'rozměry datového bloku (pole) inPocetRadku = UBound(arrPridavanaData, 1) intPocetSloupcu = UBound(arrPridavanaData, 2) With loTabulka.DataBodyRange 'přidání dat mezi 5 a 6. řádek Tabulky 'tj. pod 5. řádek 'neposouvá buňky ležící pod Tabulkou .Resize(inPocetRadku).Offset(5).Insert Shift:=xlShiftDown .Resize(inPocetRadku, intPocetSloupcu).Offset(5).Value = _ arrPridavanaData End With 'oživení filtru If loTabulka.ShowAutoFilter = True Then loTabulka.AutoFilter.ApplyFilter End If End Sub |
Pokud se nepletu, Tabulku průvodci na listu nativně nabízejí pro zdroje dat z Microsoft Access, Microsoft Excel a pravděpodobně i z Microsoft SQL nebo třeba MySQL. V případě textových souborů (CSV) to trochu skřípe. Je otázka, zda-li jít cestou QueryTable či OLEDB (ADO, schema.ini). O tom ale zase někdy jindy. Nyní se podíváme na filtrování.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | Sub TabulkaFiltr() Dim wshList As Worksheet Dim loTabulka As ListObject Set wshList = Worksheets("Tabulka a VBA") Set loTabulka = wshList.ListObjects("MojeTabulka") 'zapnutí/vypnutí filtru Tabulky 'loTabulka.Range.AutoFilter 'striktní obsah (Richtr) loTabulka.Range.AutoFilter Field:=1, Criteria1:="=Richtr" 'striktní obsah (vše kromě položky Richtr) loTabulka.Range.AutoFilter Field:=1, Criteria1:="<>Richtr" 'obsah (položky začínající na písmeno K) '? ... jeden znak, * ... žádný nebo více znaků loTabulka.Range.AutoFilter Field:=1, Criteria1:="=K*" 'striktní obsah (Koch nebo Morávek) loTabulka.Range.AutoFilter Field:=1, Criteria1:="=Koch", Operator:=xlOr, _ Criteria2:="=Morávek" 'striktní obsah (Koch, Morávek, Sochor) loTabulka.Range.AutoFilter Field:=1, Criteria1:=Array("Koch", "Morávek", _ "Sochor"), Operator:=xlFilterValues 'není nástroj pro "vše kromě položek z následujícího výčtu" 'lze řešit negací úlohy nebo přes rozšířený filtr, tzn. 'OBCHODNIK OBCHODNIK OBCHODNIK '<>Koch <>Morávek <>Sochor 'v případě datumu je nutný americký formát mm/dd/yyyy 'a to bez ohledu na výstup ze Záznamníku maker 'tj. např. Criteria1:=">=02/23/2015" 'lze si pomoci funkcemi CLng, CDate 'oživení filtru (po aktualizaci dat) 'If loTabulka.ShowAutoFilter = True Then ' loTabulka.AutoFilter.ApplyFilter 'End If End Sub |
Pozor na jednu nebezpečnou věc. V rámci filtru a parametru Criteria očekává Excel String a to i v případě čísel a testování rovnosti (regulérně bychom měli doplňovat znak „=“, např. Criteria1:=“=6″). Běžně si Excel s předaným číselným typem (Integer) poradí i bez uvedeného rovnítka, ale rozhodně tento stav nesmí nastat při užití Operator:= xlFilterValues. Jestliže takto definovanému filtru předhodíte jednu hodnotu číselného typu, tak Excel začne dělat psí kusy, přestane fungovat překreslování a aplikace nepůjde korektně zavřít. Jedná se podle mě o neošetřený stav a bug.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Sub ResetFiltruTabulky() Dim wshList As Worksheet Dim loTabulka As ListObject Set wshList = Worksheets("Tabulka a VBA") Set loTabulka = wshList.ListObjects("MojeTabulka") 'pokud Tabulka používá filtr... If loTabulka.ShowAutoFilter Then 'pokud je filtr aktivní If loTabulka.AutoFilter.FilterMode Then 'zobrazení všech dat loTabulka.AutoFilter.ShowAllData End If End If End Sub |
Pucování tabulky od dat lze řešit dvojím způsobem. Osobně jsem si oblíbil druhý z nich.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | Sub VycisteniTabulky1() Dim wshList As Worksheet Dim loTabulka As ListObject Set wshList = Worksheets("Tabulka a VBA") Set loTabulka = wshList.ListObjects("MojeTabulka") 'vymazání všech datových řádků tabulky 'nelze aplikovat na Tabulce bez datových řádků If loTabulka.ListRows.Count > 0 Then loTabulka.DataBodyRange.Delete 'výsledek: 'podobjekt DataBodyRange = Nothing 'Tabulka zobrazuje jeden fiktivní datový řádek (bez vzorců) 'Rows.Count = 2 pro hlavičku + fiktivní řádek 'ale (!) ListRows.Count = 0 'dojde k posunu buněk ležících pod Tabulkou 'pro další práci je nutné přidat alespoň jeden řádek loTabulka.ListRows.Add 'dojde k navrácení případných vzorců ve sloupcích 'pozn. vzorce se uchovávají v ../xl/tables/table?.xml 'buňky ležící pod Tabulkou se neposunou End If End Sub |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | Sub VycisteniTabulky2() Dim wshList As Worksheet Dim loTabulka As ListObject Set wshList = Worksheets("Tabulka a VBA") Set loTabulka = wshList.ListObjects("MojeTabulka") 'operace proběhnou jen na minimálně jednořádkové 'vyplněné datové oblasti On Error Resume Next Application.ScreenUpdating = False With loTabulka.DataBodyRange 'odstranění všech řádků kromě prvního .Resize(.Rows.Count - 1).Offset(1).Delete 'vyčistění zbylého řádku od hodnot (vzorce ponechány) .SpecialCells(xlCellTypeConstants).ClearContents End With On Error GoTo 0 Application.ScreenUpdating = True End Sub |
Příloha (váže se i k předchozímu článku):
tabulka.zip