Scalacz – aplikacja do automatycznej konsolidacji danych z wielu skoroszytów

Tym razem nieco większa porcja kodu. Jest to w miarę kompletna aplikacja, gotowa do użycia. Wystarczy skopiować poniższy kod do modułu publicznego w Excelu (Alt+F11, Insert – Module). Aplikacja konsoliduje dane z wielu skoroszytów i zapisuje je w zbiorczym, nowo tworzonym arkuszu. Przed użyciem proszę uważnie przeczytać założenia

Public Const WERSJA As String = "Scalacz v.1.0 (Akademia-VBA.pl)"
 
' ==================================================
' ZAŁOŻENIA:
' 1. Wszystkie skoroszyty do scalenia znajdują się w jednym folderze
' 2. Nie ma tam żadnych skoroszytów przypadkowych (w tym docelowego)
' 3. Dane do scalenia są zawsze w pierwszym arkuszu i mają jednakowe kolumny
' 4. Dane do scalenia mają nagłówki
' ==================================================
 
Sub Scalaj()
    Dim Skonsolidowany As Worksheet
    Dim Plik As String
    Dim Skor As Workbook, Ark As Worksheet
    Dim Pocz As Range, Kon As Range
    Dim Naglowki As Range, Podzakres As Range, KomDocel As Range
    Dim Licznik As Long, LW As Long, LK As Long
    Dim ZakresDocel As Range, LW_Docel As Long
    Dim Folder As String
 
    Folder = WskazFolder("Wskaż folder z plikami do scalenia", "Scalaj")
    If Len(Folder) = 0 Then
        MsgBox "Nie wskazano foldera źródłowego", vbExclamation, WERSJA
        Exit Sub
    End If
    Application.ScreenUpdating = False
    Set Skonsolidowany = Worksheets.Add()
    Plik = Dir(Folder & "*.xls")
    Do Until Len(Plik) = 0
        Licznik = Licznik + 1
        Application.StatusBar = "Konsolidacja pliku nr " & Licznik
        Set Skor = Workbooks.Open(Folder & Plik)
        Set Ark = Skor.Sheets(1)
        If Licznik = 1 Then
            Set Naglowki = Ark.Range("A1").CurrentRegion.Rows(1)
            Naglowki.Copy Skonsolidowany.Range("A1")
            Set KomDocel = Skonsolidowany.Range("A2")
        Else
            Set ZakresDocel = Skonsolidowany.Range("A1").CurrentRegion
            LW_Docel = ZakresDocel.Rows.Count
            Set KomDocel = Skonsolidowany.Cells(LW_Docel + 1, 1)
        End If
        Set Podzakres = Ark.Range("A1").CurrentRegion
        LW = Podzakres.Rows.Count
        LK = Podzakres.Columns.Count
        Set Podzakres = Range(Ark.Range("A2"), Ark.Cells(LW, LK))
        Podzakres.Copy KomDocel
        Skor.Close False
        Plik = Dir
    Loop
    Skonsolidowany.Name = "Skonsolidowany" & StempelCzasowy
    Skonsolidowany.UsedRange.EntireColumn.AutoFit
    Skonsolidowany.Range("A1").Select
    'Application.StatusBar = "Gotowy"
    Application.StatusBar = False
    Application.ScreenUpdating = True
    MsgBox "Konsolidacja " & Licznik & " arkuszy zakończona", vbInformation, WERSJA
End Sub
 
Function StempelCzasowy() As String
    StempelCzasowy = Format(Now(), "_yyyymmdd_hhmmss")
End Function
 
Function WskazFolder(TytulOkna As String, TytulPrzycisku As String) As String
    Dim Okno As FileDialog
    Dim Wybrane As String
    Set Okno = Application.FileDialog(msoFileDialogFolderPicker)
    Okno.Title = TytulOkna
    Okno.ButtonName = TytulPrzycisku
    If Okno.Show = -1 Then
        Wybrane = Okno.SelectedItems(1)
        If Right(Wybrane, 1) <> "\" Then
            WskazFolder = Wybrane & "\"
        Else
            WskazFolder = Wybrane
        End If
    End If
End Function

Zabezpieczenia makr, poziomy zabezpieczeń

Na szczęście makrowirusy nie są ostatnio bardzo popularne, ale proszę zawsze uważać, lepiej dmuchać na zimne. Poziom zabezpieczeń ustawiamy z menu – Narzędzia – Makro – Zabezpieczenia.

Nie wolno ustawiać niskiego poziomu zabezpieczeń, ponieważ wtedy każdy uruchomiony plik zawierający makro, będzie mógł je bez pytania uruchomić. W większości przypadków należy użyć średniego poziomu zabezpieczeń, który gwarantuje zadanie pytania czy włączyć makra, przy uruchamianiu pliku. Proszę się dobrze zastanowić przed odpowiedzią na to pytanie!

Nigdy nie należy włączać makr w obcym nieznanym pliku!

Zabezpieczenia wysokie i bardzo wysokie stosujemy tylko gdy mamy wykupiony certyfikat bezpieczeństwa do projektu VBA co się raczej rzadko zdarza

Jak skorzystać z kodu VBA znalezionego w Internecie

Dla początkujących może być to poważny problem. Przede wszystkim trzeba ocenić czy przypadkiem kod nie jest szkodliwy. Na szczęście w przypadku kodu VBA jest to bardzo rzadko spotykane. Po drugie trzeba rozróżnić czy jest to kod do umieszczenia w module publicznym, czy prywatnym module formularza, raportu, skoroszytu czy arkusza.

Moduł publiczny wstawiamy z menu Instert – Module (wcześniej Alt-F11 aby otworzyć edytor VBA). Tu wklejamy funkcje, które mają być dostępne z poziomu funkcji użytkownika w Excelu czy też w każdej kwerendzie, raporcie czy formularzu w Accessie (np wrzucamy tu funkcję Slownie, jeżeli chcemy aby była powszechnie dostępna w całej bazie)

Moduł prywatny zawsze związany jest z jakimś obiektem. Tylko tu możemy umieścić kod automatycznie wykonujący się z powodu zdarzeń generowanych przez użytkownika takich jak otwarcie czy zamknięcie dokumentu, lub kliknięcie w przycisk. Jeżeli widzimy kod bezpośrednio odwołujący się do kontrolek na formularzu, jak np poniższe dwie procedury:

Private Sub UserForm_Initialize()
 
    przyciskUruchom.Enabled = False
    listaFunkcji.AddItem "Funkcja1"
    listaFunkcji.AddItem "Funkcja2"
 
End Sub
 
Private Sub listaFunkcji_Click()
    przyciskUruchom.Enabled = True
End Sub

to znaczy, że aby przetestować taki kod trzeba: po pierwsze trzeba utworzyć formularz, po drugie umieścić na nim pole listy i przycisk a po trzecie odpowiednio je ponazywać (właściwość Name kontrolki). Efektem powyższych procedur jest początkowe wyłączenie przycisku i załadowanie pola listy. Następnie gdy użytkownik kliknie coś na liście, przycisk zostanie włączony.

Tworzenie i użycie własnej zmiennej obiektowej typu Excel.Range

Przykład na tworzenie własnej zmiennej typu Range i posługiwanie się właściwościami obiektu Range, typu Range:

  1. CurrentRegion
  2. Rows(W)
  3. Columns(K)
  4. Cells(W,K)
Sub WlasnaZmiennaRange()
 
    Dim Zakres As Range
    Dim LW As Long, LK As Long
 
    'sprawdzenie, czy aktywna komórka znajduje się
    'w zakresie danych, przy pomocy własnej funkcji logicznej
    If fnBrakZakresu Then Exit Sub
 
    'ustawienie referencji do obiektu
    'na podstawie właściwości "Bieżący obszar" aktywnej komórki
    Set Zakres = ActiveCell.CurrentRegion
 
    'kolorowanie zakresu poprzez własną zmienną
    Zakres.Interior.ColorIndex = 35
 
    'odczyt liczby wierszy i kolumn zakresu
    LW = Zakres.Rows.Count
    LK = Zakres.Columns.Count
 
    'wyświetlenie wartości zmiennych w oknie debuggera
    Debug.Print "lw: " & LW
    Debug.Print "lk: " & LK
 
    'kolorowanie skrajnych wierszy
    Zakres.Rows(1).Interior.ColorIndex = 34
    Zakres.Rows(LW).Interior.ColorIndex = 34
 
    'i kolumn
    Zakres.Columns(1).Interior.ColorIndex = 34
    Zakres.Columns(LK).Interior.ColorIndex = 34
 
    'kolorowanie narożników zakresu
    Zakres.Cells(1, 1).Interior.ColorIndex = 36
    Zakres.Cells(1, LK).Interior.ColorIndex = 36
    Zakres.Cells(LW, 1).Interior.ColorIndex = 36
    Zakres.Cells(LW, LK).Interior.ColorIndex = 36
 
End Sub

Na koniec funkcja logiczna, z której korzysta powyższa procedura:

Function fnBrakZakresu() As Boolean
    If Len(ActiveCell) = 0 Then
        MsgBox "Ustaw się w niepustej komórce zakresu danych!"
        fnBrakZakresu = True
    End If
End Function

Co to znaczy: Wymaga referencji do Microsoft Office XX Object Library

Oznacza to, że w edytorze VBA należy uruchomić okienko do dodawania referencji do bibliotek obiektowych (Tools – References), znaleźć bibliotekę Microsoft Office XX Object Library (gdzie XX to nr Twojego Office’a, np 14.0) i wstawić przy niej ptaszek

Jak z VBA wywołać okno dialogowe Office do pobrania nazwy foldera od użytkownika

Nie możemy wymagać od użytkownika, aby poprawnie wpisywał nazwy foldera z ręki. Dużo wygodniej będzie mu użyć dobrze znanego mu okna dialogowego do wskazywania folderów. Dodatkowym bonusem jest, że można też wskazać folder właśnie utworzony w tym samym oknie. Bardzo wygodna funkcja…

'wymaga referencji do Microsoft Office XX Object Library
Function WskazFolder(TytulOkna As String, TytulPrzycisku As String) As String
    Dim Okno As FileDialog
    Dim Wybrane As String
    Set Okno = Application.FileDialog(msoFileDialogFolderPicker)
    Okno.Title = TytulOkna
    Okno.ButtonName = TytulPrzycisku
    If Okno.Show = -1 Then
        Wybrane = Okno.SelectedItems(1)
        If Right(Wybrane, 1) <> "\" Then
            WskazFolder = Wybrane & "\"
        Else
            WskazFolder = Wybrane
        End If
    End If
End Function