|
| | | |
• V.2. Inne - Własne kontrolki •
- 2.1 Jak zmienić niektóre właściwości elementów wykresu Graph ?
- 2.2 Jak zrobić systemowe pionowe paski przewijania na przykładzie ustawiania składowych RGB koloru ?
- 2.3 Jak zrobić własny pionowy i poziomy pasek przewijania oraz przykładowe wywołania ?
- 2.4 Jak zrobić, by do formantu TextBox można było wpisać tylko cyfry, znak - (minus) na początku liczby oraz separator dziesiętny przy określonej ilości cyfr dziesiętnych ?
- 2.5 Jak wyeliminować w formancie TextBox błąd konwersji nieprawidłowo wpisanej daty np. 07.14.08 na 14.07.2008 lub 32.12.08 na 08.12.1932 ?
- 2.6 Jak zrobić prostą wersję numerycznego formantu TextBox do którego można wpisać znak liczby, separator dziesiętny, cyfry oraz określoną ilość cyfr dziesiętnych ?
- 2.7 Jak zrobić własne skalowalne strzałki nawigacyjne w formancie Image i CommandButton ?
- 2.8 Jak zrobić odpowiednik kontrolki SpinButton z formularza FormUser ?
| | | | |
|
| | |
|
2.1 Jak zmienić niektóre właściwości elementów wykresu Graph ?

' zakładam, że obiekt objGraph ma dwie osie wykresu (X;Y) i 3 serie danych !!!
Private Const xlHairline = 1
Private Const xlThin = 2
Private Const xlThick = 4
Private Const xlMedium = &HFFFFEFD6
Private Const xlTickMarkInside = 2
Private Const xlTickMarkOutside = 3
Private Const xlTickMarkCross = 4
Private Const xlTickMarkNone = &HFFFFEFD2
Private Const xlCenter = &HFFFFEFF4
Private Const xlHorizontal = &HFFFFEFE0
Private Const xlVertical = &HFFFFEFBA
Private Const xlHundreds = -2
Private Const xlThousands = -3
Private Const xlMillions = -6
Private Const xlMarkerStyleSquare = 1
Private Const xlMarkerStyleCircle = 8
Private Const xlMarkerStyleTriangle = 3
Private Const xlMarkerStyleDiamond = 2
Private Const xlMarkerStyleDash = &HFFFFEFED
Private Const xlMarkerStyleStar = 5
Private Const xlLabelPositionAbove = 0
Private Const xlLabelPositionBelow = 1

Private Sub btnTest_Click()
Dim objGraph As Object
Set objGraph = Me.oleChart.Object
With objGraph
' 1. Tytuł wykresu
.ChartTitle.Text = "Zmieniamy tytuł wykresu: " & _
vbLf & "z nową linią"
.ChartTitle.Font.Color = vbRed
' 2. Tytuł osi poziomej
If Not .Axes(1).HasTitle Then .Axes(1).HasTitle = True
.Axes(1).AxisTitle.Caption = "Zmieniamy tytuł osi poziomej"
' 3. Czcionka etykiet osi poziomej i wygląd osi poziomej
' wystąpi błąd, jeżeli tabela jest widoczna
.HasDataTable = False
.Axes(1).TickLabels.Font.Size = 12
.Axes(1).TickLabels.Font.Color = vbMagenta
.Axes(1).TickLabels.Font.Bold = True
.Axes(1).Border.Weight = xlThick
.Axes(1).Border.Color = vbBlue
.Axes(1).MinorTickMark = xlTickMarkCross
' 4. Tytuł osi pionowej
.Axes(2).AxisTitle.HorizontalAlignment = xlCenter
.Axes(2).AxisTitle.VerticalAlignment = xlCenter
.Axes(2).AxisTitle.Orientation = xlHorizontal
' przesuń etykietę tytułu osi pionowej
.Axes(2).AxisTitle.Left = 50
.Axes(2).AxisTitle.Caption = "Oś" & " pionowa: "
.Axes(2).AxisTitle.Font.Color = vbGreen
' 5. Czcionka etykiet osi pionowej i wygląd osi pionowej
.Axes(2).TickLabels.Font.Size = 10
.Axes(2).TickLabels.Font.Color = vbGreen
.Axes(2).TickLabels.Font.Bold = True
.Axes(2).Border.Weight = xlMedium
.Axes(2).Border.Color = vbBlue
.Axes(2).MinorTickMark = xlTickMarkInside
' 6. Etykieta jednostek miary dla osi pionowej
' Graph ver.8.0 nie obługuje etykiety jednostki miary
Static g As Boolean
If g = True Then
.Axes(2).DisplayUnit = xlThousands ' xlHundreds ' xlMillions
.Axes(2).DisplayUnitLabel.Caption = "j.m 1000"
.Axes(2).DisplayUnitLabel.Font.Color = vbBlue
.Axes(2).DisplayUnitLabel.Border.Color = vbRed
Else
.Axes(2).DisplayUnit = 0
End If
g = Not g
' 7. Linie siatki wykresu
Static m As Byte
m = m + 1
.Axes(1).HasMajorGridlines = True
.Axes(1).HasMinorGridlines = True
.Axes(2).HasMajorGridlines = True
.Axes(2).HasMinorGridlines = True
.Axes(1).MajorGridlines.Border.Color = vbRed
.Axes(1).MinorGridlines.Border.Color = vbGreen
.Axes(2).MajorGridlines.Border.Color = vbMagenta
.Axes(2).MinorGridlines.Border.Color = vbCyan
' cztery wersje wizualizacji linii siatki
Select Case m
Case 1
.Axes(1).HasMajorGridlines = False
.Axes(1).HasMinorGridlines = False
.Axes(2).HasMajorGridlines = True
.Axes(2).HasMinorGridlines = True
Case 2
.Axes(1).HasMajorGridlines = True
.Axes(1).HasMinorGridlines = True
.Axes(2).HasMajorGridlines = False
.Axes(2).HasMinorGridlines = False
Case 3
.Axes(1).HasMajorGridlines = True
.Axes(1).HasMinorGridlines = True
.Axes(2).HasMajorGridlines = True
.Axes(2).HasMinorGridlines = True
Case Else
.Axes(1).HasMajorGridlines = False
.Axes(1).HasMinorGridlines = False
.Axes(2).HasMajorGridlines = False
.Axes(2).HasMinorGridlines = False
m = 0
End Select
' 8. Punkty wykresu liniowego
Dim aMarkerStyle(1 To 3) As Long
Dim i As Long
Dim j As Long
Dim vValue As Variant
Static f As Boolean
If f = False Then
aMarkerStyle(1) = xlMarkerStyleDiamond
aMarkerStyle(2) = xlMarkerStyleDash
aMarkerStyle(3) = xlMarkerStyleStar
.SeriesCollection(1).Border.Color = vbBlue
.SeriesCollection(2).Border.Color = vbGreen
.SeriesCollection(3).Border.Color = vbCyan
' ustaw tekst Etykiet
.SeriesCollection(2).hasDataLabels = True
For i = 1 To .SeriesCollection(2).Points.Count
.SeriesCollection(2).DataLabels(i).Text = "Punkt " & CStr(i)
Next
Else
.SeriesCollection(1).Border.Color = vbRed
.SeriesCollection(2).Border.Color = vbGreen
.SeriesCollection(3).Border.Color = vbBlue
aMarkerStyle(1) = xlMarkerStyleCircle
aMarkerStyle(2) = xlMarkerStyleSquare
aMarkerStyle(3) = xlMarkerStyleTriangle
.SeriesCollection(3).DataLabels.Position = xlLabelPositionAbove
' przywróć tekst etykiet
For i = 1 To .SeriesCollection(2).Points.Count
.SeriesCollection(2).DataLabels(i).AutoText = True
Next
End If
f = Not f
If f = False Then Exit Sub
DoCmd.Hourglass True
' przejdź po trzech seriach wykresu
For j = 1 To 3
For i = 1 To .SeriesCollection(j).Points.Count
vValue = Nz(.Application.DataSheet.Cells(i + 1, j + 1).Value, 0)
.SeriesCollection(j).Points(i).MarkerStyle = aMarkerStyle(j)
' zmień kolor parzystych i nie parzystych
If (vValue Mod 2) = 0 Then
.SeriesCollection(j).Points(i).MarkerBackgroundColor = _
vbRed
.SeriesCollection(j).Points(i).MarkerForegroundColor = _
vbRed
If j = 3 And Me.tglMarkerStyle = True Then
.SeriesCollection(3).DataLabels(i).Position = _
xlLabelPositionAbove
End If
Else
.SeriesCollection(j).Points(i).MarkerBackgroundColor = _
vbGreen
.SeriesCollection(j).Points(i).MarkerForegroundColor = _
vbGreen
If j = 3 And Me.tglMarkerStyle = True Then
.SeriesCollection(3).DataLabels(i).Position = _
xlLabelPositionBelow
End If
End If
Next
Next
DoCmd.Hourglass False
' 9. Zmień własciwości Tabeli (widocznej pod wykresem)
Static k As Long
Static aVal(1 To 3) As String
If Len(aVal(1)) = 0 Then
aVal(1) = .Application.DataSheet.Cells(1, 2).Value
End If
If Len(aVal(2)) = 0 Then
aVal(2) = .Application.DataSheet.Cells(1, 3).Value
End If
If Len(aVal(3)) = 0 Then
aVal(3) = .Application.DataSheet.Cells(1, 4).Value
End If
k = k + 1
' trzy wersje wizualizacji Tabeli
Select Case k
Case 1
.HasLegend = True
.HasDataTable = True
.DataTable.ShowLegendKey = False
.Application.DataSheet.Cells(1, 2).Value = aVal(1)
.Application.DataSheet.Cells(1, 3).Value = aVal(2)
.Application.DataSheet.Cells(1, 4).Value = aVal(3)
.DataTable.Font.Bold = False
.DataTable.Font.Color = vbBlack
Case 2
.HasLegend = True
.HasDataTable = True
.DataTable.ShowLegendKey = True
.Application.DataSheet.Cells(1, 2).Value = "AAA aa"
.Application.DataSheet.Cells(1, 3).Value = "BBB bb"
.Application.DataSheet.Cells(1, 4).Value = "CCC cc"
.DataTable.Font.Bold = False
.DataTable.Font.Color = vbBlue
Case Else
.HasLegend = False
.HasDataTable = False
k = 0
End Select
' 10. Zmień właściwości czcionki Legendy
Dim aCol(1 To 6)
Static h As Boolean
aCol(1) = vbRed: aCol(2) = vbGreen: aCol(3) = vbBlue
aCol(4) = vbCyan: aCol(5) = vbMagenta: aCol(6) = vbYellow
' legenda musi być widoczna
.HasLegend = True
If h = True Then
For i = 1 To .Legend.LegendEntries.Count
.Legend.LegendEntries(i).Font.Bold = True
.Legend.LegendEntries(i).Font.Color = aCol(i)
Next
Else
For i = 1 To .Legend.LegendEntries.Count
.Legend.LegendEntries(i).Font.Bold = False
.Legend.LegendEntries(i).Font.Color = vbMagenta
Next
End If
h = Not h
End With
Set objGraph = Nothing
End Sub
ΔΔΔ | | | | |
|
| | |
|
2.2 Jak zrobić systemowe pionowe paski przewijania na przykładzie ustawiania składowych RGB koloru ?
- Tworzy dynamicznie trzy pionowe paski przewijania i wstawia je formularza
- Przechwytuje komunikaty pasków i generuje zdarzenie OnChange w formancie tekstowym w formularzu poprzez przekazanie do niego wartość pozycji suwaka (od 0 - 255) oraz indeks paska (0-2)
- W zdarzeniu OnChange ustawiane są kolory tła etykiet odpowiadające wartościom pasków i kolor dodatkowej etykiety na kolor będący kolorem wypadkowym.
Ograniczenia przykładu SysScrollbar:
- Access 97 korzysta z funkcji AddrOf autorstwa Miszy Kaplana.
- Access 2000 korzysta z wbudowanej funkcji AddressOf (nie jest to może jakieś ograniczenie, no ale ...... )
- Niestety, przykład działa tylko dla pionowych pasków przewijania ;-(
- Jeżeli chcemy używać obu pasków przewijania to lepiej zdecydować się na przykład przedstawiony poniżej:
ΔΔΔ | | | | |
|
| | |
|
2.3 Jak zrobić własny pionowy i poziomy pasek przewijania oraz przykładowe wywołania ?
- Każdy pasek przewijania składa się z czterech formantów typu Image (bez bitmap) oraz trzech przycisków (przezroczystych) typu Button. Dwa przyciski wykorzystywane są w celu uzyskania efektu Autopowtarzania podczas wciśniętego prawego przycisku myszy na strzałkach przewijania, trzeci (nałożony na tło paska) odpowiada za przesuw suwaka do miejsca kliknięcia myszką. Dodatkowa etykieta symuluje podświetlenie tła paska.
- paski przewijania w standardowym wywołaniu z wyglądu są zgodne z systemowymi paskami przewijania. Również kolor i wielkość strzałki przycisku jest zbliżona do wielkości systemowej, gdyż bitmapy: strzałek, suwaka oraz tła suwaka są tworzone dynamicznie podczas ładowania formularza.
- Standardowo przyjęto, że dla wartości minimalnej suwak znajduje się w lewym skrajnym położeniu dla paska poziomego lub górnym (dla paska pionowego).
- W przypadku paska pionowego można zmienić wzajemnie zakres Minimum z zakresem Maksimum, tak by podczas przesuwania suwaka do góry wartość zwracana także rosła.
- Jeżeli paski przewijania wywołamy poprzez przekazanie obiektu (w przykładzie formant typu Label lub Image) pasek przewijania dostosowuje swoją wielkość do przekazanego obiektu.
- Można również wywołać paski poprzez przekazania pozycji i wymiarów pasków.
- Możliwa jest prawie dowolna (w granicach zdrowego rozsądku) zmiana wysokości i szerokości strzałek oraz suwaka. Dla niespójnych wartości przeprowadzana jest korekcja wymiarów przycisków strzałek i wielkości suwaka. (korekcja nie jest całkowicie zgodna z systemowymi paskami, ale jakoś działa).
- Można także zmieniać kolorystykę elementów (strzałek, suwaka + tła strzałek, tła pod suwakiem). Nie jest możliwa zmiana kolorów odpowiadających za efekt 3D "wklęsły, wypukły) ponieważ uzyskiwany jest on poprzez acEffectRaised i acEffectSunken, które to wykorzystują kolor systemowe.
- Odmienne od zachowań systemowego paska jest zdarzenie kliknięcia na obszar pomiędzy suwakiem, a strzałką. Tło systemowego paska zostaje podświetlone i przy wciśniętym przycisku następuje autoprzewijanie o "stronę". W przykładzie zastosowałem jedynie pseudoobsługę tego zdarzenie - każde kliknięcie na ten obszar powoduje dosunięcie suwaka do aktualnej pozycji wskaźnika myszy. Na czas trzymania wciśniętego lewego przycisku myszy tło suwaka pomiędzy suwakiem, a strzałką zostaje podświetlone.
ΔΔΔ | | | | |
|
| | |
|
2.4 Jak zrobić, by do formantu TextBox można było wpisać tylko cyfry, znak - (minus) na początku liczby oraz separator dziesiętny przy określonej ilości cyfr dziesiętnych ?
Metoda opiera się na kontrolowaniu wartości KeyCode w obsłudze zdarzenia:
Private Sub TextBox_KeyDown(KeyCode As Integer, Shift As Integer)
- Przede wszystkim musimy wiedzieć jaki jest separator dziesiętny. Wszystko jest w porządku jeżeli znakiem separatora jest "przecinek" ew. "kropka", gdyż znamy kod KeyCode odpowiadajacy tym znakom. A co się stanie jeżeli użytkownik ustawił sobie jako separator znak "@" lub np "_". Wiem, wiem, że to lekka przesada, nie mniej jednak w tabelce "tDecSep" umieściłem odczytane kody klawiszy i zrobiłem dodatkowy formularz do odczytu nieznanego kodu klawisza separatora dziesiętnego.
Ale dla znaku - (minus) liczb ujemnych dałem sobie spokój.
- Musimy również uwzględnić położenie kursora, czy jest zaznaczony tekst (by go nadpisać), czy dozwolony jest separator, a jeżeli tak to czy juz został wpisany, dozwoloną ilość miejsc dziesiętnych itp.
- Wiele kodów klawiszy (nieistotnych dla naszego TextBox'a) musimy przepuść przez procedurę by wygenerowały przypisane im zdarzenie, np. wszystkie klawisze funkcyjne oraz wszystkie litery i klawisze "F1" do "F16" z klawiszem ALT lub CTRL.
- W przypadku kiedy użytkownik chce wpisać nieprawidłowy znak, uniemożliwiamy mu to zerując zmienną KeyCode = 0
Dodatkowo w procedurze obsługi zdarzenia OnChange:
Private Sub txt_Change()
zabezpieczamy się przed przed wklejeniem poprzez Ctrl + V nieprawidłowego tekstu (nienumerycznego lub z niedozwoloną ilością miejsc po przecinku) do naszego formantu numerycznego
ΔΔΔ | | | | |
|
| | |
|
2.5 Jak wyeliminować w formancie TextBox błąd konwersji nieprawidłowo wpisanej daty np. 07.14.08 na 14.07.2008 lub 32.12.08 na 08.12.1932 ?
- Moim zdaniem obsługa pól typu Data przez Access pozostawia wiele do życzenia.
- Nigdy nie ma pewności, czy wpisana data zostanie prawidłowo zinterpretowana przez Access. Dla przykładu:
- Format daty krótkiej w ustawieniach regionalnych: dd-MM-rr
- Interpretacja roku dwucyfrowego: 1930 do 2029
- Dla formantu formularza bez ustawionej maski wprowadzania możliwości popełnienia błędu są bardzo duże:
Tabela 1. Błędy wprowadzania danych
Bez maski wprowadzania: |
Wartość wprowadzona: dd-MM-rr | Otrzymana Data |
11-111 | 1-11-111 |
1-11 | 1-11-2004 |
111-11-11 | 11-11-111 |
12-13 | 13-12-2004 |
12-13-14 | 13-12-2014 |
Maska wprowadzania: data krótka - 99-99-00;0;_ |
9-13-88 | 13-09-1988 |
32-04-24 | 24-04-1932 |
31-11-30 | 30-11-1931 |
Jak widać wprowadzanie daty do formantów bez maski jest zupełnie pozbawione sensu.
W przypadku zastosowania maski ilość błędów zostaje ograniczona, ale nie do końca.
Dodatkowym utrudnieniem jest szybkie wprowadzania dat gdzie dzień, miesiąc i rok są
jednocyfrowe np. 1-2-3 (01-02-2003)
W przypadku maski 99-99-09;0;_ możemy taką datę najłatwiej wprowadzić wpisując
1 separator 2 separator 3 - czyli praktycznie musimy wprowadzić 5 znaków.
Ale przy takiej masce nie jesteśmy w stanie wprowadzić daty 1-2-1234. Musimy maskę zamienić na 09-09-0999;0;_.
I znowu problem - jeżeli użytkownik zmieni format daty krótkiej na rr-MM-dd, to nie będziemy w stanie wpisać tej daty w postaci 1234-2-1.
Ponieważ nie jesteśmy w stanie przewidzieć jaki format daty będzie miał użytkownik, więc respektując jego upodobania maskę możemy tworzyć dynamicznie:
Dla formatu daty krótkiej:
- dd-MM-rr) tworzymy maskę w postaci 99/99/99;0;_
- dd-MM-rrrr i MM-dd-rrrr tworzymy maskę w postaci 99/99/9999;0;_
- rrrr-MM-dd tworzymy maskę w postaci 9999/99/99;0;_
- Maski takie umożliwiają szybsze wprowadzenie daty typu 1-2-3 poprzez wpisanie:
- 1 separator 2 separator 3, ale i tak maska w formancie obsługującym datę praktycznie zawsze spowoduje jakieś problemy. Najmniej problemów jest gdy maska zawiera same 9. Co prawda nie wymusza wpisywania odpowiednich znaków, a jedynie informuje użytkownika o formacie wprowadzanych danych. Sprawdzaniem poprawności daty musi zająć się Access i procedury nasze procedury pomocnicze.
- Po wprowadzeniu daty do formantu pozostaje nam odczytanie składowych daty
- i sprawdzenie poprawności wartości daty za pomocą jednego z wyrażeń:
Dt1 = DateSerial(CInt(sYear(i)), CInt(sMonth(i)), CInt(sDay(i)))
Dt2 = DateValue(sDay(i) & cShDateSep & sMonth(i) & cShDateSep & sYear(i))
Dt3 = DateValue(sMonth(i) & "/" & sDay(i) & "/" & sYear(i))
Dt4 = Eval("#" & sMonth(i) & "/" & sDay(i) & "/" & sYear(i) & "#")
Tabela 2. dla formatu daty dd-MM-rr
Lp. | d(i) | M(i) | y(i) | DateSerial (d-M-y) | DateValue (d-M-y) | DateValue (M/d/y) | Eval(M/d/y) |
1. | 10 | 31 | 2004 | 10-07-2006 | 31-10-2004 | 31-10-2004 | 31-10-2004 |
2. | 10 | 31 | 0004 | 10-07-2006 | 31-10-2004 | 31-10-2004 | 04-10-1931 |
3. | 02 | 15 | 2004 | 02-03-2005 | 15-02-2004 | 15-02-2004 | 15-02-2004 |
4. | 02 | 15 | 0004 | 02-03-2005 | 15-02-2004 | 15-02-2004 | 04-02-2015 |
5. | 12 | 15 | 2004 | 12-03-2005 | 15-12-2004 | 15-12-2004 | 15-12-2004 |
6. | 12 | 15 | 0004 | 12-03-2005 | 15-12-2004 | 15-12-2004 | 04-12-2015 |
7. | 11 | 31 | 2004 | 11-07-2006 | ERROR | ERROR | ERROR |
8. | 11 | 31 | 0004 | 11-07-2006 | ERROR | 04-11-1931 | 04-11-1931 |
9. | 00 | 00 | 2004 | 30-11-2003 | ERROR | ERROR | ERROR |
10 | 00 | 00 | 0004 | 30-11-2003 | ERROR | ERROR | ERROR |
11. | 32 | 12 | 2004 | 01-01-2005 | ERROR | ERROR | ERROR |
12. | 32 | 12 | 0004 | 01-01-2005 | 04-12-1932 | ERROR | ERROR |
13. | 01 | 01 | 0100 | 01-01-100 | 01-01-100 | 01-01-100 | 01-01-100 |
14. | 31 | 12 | 0099 | 31-12-1999 | 31-12-1999 | 31-12-1999 | 31-12-1999 |
Jak widać w Tabeli 2 DateSerial nie nadaje się do sprawdzanie poprawności daty.
W zasadzie należało by porównać Dt2, Dt3 i Dt4 (gdyż wyniki zależne są od kolejności części składowych daty krótkiej w Ustawieniach Regionalnych) i w przypadku zgodności wszystkich trzech wartości mamy 90% pewności, że data jest prawidłowa. Niestety, pozostaje pewien procent niepewności: patrz poz.1 i poz.5
Dlatego też musimy wcześniej sprawdzić poprawność wprowadzonej wartości Miesiąca (w obu przypadkach miesiąc jest większy od 12, ale wprowadzony dzień jest prawidłową wartością dla miesiąca i Access w ten "inteligentny" sposób dopasowuje sobie datę, trochę niezgodnie z zamiarem użytkownika.
Przypadek ten nie zachodzi dla poz.7 i poz.8 gdyż dzień 31 nie jest prawidłową wartością dla listopada.
ΔΔΔ | | | | |
|
| | |
|
2.6 Jak zrobić prostą wersję numerycznego formantu TextBox do którego można wpisać znak liczby, separator dziesiętny, cyfry oraz określoną ilość cyfr dziesiętnych ?
Metoda opiera się na kontrolowaniu wartości KeyAscii w obsłudze zdarzenia:
Private Sub TextBox_KeyPress(KeyAscii As Integer)
- W przykładzie rozpoznajemy kod naciśniętego klawisza, uwzględniamy położenie kursora, czy jest zaznaczony tekst (by go nadpisać), czy dozwolony jest separator, a jeżeli tak to czy juz został wpisany, dozwoloną ilość miejsc dziesiętnych itp.
- W przypadku kiedy użytkownik chce wpisać nieprawidłowy znak, uniemożliwiamy mu to zerując argument KeyCode = 0
Dodatkowo w procedurze obsługi zdarzenia OnChange:
Private Sub txt_Change()
zabezpieczamy się przed przed wklejeniem poprzez Ctrl + V nieprawidłowego tekstu (nienumerycznego lub z niedozwoloną ilością miejsc po przecinku) do naszego formantu numerycznego
ΔΔΔ | | | | |
|
| | |
|
2.7 Jak zrobić własne skalowalne strzałki nawigacyjne w formancie Image i CommandButton ?
ΔΔΔ | | | | |
|
| | |
|
2.8 Jak zrobić odpowiednik kontrolki SpinButton z formularza FormUser ?
ΔΔΔ | | | | |
|
| |