Własne okna dialogowe
flowCRANE
Wstęp
Nie raz pewnie podczas pisania programu chcemy, by wygląd całego GUI aplikacji był unikalny. Tworzymy różnego rodzaju kontrolki, wyposażamy je w ręczne rysowanie np. item'ów (jak w komponentach z klasy TListBox, TComboBox, TMainMenu itd.), dodajemy własną grafikę. Interefejs programu zaczyna wyglądać efektownie, lecz pojawia się problem - standardowe okna dialogowe nie pasują wyglądem do naszego programu lub chcemy, by wyposażone zostały we więcej komponentów, żeby miały większe możliwości; Co wtedy? Trzeba stworzyć własne okno dialogowe i nadać mu wygląd, jakiego nie ma możliwości uzyskać wykorzystując wbudowane. Ale jak to zrobić...?
Ten artykuł ma na celu przedstawienie sposobu tworzenia własnych okien dialogowych, które pod każdym względem będziemy mogli dostosować do naszej aplikacji. Nie będą to jedynie proste okienka zwracające jedynie wartość modalną okna (jak mrYes, mrNo, mrYesToAll, mrCancel itd.) ale także takie, które pozwalają zwrócić np. całą strukturę danych.
Artykuł ten kierowany jest do początkujących programistów borykających się z tym problemem. Zachęcam serdecznie wszystkich do przeczytania poniższej treści.
1 Wstęp
2 Proste okno dialogowe - InfoBox
2.1 Formularz
2.2 Komponenty
2.3 Zestaw ikon informacyjnych
2.4 Zwalnianie okna z pamięci
2.5 Funkcja dialogu
3 Okna zwracające wartość niemodalną
4 Własny FontDialog
4.6 Formularz i kontrolki
4.7 Ikona czcionki i rozmiaru
4.8 Tworzenie formularza
4.9 Usuwanie formularza z pamięci
4.10 Własny wygląd kontrolek
4.11 Modyfikowanie tekstu podglądu czcionki
4.12 Klasa dialogu
4.12.1 Konstruktor klasy
4.12.2 Własna procedura zamknięcia formularza
4.12.3 Metoda Execute
4.13 Wykorzystanie klasy dialogu
4.14 Funkcja wykorzystująca klasę dialogu
5 Najważniejsze informacje
6 Zakończenie
Proste okno dialogowe - InfoBox
Formularz
W pierwszej kolejności należy zaprojektować formularz. Projektowanie rozpoczynamy od ustawienia właściwości samego formularza. Ustalamy właściwości okna po kolei:
Właściwość | Wartość |
---|---|
BorderIcons | [biSystemMenu] |
BorderStyle | bsSingle |
Caption | '' |
Cursor | crArrow |
Height | 163 |
Icon | wybieramy ikonę swojego programu o wymiarach 16x16 pikseli (w przykładzie - koło zębate) |
KeyPreview | True |
Name | InfoBoxForm |
Position | poOwnerFormCenter |
Width | 457 |
Jeśli chodzi o formularz - mamy wszystko przygotowane. |
Komponenty
Nasze okienko wyposażymy w kilka kontrolek. Będą potrzebne:
Identyfikator | Klasa komponentu | Opis |
---|---|---|
imgIcon | TIcon |
ikona typu wiadomości (pytanie, informacja, ostrzeżenie, błąd) |
lblText | TLabel |
przedstawiona będzie w nim treść wiadomości |
btnYes | TButton |
przycisk Tak |
btnNo | TButton |
przycisk Nie |
btnOk | TButton |
przycisk Ok |
Komponenty ustawiamy według własnego uznania, ja proponuję tak, jak jest to pokazane na poniższym rysunku: |
Na rysunku nie widać przycisku btnOk ponieważ jest on schowany pod btnNo. Do dyspozycji będziemy mieli dwie możliwości ustawień przycisków: Tak i Nie lub Ok, stąd ten ostatni będzie wyświetlany w miejscu przycisku btnNo.
Zestaw ikon informacyjnych
Na formularzu mamy komponent o identyfikatorze imgIcon
, który będzie służyć do wyświetlania obrazu odpowiedniego dla prezentowanej informacji. Musimy przygotować sobie zestaw ikon, potrzebne będą: zapytania (pytajnik), informacji (żarówka), ostrzeżenia (wykrzyknik) oraz błędu (krzyżyk). Poniżej przykładowy zestaw ikonek:
Ikony trzeba przygotować do odpowiedniego rozmiaru. W naszym okienku komponent imgIcon ma rozmiary 48x48 px
, stąd każda ikona powinna mieć dokładnie taką samą wielkość. Teraz musimy utworzyć sobie plik zasobu, w którym umieścimy wszystkie ikonki. Ustalamy je w kolejności podanej wyżej:
ID | Opis ikony |
---|---|
INFO_BOX_ICON_0 | ikona zapytania |
INFO_BOX_ICON_1 | ikona informacji |
INFO_BOX_ICON_2 | ikona ostrzeżenia |
INFO_BOX_ICON_3 | ikona błędu |
Po utworzeniu pliku zasobu InfoBoxIcons.res dodajemy go do programu wpisując odpowiednią linijkę w kodzie modułu: |
implementation
{$R *.dfm}
{$R InfoBoxIcons.res}
{...}
Zwalnianie okna z pamięci
Jedyne co musimy oprogramować to zwalnianie okna z pamięci podczas jego zamknięcia. Uzupełniamy więc zdarzenie OnClose
formularza w poniższe dwie linijki:
procedure TInfoBoxForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
InfoBoxForm := nil;
end;
Dzięki temu okno po każdorazowym użyciu będzie zwalniane z pamięci i unikniemy późniejszych niepotrzebnych błędów.
Funkcja dialogu
Samo okienko do niczego się jeszcze nie nadaje - można je wyświelić, kliknąć w jakiś przycisk. Wartość zostanie zwrócona, ale nic jej jeszcze nie przechwytuje. Potrzebujemy więc funkcję, która wykorzysta zaprojektowane wcześniej okienko i przechwyci zwróconą przez nie wartość na podstawie wciśniętego przycisku.
W pierwszej kolejności musimy stworzyć sobie kilka stałych oraz typów, które będą nam służyć do rozróżniania pewnych trybów okienka, jak np. zbiór przycisków, typ ikony informacyjnej, aktywny przycisk:
{ TYP IKONY INFORMACYJNEJ }
const
itQuestion = 'INFO_BOX_ICON_0';
itInformation = 'INFO_BOX_ICON_1';
itWarning = 'INFO_BOX_ICON_2';
itStop = 'INFO_BOX_ICON_3';
type
TInfoIconType = type String;
{ ZBIÓR PRZYCISKÓW }
const
bsOk = 0;
bsYesNo = 1;
type
TButtonsSet = bsOk .. bsYesNo;
{ AKTYWNY PRZYCISK }
const
abOk = 'btnOk';
abYes = 'btnYes';
abNo = 'btnNo';
type
TActiveButton = type String;
Mamy już przygotowane wszystko, co będzie potrzebne do napisania funkcji. Musimy wykonać kilka kroków uzupełniających nasze okienko w odpowiednie informacje:
- utworzyć formularz,
- ustalić jego tytuł (Caption),
- ustalić treść wiadomości (lblText.Caption),
- załadować obrazek (imgIcon.Picture),
- pokazać odpowiednie przyciski,
- ustalić aktywny przycisk,
- pokazać okienko,
- przechwycić wartość modalną.
Przykładowa funkcja realizująca to zadanie:
function InfoBox(Owner: TComponent; const Title, Text: String; IconType: TInfoIconType;
ButtonsSet: TButtonsSet; ActiveButton: TActiveButton): TModalResult;
var
InfoBoxForm: TInfoBoxForm;
rsIcon: TResourceStream;
begin
InfoBoxForm := TInfoBoxForm.Create(Owner);
try
with InfoBoxForm do
begin
{ TYTUŁ OKNA }
Caption := Title;
{ TREŚĆ WIADOMOŚCI }
lblText.Caption := Text;
{ IKONA }
rsIcon := TResourceStream.Create(hInstance, IconType, 'RT_ICON');
try
imgIcon.Picture.Icon.LoadFromStream(rsIcon);
finally
rsIcon.Free();
end;
{ ZBIÓR PRZYCISKÓW }
case ButtonsSet of
bsOk: btnOk.Show();
bsYesNo: begin
btnYes.Show();
btnNo.Show();
end;
end;
{ AKTYWNY PRZYCISK }
ActiveControl := TButton(FindComponent(ActiveButton));
{ WYWOŁANIE OKIENKA I PRZECHWYCENIE WARTOŚCI MODALNEJ }
Result := ShowModal();
end;
finally
InfoBoxForm.Free();
end;
end;
Przykładowe wywołanie okienka ostrzegającego użytkownika przed utratą danych:
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
CanClose := InfoBox(Self, 'Wyjśćie z programu',
'Wyjście z programu grozi bezpowrotną utratą danych.'#10#10 +
'Czy na pewno chcesz zakończyć działanie aplikacji bez zapisu?',
itWarning, bsYesNo, abYes) = mrYes;
end;
czego rezultatem jest pokazanie następującego okienka:
Inny przykład - wyświetlenie informacji o pomyślnym zapisie ustawień programu do pliku:
InfoBox(Self, 'Zapis ustawień', 'Ustawienia zostały pomyślnie zapisane do pliku "' + FileName + '".',
itInformation, bsOk, abOk);
po wykonaniu powyższej linii zostanie wyświetlone okienko:
Okna zwracające wartość niemodalną
Pierwsza część artykułu przedstawiała sposób na utworzenie okna dialogowego, które zwraca wartość modalną. Przy tworzeniu takiego dialogu nie musimy wiele pracować, ponieważ jego obsługa ogranicza się jedynie do przechwycenia wartości zwracanej przez przyciski.
W kolejnej części zaprezentuję sposób tworzenia okna dialogowego zwracającego dane dowolnego typu. Nie będzie to jednak tak proste jak w poprzednim przykładzie - musimy zaprojektować okno, oprogramować jego poszczególne elementy po czym stworzyć klasę, która będzie wykorzystywać nasz formularz i pobierać informacje z różnych komponentów umieszczonych w okienku.
Własny FontDialog
Formularz i kontrolki
W pierwszej kolejności musimy zaprojektować formularz. Kładziemy na nim kilka kontrolek określających różne właściwości czcionki. Przykładowe okienko zaprezentowane jest na poniższym rysunku:
Opis kontrolek umieszczonych na formularzu:
Nazwa | Klasa komponentu | Identyfikator kontrolki | Opis |
---|---|---|---|
Nazwa czcionki | TComboBox |
cbFontName | lista wszystkich destępnych czcionek w systemie |
Rozmiar | TComboBox |
cbFontSize | lista standardowych rozmiarów czcionki |
pogrubienie | TCheckBox |
cbBold | czcionka pogrubiona |
kursywa | TCheckBox |
cbItalic | czcionka pochylona |
podkreślenie | TCheckBox |
cbUnderline | czcionka podkreślona |
przekreślenie | TCheckBox |
cbStrikeOut | czcionka przekreślona |
Podgląd | TPanel |
pnlPreview | podgląd ustawień czcionki |
Zapisz ustawienia | TButton |
btnSave | przycisk do zapisu ustawień |
Ikona czcionki i rozmiaru
Do przedstawienia w bardziej ciekawy sposób kroju czcionki oraz rozmiaru potrzebować będziemy dwóch ikonek. Ja dla przykładu wybrałem poniższe:
Ikona nazwy czcionki ma rozmiary 24x24
piksele, a rozmiaru 16x16
pikseli. Musimy teraz utworzyć plik zasobów, w którym umieszczone zostaną powyższe ikony:
ID | Opis |
---|---|
FONT_ICON_0 | ikona nazwy czcionki |
FONT_ICON_1 | ikona rozmiaru czcionki |
Przygotowany plik zasobów FontIcons.res dodajemy do listy zasobów modułu:
implementation
{$R *.dfm}
{$R FontIcons.res}
Tworzenie formularza
W pierwszej kolejności musimy załadować ikony z zasobów aplikacji. Tworzymy prostą macierz o statycznym rozmiarze:
type
TFontForm = class(TForm)
{...}
private
aFontIcons: array [0 .. 1] of TIcon;
end;
i ładujemy do niej ikonki w zdarzeniu OnCreate formularza:
procedure TFontForm.FormCreate(Sender: TObject);
var
rsIcon: TResourceStream;
I: Byte;
begin
for I := 0 to 1 do
begin
rsIcon := TResourceStream.Create(hInstance, 'FONT_ICON_' + IntToStr(I), 'RT_ICON');
aFontIcons[I] := TIcon.Create();
aFontIcons[I].LoadFromStream(rsIcon);
rsIcon.Free();
end;
{...}
Macierz mamy uzupełnioną w ikonki, teraz trzeba załadować listę czcionek dostępnych w systemie do komponentu cbFontName:
{...}
cbFontName.Items.Assign(Screen.Fonts);
end;
Usuwanie formularza z pamięci
Podczas usuwania formularza musimy zwolnić pamięć zajmującą przez wczytane z zasobów ikonki do macierzy:
procedure TFontForm.FormDestroy(Sender: TObject);
var
I: Byte;
begin
for I := 0 to 1 do
aFontIcons[I].Free();
end;
Własny wygląd kontrolek
Oprogramowaliśmy już wczytywanie ikon do prywatnej macierzy formularza, teraz czas na napisanie własnych procedur OnDrawItem
dla komponentów cbFontName oraz cbFontSize. Procedury nie będą skomplikowane - wystarczy narysowanie tła, ikony oraz tekstu. W przypadku listy czcionek każda z nich będzie rysowana w swoim kroju.
Przykładowy kod zdarzenia OnDrawItem kontrolki cbFontName:
procedure TFontForm.cbFontNameDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
List: TComboBox;
iTextHeight: Word;
begin
List := TComboBox(Control);
with List.Canvas do
begin
{ USTALENIE KOLORÓW WEDŁUG ZAZNACZENIA }
case odSelected in State of
True: begin
Pen.Color := $009C5327;
Brush.Color := $00C56A31;
Font.Color := clWhite;
end;
False: begin
Pen.Color := clWhite;
Brush.Color := clWhite;
Font.Color := clBlack;
end;
end;
{ TŁO }
Rectangle(Rect);
{ IKONA }
Draw(Rect.Left + 2, Rect.Top + 2, aFontIcons[0]);
{ TREŚĆ }
Font.Name := List.Items[Index];
Font.Size := 10;
iTextHeight := TextHeight(List.Items[Index]);
TextOut(Rect.Left + 30, Rect.Top + 5 + ((28 - iTextHeight) mod 2), List.Items[Index]);
{ USUNIĘCIE STANDARDOWEJ RAMKI PODŚWIETLAJĄCEJ }
if odFocused in State then
begin
Pen.Color := Pen.Color xor $FFFFFF;
DrawFocusRect(Rect);
end;
end;
end;
Najpierw rozpoznajemy czy dany item jest podświetlony (wartość odSelected w zbiorze State) i ustalamy kolory dla ramki, tła oraz tekstu od razu. Następnie rysujemy tło motedą Rectangle(), po czym rysujemy ikonę pobraną z tablicy aFontIcons. Kolejnym krokiem będzie narysowanie tekstu (nazwy czcionki) w swoim kroju oraz usunięcie standardowej ramki. Teraz czas na oprogramowanie zdarzenia OnDrawItem kontrolki cbFontSize:
procedure TFontForm.cbFontSizeDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
List: TComboBox;
begin
List := TComboBox(Control);
with List.Canvas do
begin
{ USTALENIE KOLORÓW }
case odSelected in State of
True: begin
Pen.Color := $009C5327;
Brush.Color := $00C56A31;
Font.Color := clWhite;
end;
False: begin
Pen.Color := clWhite;
Brush.Color := clWhite;
Font.Color := clBlack;
end;
end;
{ TŁO }
Rectangle(Rect);
{ IKONA }
Draw(Rect.Left + 2, Rect.Top + 2, aFontIcons[1]);
{ TEKST }
TextOut(Rect.Left + 28, Rect.Top + 4, List.Items[Index]);
{ USUNIĘCIE STANDARDOWEJ RAMKI PODŚWIETLAJĄCEJ }
if odFocused in State then
begin
Pen.Color := Pen.Color xor $FFFFFF;
DrawFocusRect(Rect);
end;
end;
end;
Rysowanie mamy już za sobą. Powyższe algorytmy oczywiście można połączyć w jeden, który rozróżnia czy będzie rysowana czcionka czy jej rozmiar. Zaprezentowany sposób jednak jest prostrzy w zrozumieniu dla początkujących.
Rezultat zastosowania kodów zdarzeń OnDrawItem komponentów przedstawia poniższy rysunek:
Oczywiście to tylko przykładowy wygląd - można go ustalić dowolnie, według własnego uznania i potrzeb dla projektu.
Modyfikowanie tekstu podglądu czcionki
Skoro mamy już okienko podglądu z przykładowym tekstem - musimy podczas zmiany stanu kontrolek odpowiednio modyfikować tekst kontrolki pnlPreview. Do tego celu napiszemy sobie dwie procedury - jedna będzie ustalać krój czcionki i jej rozmiar, a druga atrybuty. Oprogramujemy jedynie dwa zdarzenia (odpowiednio dla kontrolek z klasy TComboBox oraz TCheckBox) i podepniemy je pod resztę kontrolek z tej samej klasy.
Najpierw zajmijmy się listami - zdarzenie OnChange kontrolki cbFontName:
procedure TFontForm.cbFontNameChange(Sender: TObject);
begin
with pnlPreview.Font do
begin
Name := cbFontName.Text;
Size := StrToInt(cbFontSize.Text);
end;
end;
To zdarzenie podpinamy pod OnChange kontrolki cbFontSize. Następnie trzeba oprogramować zdarzenie OnClick komponentu cbBold:
procedure TFontForm.cbBoldClick(Sender: TObject);
begin
with pnlPreview.Font do
begin
{ POGRUBIENIE }
case cbBold.Checked of
True: Style := Style + [fsBold];
False: Style := Style - [fsBold];
end;
{ KURSYWA }
case cbItalic.Checked of
True: Style := Style + [fsItalic];
False: Style := Style - [fsItalic];
end;
{ PODKREŚLENIE }
case cbUnderline.Checked of
True: Style := Style + [fsUnderline];
False: Style := Style - [fsUnderline];
end;
{ PRZEKREŚLENIE }
case cbStrikeOut.Checked of
True: Style := Style + [fsStrikeOut];
False: Style := Style - [fsStrikeOut];
end;
end;
end;
i podpinamy je pod wszystkie komponenty z klasy TCheckBox formularza.
Klasa dialogu
Formularz mamy już w całości oprogramowany - teraz trzeba napisać klasę, która będzie go wykorzystywać i pobierać z niego informacje do swoich pól. PIerwszym krokiem będzie ustalenie typu danych, jakie będzie przechowywał dialog - w tym przykładzie zadeklarujemy sobie rekord przechowyjący podstawowe informacje o czcionce:
type
TFontInfoRec = record
{ NAZWA CZCIONKI }
Name: TFontName;
{ ROZMIAR }
Size: Integer;
{ ATRYBUTY }
Style: TFontStyles;
end;
Następnie musimy napisać wspomnianą klasę. Przykładowa deklaracja takiej klasy:
type
TFontDialog = class(TObject)
private
FFontDialogForm: TFontForm;
FFontInfo: TFontInfoRec;
procedure DialogClose(Sender: TObject; var Action: TCloseAction);
public
constructor Create();
function Execute(Owner: TComponent; FontName: TFontName; FontSize: Integer;
FontStyle: TFontStyles): Boolean;
public
property Font: TFontInfoRec read FFontInfo;
end;
Opis wykorzystanych elementów w klasie:
Identyfikator | Opis |
---|---|
FFontDialogForm | prywatne pole - zaprojektowany wcześniej formularz |
FFontInfo | rekord do przechowania informacji o czcionce |
DialogClose | własna procedura zamknięcia okna - w niej odbywać się będzie przechwytywanie informacji |
Create | konstruktor klasy |
Execute | funkcja ta będzie tworzyć i pokazywać formularz |
Font | informacje o czcionce pobrane z okna |
Konstruktor klasy
Teraz trzeba oprogramować po kolei każdą metodę - rozpoczynamy od konstruktora klasy, w którym musimy utworzyć obiekt dialogu oraz zainicjować formularz:
constructor TFontDialog.Create();
begin
inherited Create();
FFontDialogForm := nil;
end;
Własna procedura zamknięcia formularza
Następnie kodujemy metodę CloseDialog - najpierw przepisujemy właściwości komponentu pnlPreview do pola FFontInfo, po czym zamykamy i nil'ujemy formularz:
procedure TFontDialog.DialogClose(Sender: TObject; var Action: TCloseAction);
begin
with FFontDialogForm do
begin
{ NAZWA CZCIONKI }
FFontInfo.Name := pnlPreview.Font.Name;
{ ROZMIAR }
FFontInfo.Size := pnlPreview.Font.Size;
{ ATRYBUTY }
FFontInfo.Style := pnlPreview.Font.Style;
end;
Action := caFree;
FFontDialogForm := nil;
end;
W tej właśnie procedurze przed zamknięciem formularza przechwytujemy interesujące nas informacje i wpisujemy je do prywatnych pól klasy dialogu. Jeśli tego nie zrobimy - formularz zostanie zamknięty a dane zostaną utracone bezpowrotnie.
Metoda Execute
Kolejnym krokiem jest oprogramowanie metody Execute. W pierwszej kolejności musimy sprawdzić czy formularz jest już utworzony i jeśli nie - utworzyć go. Następnie przypisujemy mu nową procedurę zamknięcia - DialogClose, po czym na podstawie podanych parametrów funkcji uzupełniamy pola rekordu FFontInfo - będą to wartości domyślne. W następnej kolejności musimy ustawić właściwości komponentów na formularzu także w oparciu o wartości argumentów funkcji. Ostatnią czynnością jest pokazanie okna i oczekiwanie na zwróconą wartość modalną, która wpisana zostanie do rezultatu funkcji.
Poniżej przykładowy kod funkcji Execute:
function TFontDialog.Execute(Owner: TComponent; FontName: TFontName; FontSize: Integer;
FontStyle: TFontStyles): Boolean;
var
iNameIndex, iSizeIndex: Integer;
begin
{ TOWRZENIE FORMULARZA }
if not Assigned(FFontDialogForm) then
FFontDialogForm := TFontForm.Create(Owner);
with FFontDialogForm do
begin
{ PRZYPISANIE WŁASNEJ PROCEDURY ZAMKNIĘCIA }
OnClose := DialogClose;
{ WYPEŁNIENIE PÓL REKORDU KLASY DIALOGU }
FFontInfo.Name := FontName;
FFontInfo.Size := FontSize;
FFontInfo.Style := FontStyle;
{ USTAWIENIE WŁAŚCIWOŚCI KOMPONENTÓW }
{ NAZWA CZCIONKI }
iNameIndex := cbFontName.Items.IndexOf(FontName);
if iNameIndex <> -1 then
cbFontName.ItemIndex := iNameIndex
else
cbFontName.ItemIndex := cbFontName.Items.IndexOf('Tahoma');
{ ROZMIAR CZCIONKI }
iSizeIndex := cbFontSize.Items.IndexOf(IntToStr(FontSize));
if iSizeIndex <> -1 then
cbFontSize.ItemIndex := iSizeIndex
else
cbFontSize.ItemIndex := cbFontSize.Items.IndexOf('8');
{ ODŚWIEŻENIE PANELU PODGLĄDU }
cbFontNameChange(cbFontName);
{ STYL CZCIONKI }
cbBold.Checked := fsBold in FontStyle;
cbItalic.Checked := fsItalic in FontStyle;
cbUnderline.Checked := fsUnderline in FontStyle;
cbStrikeOut.Checked := fsStrikeOut in FontStyle;
{ WYWOŁANIE OKNA DIALOGU I PRZECHWYCENIE WARTOŚCI MODALNEJ }
Result := ShowModal() = mrOk;
end;
end;
Teraz wystarczy tylko wybrać odpowiednie ustawienia czcionki i kliknąć w przycisk Zapisz ustawienia.
Wykorzystanie klasy dialogu
Aby wykorzsytać nasz dialog należy zadeklarować zmienną typu TFontDialog. Po utworzeniu wywołać metodę Execute z podanymi parametrami i w razie poprawnego zamnięcia dialogu (funkcja Execute zwróci True) wykorzystać właściwość Font dialogu.
Przykład wykorzystania dialogu:
procedure TMainForm.btnFontSettingsClick(Sender: TObject);
var
dlgFont: TFontDialog;
begin
dlgFont := TFontDialog.Create();
try
if dlgFont.Execute(Self, 'Tahoma', 10, [fsItalic, fsUnderline]) then
begin
{ WYKORZYSTANIE WŁAŚCIWOŚCI dlgFont.Font }
end;
finally
dlgFont.Free();
end;
end;
Powyższe kod spowoduje otworzenie okna dialogowego z ustawioną nazwą czcionki na Tahoma, rozmiar 10 oraz stylem pochylonym i podkreślonym. Efekt wykorzystania kodu zaprezentowany jest na poniższym rysunku:
Funkcja wykorzystująca klasę dialogu
Ręczne tworzenie obiektu dialogu jest trochę niewygodne. Stworzymy więc funkcję, która będzie wykorzystywać klasę dialogu i zwracać jako rezultat wartość logiczną, a przez referencję nowe właściwości czcionki.
Deklaracja funkcji wykorzystującej okno dialogowe:
function GetFontProps(Owner: TComponent; var FontInfo: TFontInfoRec; FontName: TFontName;
FontSize: Integer; FontStyle: TFontStyles): Boolean;
Funkcja jest dość prosta - posiada kilka parametrów:
Parametr | Typ | Opis |
---|---|---|
Owner | TComponent | właściciel modalnie wywoływanego okna dialogowego |
FontInfo | TFontInfoRec | struktura danych czcionki, do której zostaną wpisane pobrane z okna dialogowego właściwości czcionki |
FontName | TFontName | domyślna nazwa czcionki |
FontSize | Integer | domyślny rozmiar czcionki |
FontStyle | TFontStyles | atrybuty czcionki |
W ciele klasy musimy przede wszystkim utworzuć obiekt dialogu, wywołać metodę Execute i jeśli poprawnie zamknięto okno (wciśnięto przycisk Zapisz ustawienia) przepisać informacje z właściwości Font dialogu to parametru. Definicja funkcji wygląda następująco: |
function GetFontProps(Owner: TComponent; var FontInfo: TFontInfoRec; FontName: TFontName;
FontSize: Integer; FontStyle: TFontStyles): Boolean;
var
dlgFont: TFontDialog;
begin
Result := False;
{ UTWORZENIE OBIEKTU DIALOGU }
dlgFont := TFontDialog.Create();
try
{ WYWOŁANIE OKNA DIALOGOWEGO }
if dlgFont.Execute(Owner, FontName, FontSize, FontStyle) then
begin
Result := True;
{ UZUPEŁNIENIE PARAMETRU FontInfo }
FontInfo.Name := dlgFont.Font.Name;
FontInfo.Size := dlgFont.Font.Size;
FontInfo.Style := dlgFont.Font.Style;
end;
finally
dlgFont.Free();
end;
end;
Przykładowe wykorzystanie gotowej funkcji do ustalania czcionki komponentu memNote z klasy TMemo:
procedure TMainForm.btnFontSettingsClick(Sender: TObject);
var
firNew: TFontInfoRec;
begin
{ WYWOŁANIE OKNA DIALOGOWEGO }
if GetFontProps(Self, firNew, memNote.Font.Name, memNote.Font.Size, memNote.Font.Style) then
begin
{ WYKORZYSTANIE POBRANYCH INFORMACJI }
memNote.Font.Name := firNew.Name;
memNote.Font.Size := firNew.Size;
memNote.Font.Style := firNew.Style;
end;
end;
Użycie pojedynczej funkcji jest znacznie wygodniejsze niż każdorazowe ręcznie tworzenie i obsługiwanie obiektu. Poza tym jeśli w kilku miejscach programu chcemy wykorzystać nasz dialog - tworząc funkcję zaoszczędzamy na ilości linii kodu programu.
Najważniejsze informacje
Tworząc własne okno dialogowe należy pamiętać przede wszystkim o:
- wewnątrz konstruktora klasy tworzymy obiekt dialogu i nil'ujemy formularz,
- w ciele procedury DialogClose dokonujemy kopiowania danych z formularza do prywatnego pola klasy, po czym zamykamy i zwalniamy formularz z pamięci,
- metoda Execute służy do tworzenia formularza, ustawienia właściwości komponentów, pokazania okna na ekranie oraz przechwycenia wartości modalnej zwróconej przez okno,
- w zdarzeniu OnClick odpowiedniego przycisku ustalamy szereg warunków, które zablokują możliwość wybrania przez użytkownika złych danych lub nie wybrania ich wcale, a po sprawdzeniu stanu kontrolek przypisaniu formularzowi odpowiedniej wartości modalnej (tutaj zastosowałem wartość mrOk po kliknięciu w przycisk btnSaveSettings, lecz nie było wymagane spradzanie poprawności wprowadzonych danych);
Zakończenie
W wymieniony wyżej sposób mozemy tworzyć dowolne okna, ustalać im kontrolki oraz ich zachowania według własnych potrzeb oraz zwracać dane dowolnego typu, czy to będą typy proste jak String, Boolean, Byte czy złożone jak struktury danych, macierze itd.. Mam nadzieję, że z artykułu będą korzystać przede wszystkim początkujący programiści, którzy nie znali jeszcze możliwości tworzenia w ten sposób własnych okien dialogowych.
Jeżeli ma ktoś jakieś pytania bądź sugestie bardzo proszę o kontakt. Dziękuję za zainteresowanie tematem.
Muzeum Programowania ;)
Początkującym na pewno się przyda i włożyłeś w to dużo pracy. Z grubsza czytając, bardzo dobrze napisany - prostym językiem :D
Pozdrawiam :)