Baza danych

  Bardzo często początkujący programiści, a szczególnie ci korzystający z pakietów Borland C++ Builder x.xx idą na łatwiznę i tworzą bazy danych w oparciu o komponenty DBD lub DBF dołączane wraz z pakietem BCB. Taki sposób pisania programów tak naprawdę niewiele ma wspólnego z programowaniem, ponieważ komponenty BDB i BDF wykorzystują specjalny program instalowany wraz z pakietem BCB, a to z kolei wiąże się z pewnymi ograniczeniami, mianowicie programy stworzone w ten sposób wymagają do działania właśnie tego dodatku z pakietu, a to oznacza, że jeśli chcemy przenieść taki program na inny komputer to trzeba koniecznie dołączyć engine DBD lub DBF. Takie programy są dobre tylko do tworzenia prototypów i programów na domowy użytek. Ponadto takie bazy danych można stworzyć praktycznie bez kodowania i może to zrobić nawet osoba nie będąca programistą. Istnieje możliwość rozbudowania i dołączania własnych funkcji do tego typu baz danych tworząc w ten sposób bardzo skomplikowaną i użyteczną bazę danych, ale programista powinien umieć tworzyć programy niezależnie od posiadanego kompilatora. Dlatego w tym kursie pokażę jak w prosty sposób stworzyć całkiem użyteczną bazę danych. Prezentowany program będzie wyposażony w tabelę z możliwością dowolnego określania ilości wierszy i kolumn, oraz definiowania nagłówków dla kolumn, a także będzie posiadał funkcję umożliwiającą dołączenie do każdego wiersza notatki oraz grafiki w formacie bmp. Program będzie tworzył pliki bazodanowe we własnym formacie, jako rozszeżenie dla plików wybrałem *.dbf (data base format), ale rozszeżenie plików jest czysto umowne i może być dowolne. Program można wykorzystać np. jako książkę adresową zawierającą w tabeli np. imiona i nazwiska osób w notatce adres a w grafice zdjęcie.

Zrzut ekranu



   krok 1. - tworzymy nowy projekt
  Na początek należy utworzyć na dysku nowy katalog w którym będziemy przchowywać projekt. Następnie uruchamiamy BCB. W chwili uruchomienia program automatycznie tworzy nowy projekt. Przechodzimy do menu File i wybieramy Save All. Plik źródłowy zapisujemy pod nazwą Unit1.cpp, plik nagłówkowy zostanie utworzony automatycznie i zapisany pod nazwą Unit1.h. Nazwa programu jest dowolna ja zastosowałem tutej DataBase.
  Ze względu na spory stopień komplikacji programu i zastosowanie wielu funkcji zdecydowałam się na utworzenie klasy która będzie całą bazą danych. Klasa będzie się nazywała 'TDataBase' i zostanie umieszczona w odrębnym pliku. W tym celu przechodzimy do menu Plik|New. W otwartym w ten sposób oknie wybieramy Unit. Ponownie wybieramy menu Plik|Save i nowo utworzoną jednostkę zapisujemy pod nazwą 'klasa' (nazwa jest dowolna).
  Teraz przechodzimy do pliku 'Unit1.h' i w sekcji include umieszczamy wpis: #include "klasa.h", w ten sposób włączamy do projektu nowo utworzoną jednostkę.
Przykład:

// Plik nagłówkowy Unit1.h.
//--------------------------------
//--------------------------------
#ifndef Unit1H
#define Unit1H

//--------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Grids.hpp>
#include <Menus.hpp>
#include "Klasa.h" // dołączany plik klasa.cpp
#include <ExtCtrls.hpp>
#include <Dialogs.hpp>

//--------------------------------



   krok 2. - tworzymy strukturę memo
Przechodzimy do pliku klasa.h i w sekcji include włączamy plik 'Grids.hpp', plik ten zawiera klasę komponentu 'TStringGrid'.
Przykład:

// Plik nagłówkowy klasa.h.
//--------------------------------
#ifndef KlasaH
#define KlasaH
//--------------------------------
#include <Grids.hpp> // Włączany plik Grids.hpp
//--------------------------------


Następnie tworzymy strukturę o nazwie memo, która posłuży nam później do przenoszenia danych pomiędzy formularzem (a konkretnie obiektem Memo1) i klasą TDataBase.
Przykład:

// Plik nagłówkowy klasa.h.
//--------------------------------
#include <Grids.hpp>
//--------------------------------
struct memo // Tworzona struktura.
{
   String A[1000];
};
//--------------------------------


Jak widać struktura zawiera zmienną typu String, a dokładnie tablicę jednowymiarową zawierającą tysiąc pozycji. Liczba tysiąc określa ile zmiennych można wykorzystać wewnątrz struktury, a efekt jest taki jakbyśmy utworzyli tysiąc zmiennych typu String i nadali im kolejne nazwy: A0, A1, A2, A3... i tak dalej ża do tysiąca. Do struktury wrócimy później gdy zajmiemy się zapisywaniem notatek do pliku bazy danych.

   krok 3. - tworzymy deklarację klasy TDataBase i funkcji, które klasa zawiera
  Teraz utworzymy klasę 'TDataBase', a zaczniemy od utworzenia deklaracji klasy zwanej również prototypem klasy. W tym celu w pliku klasa.h poniżej deklaracji struktury umieszczamy wpisy, które podaję w tabeli poniżej.
Przykład:

// Plik nagłówkowy klasa.h.
//--------------------------------
#ifndef KlasaH
#define KlasaH
//--------------------------------
#include <Grids.hpp>
//--------------------------------
struct memo
{
  String A[1000];
};
//--------------------------------
class TDataBase
{
 public:
 TDataBase();
 ~TDataBase();

 void SaveToFile(String FileName, TStringGrid *Grid);
 void LoadFromFile(String FileName, TStringGrid *Grid);
 void SetMemo(String tekst, int number);
 void SetBitmap(String FileName, int number);
 memo GetMemo(int number);
 String GetBitmap(int number);

 int MemoCount;
 bool JestNotatka;

 private:
 int ObliczIleKolumn(String tekst);
 int ObliczIleWierszy(TStringList *lista);

 String SetColSize(int Cols, TStringGrid *Grid);
 void GetColSize(String tekst, int Cols, TStringGrid *Grid);

 bool GetMemoSection(String tekst);
 int WierszeMemo(String tekst);

 TStringList *Memo;
 TStringList *Bitmap;
};
//--------------------------------
#endif


Jak widać deklaracja klasy zaczyna się od słowa kluczowego class po którym występuje nazwa klasy TDataBase (nazwa klasy jest dowolna), potem są nawiasy klamrowe (otwierający i zamykający) po których zostaje umieszczony średnik (;).
Wewnątrz nawiasów znajdują się dwie sekcje - public: i private:. W sekcji public znajdują się deklaracje funkcji i zmiennych publicznych czyli takich które są dostępne zarówno wewnątrz klasy jak i na zewnątrz. Natomiast w sekcji private znajdują się deklaracje funkcji i zmiennych dostępnych tylko wewnątrz klasy.
Pierwszy wpis 'TDataBase()' jest deklaracją konstruktora klasy, jeżeli byśmy nie utworzyli konstruktora klasy to kompilator sam utworzyłby domyślnego konstruktora, lecz w tym przypadku konstruktor będzie zawierał procedury, które będą wykonywane w momencie tworzenia klasy. Jeśli tworzymy konstruktora klasy to trzeba również utworzyć destruktora klasy - '~TDataBase()'. Destruktor nosi taką samą nazwę jak konstruktor z tą różnicą, że zawiera na samym początku znak tyldy (~).

  • Funkcja 'SaveToFile' zostanie wykorzystana do zapisywania danych do pliku. Funkcja pobiera dwa parametry: FileName, który będzie nazwą pliku, oraz Grid wywodzący się z klasy TStringGrid, który posłuży do pobierania danych z obiektu StringGrid1, który będzie znajdował się na formularzu.
  • Funkcja 'LoadFromFile' zostanie wykorzystana do ładowania danych z pliku. Pobiera te same parametry co funkcja SaveToFile.
  • Funkcja 'SetMemo' będzie pobierała notatki, które następnie zostaną zapisane do obiektu typu TStringList - Memo, deklaracja tego obiektu znajduje się w sekcji private. Funkcja pobiera dwa parametry, pierwszy: tekst będzie zawierał treść notatki, drugi: number będzie zawierał numer wiersza, którego notatka będzie dotyczyć.
  • Funkcja 'GetMemo' posłuży do pobierania notatek z obiektu TStringList Memo i przepisywania ich do obiektu 'Memo1', który będzie znajdowała się na formularzu. Wcześniej opisywane funkcje były typu void a co za tym idzie nie zwracały żadnych wartości, natomiast funkcja GetMemo wywodzi się ze struktury memo i musi zwracać również typ memo. Funkcja pobiera tylko jeden parametr zawierający numer wiersza, którego notatka ma dotyczyć.
  • Funkcja 'GetBitmap' będzie zwracała nazwę pliku zawierającego bitmapę. Jako parametr należy przekazać funkcji numer wiersza, którego bitmapa ma dotyczyć.
  • Zmienna 'MemoCount' będzie zawierała liczbę wierszy w notatce.
  • Zmienna 'JestNotatka' jest typu bool i będzie zawierała tylko dwie wartości, jeśli wiersz będzie zawierał notatkę zmienna zostanie ustawiona na true, w przeciwnym razie zostanie ustawiona na false.
  • Funkcja 'ObliczIleKolumn' służy do obliczania ile kolumn zawiera baza danych. Funkcja dostępna jest tylko wewnątrz klasy, jako parametr pobiera treść wiersza ze wszystkich kolumn.
  • Funkcja 'ObliczIleWierszy' posłuży do obliczania ile wierszy zawiera baza danych.
  • Funkcja 'SetColSize' posłuży do zapisywania w bazie danych szerokości kolumn obiektu StingGrid1.
  • Funkcja 'GetColSize' posłuży do pobierania z bazy danych i ustawiania szerokości kolumn obiektu StringGrid1.
  • Funkcja 'GetMemoSection' jest funkcją pomocniczą sprawdzającą gdzie w bazie danych kończy się sekcja zawierająca wartości z tabeli i gdzie zaczyna się sekcja zawierająca notatki oraz nazwy plików zawierających bitmapy.
  • Funkcja 'WierszeMemo' jest funkcją obliczającą ile wierszy zawiera notatka.
  • Obiekt 'Memo' posłuży do przechowywania notatek w pamięci.
  • Obiekt 'Bitmap' posłuży do przechowywania w pamięci nazw plików zawierających bitmapy.

       krok 4. - tworzymy definicję klasy TDataBase i funkcji które klasa zawiera.
      Po utworzeniu deklaracji należy utworzyć definicje. W tym celu przechodzimy do pliku klasa.cpp. Definicje są bardzo złożone i dokładny opis wszystkich zająłby dużo miejsca i czasu, a i tak niewiele by wyjaśnił, dlatego jeśli kogoś interesuje jak działają poszczególne funkcje to pozostaje tylko ich dokładna analiza. Wszystkie funkcje umieściłem w pliku tekstowym i ci z was, którym się nie chce mogą po prostu przekopiować zawartość pliku i wszystko powinno prawidłowo działać. Proponuję jednak przepisywanie definicji funkcji jedna po drugiej, to pozwoli przynajmniej w ogólnym zarysie zorientować się jak działają.
    Klasa i funkcje zostały tak stworzone, żeby zawierały tylko procedury zrozumiałe już dla początkujących programistów, w związku z tym rozwiązania które zastosowałem nie zawsze są najlepsze, starałem się jednak nie komplikować wszystkiego bardziej niż to konieczne.
    Po utworzeniu definicji klasa TDataBase jest gotowa do wykorzystania w naszym projekcie.

       krok 5. - umieszczamy komponenty na formularzu
      Teraz gdy klasa TDataBase jest już gotowa przechodzimy do obiektu 'Form1' i umieszczamy na nim osiem komponentów: MainMenu, PopupMenu, SaveDialog, dwa komponenty OpenDialog, StringGrid, Memo, Image. Niżej umieszczam opis właściwości, które należy zmienić w poszczególnych obiektach:

  • MainMenu1
     Klikamy dwukrotnie na obiekcie, wyskoczy okienko Form1->MainMenu1, tworzymy menu według wzoru:
       Plik | Nowy | Otwórz | Zapisz | Zapisz Jako
       Edytuj | Dodaj Wiersz | Usuń Wiersz | Dodaj Kolumnę | Usuń Kolumnę | Nagłówki Kolumn | Dodaj Notatkę

  • PopupMenu1
     Klikamy dwukrotnie na obiekcie, wyskoczy okienko Form1->PopupMenu1, tworzymy tylko jedną pozycją menu: Dodaj Bitmapę.

  • SaveDialog1
     DefaultExt -> dbf
     Filter -> Filter Name: Pliki data base format (*.dbf), - Filter:  *.dbf

  • OpenDialog1
     DefaultExt -> dbf
     Filter -> Filter Name: Pliki data base format (*.dbf) | Wszystkie pliki (*.*), - Filter: *.dbf | *.*

  • OpenDialog2
     DefaultExt -> bmp
     Filter -> Filter Name: Bitmapy (*.bmp), - Filter: *.bmp
     Name -> PictureDialog1

  • StringGrid1
     ColCount -> 2
     FixedCols -> 0
     Height -> 405
     Left -> 8
     Options:
            goColSizing -> true
            goEditing -> true
            goAlwaysShowEditor -> true
     PopupMenu -> PopumMenu1
     Top -> 15
     Width -> 350

  • Memo1
     DragMode -> dmAutomatic
     Height -> 140
     Left -> 370
     ReadOnly -> true
     ScrollBars -> ssVertical
     Top -> 15
     WantReturns -> true
     Width -> 255
     WordWrap -> true

  • Image1
     Height -> 255
     Left -> 370
     Stretch -> true
     Top -> 165
     Width -> 255

  • Form1
     ActiveControl -> StringGrid1
     BorderStyle -> bsSingle
     Height -> 480
     Menu -> MainMenu1
     Position -> poScreenCenter
     Width -> 640

       krok 6. - tworzymy okno dodawania nagłówków
      Teraz utworzymy okno dialogowe, które będzie dodawało nagłówki w tabeli StringGrid1. W tym celu w menu wybieramy File | New Form, zostanie utworzony nowy formularz - Form2 i jednostaka Unit2. Zapisujemy wszystko, a następnie umieszczamy na obiekcie Form2 komponenty: StringGrid1, Button1, Button2. Ustawiamy właściwości komponentów według wzoru:

  • StringGrid1 - dotyczy obiektu StringGrid1 znajdującego się na formularzu Form2.
     ColCount -> 1
     DefaultColWidth -> 270
     FixedCols -> 0
     FixedRows -> 0
     Height -> 120
     Left -> 8
     Options:
            goEditing -> true
            goAlwaysShowEditor -> true
     RowCount - 1
     Top -> 24
     Width -> 296

  • Button1
     Caption -> OK
     Left -> 8
     Top -> 150

  • Button2
     Caption -> Zamknij
     Left -> 230
     Top -> 150

  • Form2  BorderStyle -> bsDialog
     Caption -> Nagłówki
     Height -> 211
     Left -> 252
     Position -> poScreenCenter
     Width -> 320

      Po rozmieszczeniu komponentów utworzymy procedury obsługi zdarzeń, ale najpierw w sekcji include pliku Unit2.cpp umieszczamy odwołanie do pliku Unit1.h.
    Przykład:

    // Plik źródłowy Unit2.cpp
    //--------------------------------
    #include <vcl.h>
    #pragma hdrstop

    #include "Unit2.h"
    #include "Unit1.h"
     // Dołączany plik Unit1.h
    //--------------------------------


    A teraz tworzymy procedury obsługi zdarzeń. Dla obiektu 'Form2' w zdarzeniu 'OnShow' umieszczamy następującą procedurę:
    Przykład:

    // Plik źródłowy Unit2.cpp
    //--------------------------------
    void __fastcall TForm2::FormShow(TObject *Sender)
    {
    StringGrid1->RowCount = Form1->StringGrid1->ColCount;
      for(int i = 0; i < Form1->StringGrid1->ColCount; i++)
          {
            StringGrid1->Cells[0][i] = Form1->StringGrid1->Cells[i][0];
          }
    }
    //--------------------------------


    Ta procedura w momencie wywołania obiektu Form2 sprawdza zawartość nagłówków tabeli StringGrid1 znajdującej się na Form1 i przepisuje je do tabeli StringGrid1 znajdującej się na Form2.
      Następnie klikamy dwukrotnie na obiekcie 'Button1' i w powstałym zdarzeniu 'OnClick' umieszczamy procedurę, która przepisze zawartość wierszy obiektu StringGrid1 znajdującego się na Form2 do nagłówków obiektu StringGrid1 znajdującego się na Form1.
    Przykład:

    // Plik źródłowy Unit2.cpp
    //--------------------------------
    void __fastcall TForm2::Button1Click(TObject *Sender)
    {
     for(int i = 0; i < Form1->StringGrid1->ColCount; i++)
          {
            Form1->StringGrid1->Cells[i][0] = StringGrid1->Cells[0][i];
          }

     Close();
    }
    //--------------------------------


    Pozostało już tylko dodanie procedury obsługi zdarzenia 'OnClick' dla obiektu 'Button2'. Zadaniem tego przycisku jest tylko zamykanie okna dialogowego Form2 bez zapisywania wprowadzonych zmian, dlatego zawiera on tylko prosty kod zamykający.
    Przykład:

    // Plik źródłowy Unit2.cpp
    //--------------------------------
    void __fastcall TForm2::Button2Click(TObject *Sender)
    {
      Close();
    }
    //--------------------------------


      Okno dialogowe zostało utworzone i nie będziemy już więcej do niego wracać.

       krok 7. - tworzymy okno dialogowe służące do dodawania notatek
      Teraz utworzymy okno dialogowe, które będzie dodawało notatki do wierszy w tabeli StringGrid1 na formularzu Form1. W tym celu w menu wybieramy File | New Form, zostanie utworzony nowy obiekt 'Form3' i jednostka 'Unit3'. Zapisujemy wszystko, a następnie umieszczamy na formularzu komponenty: ComboBox1, Memo1, Button1, Button2. Ustawiamy właściwości komponentów według podanego wzoru:

  • ComboBox1
     Height -> 21
     Left -> 8
     Sorted -> false
     Text -> (to pole pozostawiamy puste, bez żadnego wpisu)
     Top -> 30
     Width -> 80

  • Memo1
     Height -> 140
     Left -> 8
     MaxLength -> 1000 - (ogranicza liczbę wpisanych znaków do tysiąca)
     ScrollBars -> ssVertical
     Top -> 60
     WantReturns -> true
     Width -> 255
     WordWrap -> true

  • Button1
     Caption -> Dodaj
     Left -> 8
     Top -> 210

  • Button2
     Caption -> Zamknij
     Left -> 190
     Top -> 210

  • Form3
     BorderStyle -> bsDialog
     Caption -> Dodaj opis
     Height -> 272
     Position -> poScreenCenter
     Width -> 278

      Teraz w sekcji include pliku Unit3.cpp umieszczamy odwołanie do pliku Unit1.h.
    Przykład:

    // Plik źródłowy Unit3.cpp
    //--------------------------------
    #include <vcl.h>
    #pragma hdrstop

    #include "Unit3.h"
    #include "Unit1.h"
     // Dołączany plik Unit1.h
    //--------------------------------


    Następnie umieszczamy procedury obsługi zdarzeń. Dla 'Form3' w zdarzeniu 'OnShow' umieszczamy procedurę indeksującą wiersze tabeli StringGrid1 znajdującej się na Form2 i zapisujące je do obiektu ComboBox1. Poźniej w obiekcie ComboBox1 będzie można wybrać numer wiersza, którego ma dotyczyć dodawana notatka.
    Przykład:

    // Plik źródłowy Unit3.cpp
    //--------------------------------
    void __fastcall TForm3::FormShow(TObject *Sender)
    {
     for(int i = 1; i < Form1->StringGrid1->RowCount; i++)
          {
            ComboBox1->Items->Add(IntToStr(i));
          }
    }
    //--------------------------------


    Potem dołączamy procedurę obsługi zdarzenia 'OnClick' dla komponentu 'Button1'. W tym zdarzeniu treść notatki z obiektu 'Memo1' zostanie przypisana do wiersza obiektu 'StringGrid1' znajdującego się na formularzu 'Form1'. Wybór wiersza zostaje dokonany w komponencie ComboBox1.
    Przykład:

    // Plik źródłowy Unit3.cpp
    //--------------------------------
    void __fastcall TForm3::Button1Click(TObject *Sender)
    {
     if(ComboBox1->ItemIndex > -1)
       {
         String temp = "|";
     for(int i = 0; i < Memo1->Lines->Count; i++)
        {
           temp = temp + Memo1->Lines->Strings[i] + "|";

        }
     temp.Delete(temp.Length(), 1);
     Form1->Baza->SetMemo(temp, ComboBox1->ItemIndex + 1);
     Form1->Memo1->Text = Memo1->Text;
     Close();
       }
    else
       {
            ShowMessage("Nie wybrano numeru wiersza, którego ma dotyczyć notatka");
       }
    }
    //--------------------------------


    Pozostało już tylko dołączenie procedury obsługi zdarzenia 'OnClick' dla komponentu 'Button2'.
    Przykład:

    // Plik źródłowy Unit3.cpp
    //--------------------------------
    void __fastcall TForm3::Button2Click(TObject *Sender)
    {
       Close();
    }
    //--------------------------------


    Okno dialogowe jest już gotowe i nie będziemy więcej do niego wracać.

       krok 8. - wykańczanie programu
       W piątym kroku umieściliśmy na formularzu głównym - Form1 komponenty, teraz pozostało już tylko dołączyć obsługę menu i scalić wszystko. W tym celu przechodzimy do pliku nagłówkowego 'Unit1.h' i w sekcji include umieszczamy odwołanie do pliku 'Klasa.h' zawierającego klasę 'TDataBase'.
    Przykład:

    // Plik nagłówkowy Unit1.h.
    //--------------------------------
    #ifndef Unit1H
    #define Unit1H

    //--------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    #include <Grids.hpp>
    #include <Menus.hpp>
    #include <ExtCtrls.hpp>
    #include <Dialogs.hpp>
    #include "Klasa.h"
     // Włączany plik Klasa.h
    //--------------------------------


    Następnie w tym samym pliku (Unit1.h) przechodzimy do sekcji private i deklarujemy nową zmienną typu String o nazwie FileSaveName (nazwa jest dowolna). Ta zmienna posłuży do tymczasowego przechowywania nazwy pliku bazy danych.
    Jak to będzie działało?
    Otórz po wybraniu w menu Plik | Otwórz otworzy się okno dialogowe pozwalające wybrać blik bazy danych. Po otwarciu nazwa i ścieżka dostępu do tegoż pliku zostaną zapamiętane właśnie w zmiennej FileSaveName, gdy wybierzemy menu Plik | Zapisz aktualnie otwarta baza danych zostanie zapisana do pliku zapamiętanego w zmiennej, natomiast jeśli wybierzemy menu Plik | Nowy zmienna FileSaveName zostanie "opróżniona" z nazwy pliku i jeśli teraz wybierzemy menu Plik | Zapisz to otworzy się okno dialogowe umożliwiające zapisanie bazy danych w nowym miejscu i pod nową nazwą, podobnie jak menu Plik | Zapisz Jako, które zawsze wywoła okno dialogowe zapisywania do pliku. Tak więc zmienna FileSaveName działa jako "przełącznik".
      Teraz przechodzimy do sekcji public i deklarujemy obiekt typu TDataBase o nazwie 'Baza', który będzie zarządzał całą bazą danych.
    Przykład:

    // Plik nagłówkowy Unit1.h.
    //--------------------------------
    private:
     String FileSaveName;

    public:
    TDataBase *Baza;

              __fastcall TForm1(TComponent* Owner);
    //--------------------------------


    Następnie przechodzimy do pliku źródłowego 'Unit1.cpp' i w sekcji include umieszczamy odwołanie do plików 'Unit2.h' i 'Unit3.h'.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    #include <vcl.h>
    #pragma hdrstop

    #include "Unit1.h"
    #include "Unit2.h" // Włączany plik Unit2.h
    #include "Unit3.h"
     //Włączany plik Unit3.h
    //--------------------------------


    Wcześniej w pliku Unit1.h zadeklarowaliśmy nowy obiekt typu TDataBase o nazwie Baza, więc teraz trzeba zadeklarować wskaźnik do tej klasy i stworzyć na stercie (sterta - blok pamięci) egzemplarz obiektu TDataBase. Spowoduje to wywołanie konstruktora klasy TDataBase i zarezerwowanie pamięci dla tworzonego obiektu Baza.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------

    TForm1 *Form1;
    //--------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
      Baza = new TDataBase();
      FileSaveName = "";
    }
    //--------------------------------


    Teraz klikamy w menu Edytuj | Dodaj Wiersz i umieszczamy kod odpowiedzialny za dodawanie wierszy w tabeli StringGrid1.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::Dodajwiersz1Click(TObject *Sender)
    {
      StringGrid1->RowCount++;
    }
    //--------------------------------


    ...klikamy w menu Edytuj | Usuń Wiersz i umieszczamy kod odpowiedzielny za usuwanie wiersza z tabeli StringGrid1.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::Usunwiersz1Click(TObject *Sender)
    {
      StringGrid1->RowCount--;
    }
    //--------------------------------


    ...klikamy w menu Edytuj | Dodaj Kolumnę i umieszczamy kod, który będzie dodawał kolumnę w tabeli StringGrid1.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::Dodajkolumn1Click(TObject *Sender)
    {
      StringGrid1->ColCount++;
    }
    //--------------------------------


    ...klikamy w menu Edytuj | Usuń Kolumnę i umieszczamy kod usuwający kolumnę z tabeli StringGrid1.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::Usukolumn1Click(TObject *Sender)
    {
      StringGrid1->ColCount--;
    }
    //--------------------------------


    ...klikamy w menu Edytuj | Nagłówki Kolumn i dodajemy kod, który będzie wywoływał okno dialogowe Form2 odpowiedzialne za dodawanie nagłówków do kolumn w tabeli StringGrid1.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::NagwkiKolumn1Click(TObject *Sender)
    {
      Form2->ShowModal();
    }
    //--------------------------------


    ...klikamy w menu Edytuj | Dodaj Notatkę i umieszczamy kod odpowiedzialny za wywołanie okna dialogowego Form3 zadaniem którego jest dodawanie notatek do wybranych wierszy w tabeli StringGrid1.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::DodajNotatk1Click(TObject *Sender)
    {
      Form3->ShowModal();
    }
    //--------------------------------


    ...klikamy dwukrotnie na komponęcie 'PopupMenu1', wyskoczy okno Form1->PopupMenu1. Klikamy w menu Dodaj Bitmapę i umieszczamy kod, który będzie dołączał do wybranych wierszy w tabeli StringGrid odwołania do plików graficznych.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::DodajBitmap1Click(TObject *Sender)
    {
      if(PictureDialog1->Execute())
          {
            Baza->SetBitmap(PictureDialog1->FileName, StringGrid1->Row);
            Image1->Picture->LoadFromFile(PictureDialog1->FileName);
          }
    }
    //--------------------------------


    ...klikamy w menu Plik | Nowy i dodajemy kod odpowiedzialny za tworzenie nowej bazy danych.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::Nowy1Click(TObject *Sender)
    {
      Baza->RefreshBase();
      Image1->Picture = NULL;
      for(int i = 0; i < StringGrid1->RowCount; i++){
        for(int j = 0; j < StringGrid1->ColCount; j++){
          StringGrid1->Cells[j][i] = "";
                                   }
                                     }

      StringGrid1->RowCount = 2;
      StringGrid1->ColCount = 2;
      FileSaveName = "";
    }
    //--------------------------------


    ...klikamy w menu Plik | Otwórz i dodajemy kod odpowiedzialny za otwieranie baz danych.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::Otwrz1Click(TObject *Sender)
    {
      if(OpenDialog1->Execute())
        {
          Baza->RefreshBase();
          Baza->LoadFromFile(OpenDialog1->FileName, StringGrid1);
          FileSaveName = OpenDialog1->FileName;
          StringGrid1->Col = (StringGrid1->Col == 0) ? 1 : 0;
        }
    }
    //--------------------------------


    ...klikamy w menu Plik | Zapisz i dodajemy kod, który będzie zapisywał zmiany dokonane w bazie danych.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::Zapisz1Click(TObject *Sender)
    {
      if(FileSaveName.IsEmpty())
        {
          if(SaveDialog1->Execute())
            Baza->SaveToFile(SaveDialog1->FileName, StringGrid1);
        }
      else Baza->SaveToFile(FileSaveName, StringGrid1);
      FileSaveName = SaveDialog1->FileName;
    }
    //--------------------------------


    ...klikamy w menu Plik | Zapisz Jako i umieszczamy kod, który będzie umożliwiał zapisanie bazy danych pod nową nazwą i w nowej lokalizacji.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::SaveAsClick(TObject *Sender)
    {
      if(SaveDialog1->Execute())
        Baza->SaveToFile(SaveDialog1->FileName, StringGrid1);

      FileSaveName = SaveDialog1->FileName;
    }
    //--------------------------------


    Teraz przechodzimy do komponentu 'StringGrid1' i w zdarzeniu 'OnSelectCell' umieszczamy kod, który będzie wyświetlał zawartość notatki i plik graficzny odpowiadające wybranemu wierszowi w tabeli, oczywiście tylko wtedy gdy do danego wiersza zostanie wcześniej przypisana notatka lub bitmapa.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::StringGrid1SelectCell(TObject *Sender, int ACol,
              int ARow, bool &CanSelect)
    {
      Memo1->Clear();
      memo temp; //definicja struktury memo
      temp = Baza->GetMemo(ARow);
      if(Baza->JestNotatka)
        {
          for(int i = 0; i < Baza->MemoCount; i++){
            if(!temp.A[Baza->MemoCount - i - 1].IsEmpty())
             Memo1->Lines->Add(temp.A[Baza->MemoCount - i - 1]);
                  }
        }

      Image1->Picture = NULL;
      try{Image1->Picture->LoadFromFile(Baza->GetBitmap(ARow));}
      catch(...){;}
    }
    //--------------------------------


    Uwaga jeżeli w nagłówku zdarzenia OnSelectCell są zadeklarowane zmienne typu int o nazwach Row i Col, a nie ARow i ACol to umieszczając kod wewnątrz zdarzenia należy odwoływać się do takich zmiennych jakie zostały zadeklarowane czyli Row i Col. Zwracam na to uwagę ponieważ nazwy zadeklarowanych zmiennych mogą być różne w zależności od wersji BCB.
      No i na koniec w zdarzeniu formularza - 'Form1' - 'OnClose', dodajemy kod, który w momencie zamknięcia będzie usuwał obiekt Baza z pamięci.
    Przykład:

    // Plik źródłowy Unit1.cpp
    //--------------------------------
    void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
    {
      delete Baza;
    }
    //--------------------------------


    ...i to już cały kod, chyba że o czymś zapomniałem.

       krok 9. - zakończenie
      Na zakończenie pozostaje już tylko opisać jak działa program. Tak więc menu Nowy - tworzy nową bazę danych, menu Otwórz - otwiera istniejącą bazę danych, menu Zapisz - zapisuje zmiany w otwartej bazie danych, menu Zapisz Jako - pozwala zapisać nową lub otwartą bazę danych pod nową nazwą, menu Edycja - nie wymaga tłumaczenia.
      Notatki i bitmapy będą ładowane do obiektu Memo1 dopiero po wybraniu wiersza w tabeli StringGrid, pod warunkiem, że do wybranego wiersza zostanie wcześniej przypisana jakaś notatka, podobnie ma się sprawa z grafiką. Muszę wyjaśnić jednak pewną sprawę, a mianowicie zawartość tabeli i notatek jest zapisywana w jednym pliku bazy danych, natomiast grafika NIE, w bazie danych zostaje zapisana tylko nazwa i ścieżka dostępu do pliku graficznego. Można by oczywiście zrobić tak, że wszystkie dane czyli zawartość tabeli, notatki i bitmapy byłyby zapisane w jednym pliku. Takie rozwiązanie nie zawsze jest jednak najlepsze, ponieważ bitmapy zajmują bardzo dużo miejsca, a co za tym idzie plik bazy danych byłby bardzo duży w przypadku wielu plików graficznych. Wielkość pliku odgrywa znaczącą rolę, dlatego że im większy jest plik, tym dłużej się ładuje do pamięci i tym więcej miejsca zajmuje w pamięci RAM.
      Program jest bardzo prosty, nie zależało mi jednak na tworzeniu skomplikowanej bazy danych, lecz na pokazaniu jak w sposób prosty i szybki można stworzyć funkcjonalną bazę danych. Plik klasa.cpp zawierający klasę TDataBase może być wykorzystywany w innych programach, wystarczy skopiować go wraz z plikiem klasa.h do folderu w którym znajduje się projekt i dołączyć go do projektu dodając wpis w sekcji include.
      Pytania proszę kierować na skrzynkę pocztową cyfrowy.baron@wp.pl. Nie biorę żadnej odpowiedialności za błędy i szkody jakie może spowodować korzystanie z kursu, plików i programów w nim zamieszczonych.

    Przygotował: Cyfrowy Baron.



    Rozmiar: 407 bajtów ...program: DataBase.exe    Rozmiar: 298 bajtów ...pliki żródłowe (BCB4)