Tworzenie warstw w PaintBox, czyli możliwość odświeżania tylko części obiektów

0

Dzień dobry,
kilka dni temu wróciłem do programowania w moim ukochanym Delphi i trafiłem na zagadnienie, którego nie potrafię rozwiązać.
Nie mam dużego doświadczenia, piszę aplikacje głównie dla własnej przyjemności.

Chciałem zrobić sobie prosty edytor (a tak naprawdę viewer) do plików wektorowych. Na razie stworzyłem "selection tool" pod zdarzeniami MouseMove, MouseDown, MouseUp, w taki sposób że ramka jest rysowana poprzez PaintBox1.Canvas.Rectangle() w zdarzeniu PaintBox1Paint(), a odświeżanie wywołuję poprzez PaintBox1.Invalidate (kodu chyba nie muszę prezentować).
I wszystko pięknie działa do momentu w którym stwierdziłem, że stworzę sobie ramkę pokazującą obszar strony (tj w Corelu czy AI).

Problem polega na tym, że ramka została umieszczona też w zdarzeniu PaintBox1Paint(), więc widać co jakiś czas jak się przeładowuje.
I tutaj powstaje pytanie czy i w jaki sposób można zrobić rodzaj warstw? Chodzi dokładnie o możliwość określenia które elementy są odświeżane, a które nie.

Myślałem o 3 rozwiązaniach:

  1. Możliwe, że jest jakaś metoda na oznaczenie elementów odświeżanych i nie? Choć zdaje mi się, że canvas zawsze odświeżany jest jako całość (zazwyczaj tak jest).
  2. Stworzenie warstw np.: poprzez utworzenie dwóch PaintBox'ów na sobie? Szalone, tym bardziej że mogą się przestać pokrywać (i jeszcze z kilku innych powodów).
  3. Stworzenie własnej klasy (pewnie dziedziczącej z TPaintBox lub Canvas). Tylko nie mam pojęcia jak to ugryźć. Wiele canvasów???

Bardzo proszę o pomoc. Jedyne czego potrzebuję to by nie mrugały "stałe" elementy.

0

4.Przesiądź się z TPaintBox na OpenGL to nie będziesz mieć takich problemów :P

1

Ekran mruga tylko i wyłącznie dlatego, że renderowanie działa zbyt wolno. Owe mignięcie jest niczym innym jak odstępem pomiędzy Canvas.Rectangle wypełniającym tło, a malowaniem kolejnych elementów na tle.

Pierwszym błędem jaki robisz to brak obsługi pomocniczego bufora (tzw. back buffer). Taki bufor to nic innego jak pomocnicza bitmapa, istniejąca wyłącznie w pamięci, na której to powinien być renderowany kompletny obraz. Po zakonczeniu renderowania, następuje namalowanie jego zawartości w kontrolce. Nie musi się z niego korzystać dla prostych rzeczy, jednak im bardziej skomplikowane rzeczy się renderuje, tym bardziej widoczne bez niego będzie miganie ekranu.

Jeśli chodzi o warstwy to cóż – trzeba sobie taki mechanizm zaimplementować. Warstwy mogą być zrealizowane jako zestaw list (np. generycznych) przechowujących elementy, z których składa się wektorowy obraz. Taki zestaw list jest bardzo łatwy w użyciu i daje duże pole do manewru. Zmiana kolejności warstw to zmiana kolejnojności list, przesuwanie elementów wizualnie w dół lub w górę stosu, to nic innego jak przesuwanie elementów w konkretnej liście w jedną lub drugą stronę.

Tak więc możesz skorzystać z listy list, coś w tym stylu (pseudokod):

type
  TElement = class(TObject)  // bazowa klasa elementów
  {..}
  end;
  
type
  TLayer    = TList<TElement>;  // lista elementów jako warstwa
  TDocument = TList<TLayer>;    // lista warstw elementów

Nie ma co się zbytnio rozpisywać, dlatego że tworzenie nawet prostego edytora to temat rzeka.

Jakiś czas temu napisałem coś w rodzaju edytora na potrzeby wątku, pokazując sposób na przesuwanie po ekranie kolorowych prostokątów. Możesz się tym kodem inspirować, co nieco podpatrzeć, jednak służył do czegoś innego. No i brak w nim dodatkowego buforowania oraz generyków. ;)

0

:D To prawda.

Myślałem o tym, by użyć GLScene, lecz nie wiem jak trudne będzie przestawienie na "tryb 2D".
A co myślisz o GraphicEx? też zawiera OpenGL.

Czytałem też kiedyś o cairo.

furious programming dziękuję za podpowiedź. Nie wiedziałem o tym, że bufor załatwi sprawę.
Możesz mi podpowiedzieć jak to zrobić? Muszę nadpisać jakąś funkcję?

Twój post czytałem niedawno, lecz jak sam napisałeś, nie ma tam nic o buforowaniu - a to jest odpowiedź na mój problem.

Pytanie jeszcze mam inne: Czy nie lepiej użyć GLScene, Graphics32, Cairo lub coś podobnego?

0
Pan Andrzej napisał(a):

furious programming dziękuję za podpowiedź. Nie wiedziałem o tym, że bufor załatwi sprawę.

Załatwi wyłącznie sprawę migania ekranu, o ile zostanie prawidłowo zaimplementowany. Natomiast jeżeli malowanie wszystkich elementów na tej pomocniczej bitmapie będzie trwało długo, to np. zmiana rozmiaru okna zamiast płynnej (natychmiastowej) będzie skokowa. Co jednak nie zmienia faktu, że nic nie powinno migać.

Możesz mi podpowiedzieć jak to zrobić? Muszę nadpisać jakąś funkcję?

Twój post czytałem niedawno, lecz jak sam napisałeś, nie ma tam nic o buforowaniu - a to jest odpowiedź na mój problem.

Podstawy możesz znaleźć tutaj (pisane z głowy), choć to tylko wierzchołek góry lodowej. ;)

Sama pomocnicza bitmapa nie będzie lekarstwem na wszystkie bolączki trybu okienkowego. W dalszym ciągu potrzebne będą mechanizmy optymalizacyjne, ograniczające cały proces renderowania do minimum. Np. mechanizm pomijania elementów całkowicie przykrytych przez inne lub istniejących poza ekranem, a nawet całych warstw (w tym warstw wyłączonych/ukrytych).

Przy pracy nad taką kontrolką naprawdę trzeba się orobić, aby miała ręce i nogi oraz aby działała efektywnie i przede wszystkim prawidłowo. Da się zrobić, ale trzeba się narobić.

Pytanie jeszcze mam inne: Czy nie lepiej użyć GLScene, GraphicEx, Cairo lub coś podobnego?

Obstawiam, że interesują Cię takie rzeczy jak antialiasing, krzywe czy gradienty, więc biblioteka implementująca taką funkcjonalność na pewno będzie najlepszym rozwiązaniem. VCL posiada jakieś podstawy, jednak nie jest to biblioteka graficzna, więc nie zrobisz za jej pomocą zbyt wiele.

A sądząc po tym, że chcesz stworzyć viewer dla grafiki wektorowej, zaawansowane funkcje graficzne będą potrzebne.

0

Dziękuję.

Rozumiem, że tworzę bitmapę, którą dodaję "na spód". I bitmapa nie jest odświeżana w momencie wywołania metody Repaint/Refresh/Invalidate?
Czyli mogę tworzyć w ten sposób warstwy.

Jeżeli to się zgadza to mogę tworzyć bitmapę przesuwanych wektorów i ją przesuwać? Wtedy nie trzeba generować linii przy każdym MouseMove :)

A co do programów to:

  1. Cairo - nie współpracuje z Delphi
  2. GraphicEx - to biblioteka do importu znanych formatów zapisów bitmap
  3. Graphics32 - to jest tylko 32-bitowa wersja i nie widzę by miała metodu typu LineTo(), Rectangle() itp.
  4. OpenGl - boję się. że to zbyt rozbudowany kod trzeba tworzyć. A co z antyaliasingiem?

Cały czas myślę nad OpenGL, lecz naprawdę przeraża mnie "niskopoziomowość" zagadnienia.

0
Pan Andrzej napisał(a):

Rozumiem, że tworzę bitmapę, którą dodaję "na spód".

Tzn. tworzysz bitmapkę i trzymasz ją wyłacznie w pamięci - nigdzie jej nie umieszczasz. Ona sobie istnieje w pamięci, więc malowanie na niej nie jest powiązane z odświeżaniem ekranu (tego co widać na monitorze), dzięki czemu malowanie jest efektywne.

I bitmapa nie jest odświeżana w momencie wywołania metody Repaint/Refresh/Invalidate?

Bitmapa musi być odświeżana po każdej modyfikacji dokumentu. Po każdej modyfikacji wpływającej na to co znajduje się na ekranie, np. po zmianie koloru elementu, po zmianie ich kolejności, ukryciu lub pokazaniu, po włączeniu/wyłączeniu warstwy (jeśli nie była pusta), po zmianie kolejności warstw itd. Jeśli zmiana musi być widoczna dla użytkownika, to należy wyrenderować obraz ponownie (w całości na bitmapie pomocniczej) i odmalować komponent, aby zmiany zostały wprowadzone na ekran.

Jeśli chodzi o samo odmalowanie komponentu to zawsze wywołuj metodę Invalidate. Ona posiada w sobie szereg zabezpieczeń, więc jest bezpieczna w użyciu. Implementacja tej metody w VCL i LCL może się różnić, jednak jej zadanie jest to samo – odmalować kontrolkę tylko wtedy, gdy jest to możliwe.

Czyli mogę tworzyć w ten sposób warstwy.

System warstw dokumentu możesz zrealizować naprawdę na wiele sposobów. To co podałem dotyczy wyłącznie kontenerów umożliwiających stworzenie drzewiastej struktury, z podziałem dokumentu na warstwy, a te na elementy. I to w zupełności wystarczy do prostego edytora/viewera – zapewnioną masz możliwość tworzenia wielu dokumentów, w nich wielu warstw (plus operacje na nich, np. zmiana kolejności), a w nich wielu elementów (i tak samo – opcje dodawania, usuwania, zmiany kolejności itd.).

Gołe listy raczej nie wystarczą, dlatego że dokument to nie tylko zbiór warstw, a warstwy to nie tylko zestawy elementów do nich należących. Na przykładzie funkcjonalności programu Inkscape, opis dokumentu to mnóstwo innych informacji, jak np. rozmiar w danej jednostce, dane dotyczące tła, metadane czy licencja. Natomiast warstwa – oprócz listy elementów – posiadać może również swoją nazwę, co jest raczej oczywiste.

Strukturę dokumentu i jego podział na części składowe, powinieneś dobrać na podstawie określonej funkcjonalności programu. Tak więc najpierw określ sobie to co program ma umożliwiać i jak już to będziesz miał rozpisane, wtedy zajmij się kodem.

Jeżeli to się zgadza to mogę tworzyć bitmapę przesuwanych wektorów i ją przesuwać? Wtedy nie trzeba generować linii przy każdym MouseMove :)

Bitmapa ma służyć jedynie do renderowania ekranu w tle, aby uniknąć migania ekranu. Zawartość tej bitmapy ma dokładnie odpowiadać temu, co użytkownik ma widzieć na ekranie. Wszystkie elementy zawarte w dokumencie powinieneś mieć opisane obiektami i zawarte w drzewku.

Podczas obsługi komunikatu myszy nie musisz odświeżać ekranu, dlatego że malowaniem tego co znajduje się w kontrolce pod kursorem zajmuje się system. Jeśli przesuniesz nad komponentem okno to też wszystkim zajmie się system. Renderować obraz musisz tylko wtedy, kiedy coś wizualnie zmienia się w komponencie, np. jeśli przesuwasz element po ekranie, jeśli go rozciągasz lub zmieniasz jego kolor.


No i też nie polecałbym podpinać się pod komponent typu TPaintBox – to nie jest zbyt dobre rozwiązanie. Lepiej będzie jak stworzysz własną kontrolkę od podstaw i w niej zaimplementujesz całą funkcjonalność. W bazowej klasie – np. TCustomControl – jest już podstawowa funkcjonalność, metoda Paint czy popularne zdarzenia, więc możesz łatwo je nadpisać. Plusem będzie też możliwości utworzenia kilku instancji takich komponentów, dzięki czemu Twój program będzie mógł obsługiwać wiele dokumentów, np. w postaci zakładek.

0

Ponownie dziękuję.

Mam "selection tool", które właśnie musi być odświeżane w trakcie przesuwania. Jak dodałem bitmapę do Canvasu to ją też odświeża (mruga).
Rozumiem, że bitmapa ma być w pamięci i wyświetlam ją tylko raz, lecz wychodzi na to, że muszę mieć coś pod Canvasem.

A co do TCustomControl to sprawdzę. Zastanawia mnie tylko fakt, że TPaintBox dziedziczy z TGraphicControl. Czy pozwoli mi to na stworzenie np.: kilku bitmap na sobie i canvas?

I jeszcze mam pytania odnośnie tego kodu:

{ dodatkowe pole }
private
  FBackBuffer: TBitmap;

{ konstruktor }
FBackBuffer := TBitmap.Create();
FBackBuffer.PixelFormat := pf24Bit;
FBackBuffer.SetSize(Self.Width, Self.Height);

{ destruktor }
FBackBuffer.Free();

{ metoda zmieniająca rozmiar kontrolki }
FBackBuffer.SetSize(Self.Width, Self.Height);

{ metoda Paint kontrolki }
Self.Canvas.Draw(0, 0, FBackBuffer);

{ metoda renderująca }
FBackBuffer.Canvas.Brush.Color := clWhite;
FBackBuffer.Canvas.FillRect(FBackBuffer.ClipRect);
// renderowanie elementów dokumentu

Czy po prostu mi podałeś kilka linii kodu, czy mam edytować w tej formie? Chodzi mi o to czemu jest SetSize potem destruktor i znów SetSize?
I renderowanie elementów po draw?

0

Zanim zaczniesz cokolwiek robić w temacie tego programu, najpierw wróć do podstaw programowania. Niczego nie zrozumiełeś z tego co napisałem – absolutnie niczego.

Nie rozróżniasz właściwości Canvas od zupełnie osobnego obiektu klasy TBitmap, nie rozumiesz do czego konkretnie potrzebna jest pomocnicza bitmapa, łączysz ją nie wiadomo dlaczego z procesem dziedziczenia, nawet kupki instrukcji które podałem też nie rozumiesz, choć w komentarzach napisałem Ci gdzie mają się one znajdować…

Jak więc mam Ci pomóc?

0

zobacz sobie https://github.com/graphics32 - ma też coś takiego jak warstwy

0

**Fourious **- troszkę źle mnie oceniasz. Ja może mam małą wiedzę nt. samego funkcjonowania Delphi, ale mimo wszystko rozumiem co napisałeś. Mamy problem natury zrozumienia. Nie będę się wdawał w szczegóły bo bardzo mi pomogłeś, ale może przekonam Cię jakoś do siebie. Opiszę jak szedł mój proces myślowy.

Napisałem, że mruga mi przy selection tool, czyli gdy co chwila zachodzi zdarzenie MouseMove. Powiedziałeś, że potrzebuję back buffer z bitmapą, więc stwierdziłem że jak ją dodam do **Canvasu **bitmapę to ona nie będzie odświeżana póki jej nie podmienimy (rodzaj stałego tła).
Oczywiście wziąłem od razu pod uwagę fakt, że odświeża się "cały komponent", ale nie znam dokładnej zasady działania więc może rozróżnia bitmapę i elementy generowane automatycznie. Teraz już wiem, że odświeżenie dotyczy całości, więc bitmapa też mruga.

To, że bitmapę mam trzymać w pamięci to wiem, lecz trzeba ją jakoś pokazać. Jako, że **Canvas **odświeża się cały to uważam, że trzeba zrobić pod nim wyświetlanie bitmapy. O tym by generować bitmapę trzeba tylko przy modyfikacjach też wiem.

I znam różnice między CustomComponent, **GraphicComponent **i Canvas. Pomijając hierarchię w dokumentacji, to moje pytanie wynikało z tego, czy **GraphicsComponent **nie jest taką jakby lepszą wersją **CustomComponent **rozbudowaną o jakieś specjalne funkcje graficzne?

Idąc dalej: **CustomComponent **w moim wykonaniu musi mieć obsługę back buffer (którą sobie napiszę) oraz takie działanie, że warstwy wyświetlane są jako bitmapy ułożone na sobie (je można akurat złączyć podczas wyświetlania w jedną bitmapę), oraz jakiś **Canvas **na którym można wyświetlać "rysowanie" - czyli właśnie to jak używa się selection tool, bo tutaj odświeżanie musi zachować co każde wywołanie zdarzenia MouseMove.

Podczas obsługi komunikatu myszy nie musisz odświeżać ekranu, dlatego że malowaniem tego co znajduje się w kontrolce pod kursorem zajmuje się system. Jeśli przesuniesz nad komponentem okno to też wszystkim zajmie się system. Renderować obraz musisz tylko wtedy, kiedy coś wizualnie zmienia się w komponencie, np. jeśli przesuwasz element po ekranie, jeśli go rozciągasz lub zmieniasz jego kolor.
Ja maluję tylko wtedy gdy jest wciśnięty przycisk myszy. Odświeżam prostokąt, który służy jako selection tool.

Jedyne czego nie zrozumiałem to do końca działania tego kodu:

{ dodatkowe pole }
private
  FBackBuffer: TBitmap;

{ konstruktor }
FBackBuffer := TBitmap.Create();
FBackBuffer.PixelFormat := pf24Bit;
FBackBuffer.SetSize(Self.Width, Self.Height);

{ destruktor }
FBackBuffer.Free();

{ metoda zmieniająca rozmiar kontrolki }
FBackBuffer.SetSize(Self.Width, Self.Height);

{ metoda Paint kontrolki }
Self.Canvas.Draw(0, 0, FBackBuffer);

{ metoda renderująca }
FBackBuffer.BeginUpdate();
FBackBuffer.Canvas.Brush.Color := clWhite;
FBackBuffer.Canvas.FillRect(FBackBuffer.ClipRect);
// renderowanie elementów dokumentu
FBackBuffer.EndUpdate();
  1. Czemu po utworzeniu Bitmapy jest destruktor i ponownie SetSize?
  2. Czemu renderowanie jest po Draw? Czy Draw użyłeś jako stanu "defaultowego"?
  3. Zgaduję, że metoda renderująca działa tak, że przekazywana jest jakoś do funkcji Draw po updacie? Czy to jakoś integruje się z przerysowywaniem bitmapy?
  4. Zauważyłem, że ClipRect odświeża tylko zmienione pixele?

Tutaj potrzebuję jeszcze podpowiedzi.

Z chęcią poczytam trochę o przepływie danych lub sposobie działania poszczególnych komponentów/kontrolek. Polecasz jakieś materiały czy po prostu dokładniej czytać dokumentację?

0

To miało być pod cytatem, a nie w nim:

Ja maluję tylko wtedy gdy jest wciśnięty przycisk myszy. Odświeżam prostokąt, który służy jako selection tool.

0
Pan Andrzej napisał(a):

Napisałem, że mruga mi przy selection tool, czyli gdy co chwila zachodzi zdarzenie MouseMove.

Aby skutecznie manipulować zawartością ekranu, stwórz swój własny komponent dziedziczący po TCustomControl TWinControl i obsługuj komunikaty myszy, a nie zdarzenia.

Powiedziałeś, że potrzebuję back buffer z bitmapą, więc stwierdziłem że jak ją dodam do **Canvasu **bitmapę to ona nie będzie odświeżana póki jej nie podmienimy (rodzaj stałego tła).

W dalszym ciągu nie wiem co to znaczy „dodać bitmapę do Canvasu”. No bo co to znaczy? Bitmapę masz malować na płótnie, a nie ją do niej dodawać…

Edit: mała poprawka – w Delphi przy tworzeniu od podstaw kontrolek z uchwytem dziedziczy się bezpośrednio z TWinControl, a w Lazarusie z TCustomControl. Co nie zmienia faktu, że nie ma to nic wspólnego z ”lepszością” którejś z nich.


Dalszej części nie skomentuję, bo nie da się zrozumieć tego o czym napisałeś. Nie zrozumiałeś niczego o czym pisałem wcześniej, więc szkoda żebym się rozpisywał. Za dużo jeszcze nie wiesz, jeśli chodzi o tworzenie i obsługę komponentów, ogólnie o bibliotekę VCL i to w jaki sposób działa.

Musisz zająć się najpierw nauką – najpierw programowania obiektowego, następnie dobrze poznać bibliotekę komponentów i to jakimi prawami się rządzi, a na koniec nauką tworzenia własnych komponentów. W przeciwnym razie nigdy nie zrobisz tego edytora.

W sieci znajdziesz wszystko co potrzebne do nauki. Dokumentacja to jedno, ale sprawdź też tutoriale. Nie wiem z której wersji środowiska korzystasz, więc trudno wskazać konkretne materiały.

0

Cześć,
Przeczytałem jeszcze raz co napisałeś i już widzę swoje błędy. Rozumiem już jak działa kod FBackBuffer, w sensie jak go uzupełnić.

W kursach i często na forach czytam o tym, że lepiej jest stosować zdarzenia, a komunikaty tylko gdy trzeba (np.: gdy nie obsługuje je zdarzenie). Czemu uważasz że to lepsze podejście?
Przychodzi mi do głowy że może chodzić o to że daje to większą kontrolę lub/i jest szybsze.

To samo dotyczy TPaintBox - czemu uważasz że jest to źle rozwiązanie? Można napisać klasę tylko do rysowania, która dziedziczy z TPaintBox. Bardzo interesuje mnie Twoje zdanie.

0
Pan Andrzej napisał(a):

Przeczytałem jeszcze raz co napisałeś i już widzę swoje błędy. Rozumiem już jak działa kod FBackBuffer, w sensie jak go uzupełnić.

Po prostu jest to zwykła bitmapa, na której najpierw się coś maluje, a potem całą bitmapę (lub jej fragment) maluje się na płótnie komponentu lub formularza. W ten sposób tylko raz system będzie musiał zaktualizować dane na ekranie (bo jedna operacja na płótnie wizualnej kontrolki), zamiast wiele razy.

Aktualizowanie zawartości ekranu zawsze jest wąskim gardłem, dlatego back buffering jest zbawieniem.

W kursach i często na forach czytam o tym, że lepiej jest stosować zdarzenia, a komunikaty tylko gdy trzeba (np.: gdy nie obsługuje je zdarzenie). Czemu uważasz że to lepsze podejście?

W konteksie używania cudzych (gotowych i finalnych) komponentów, wygodniejsze i bezpieczniejsze jest korzystanie ze zdarzeń. Zdarzenia istnieją po to, aby kontrolka mogła w określonych sytuacjach wykonywać kod zdefiniowany nie przez autora komponentu, a przez każdego jego użytkownika. Nie ma to nic wspólnego z rozszerzaniem funkcjonalności.

Zresztą zamiast rozszerzać funkcjonalność danej kontrolki przez implementację dodatkowych funkcji w klasie formularza, nieco lepszym rozwiazaniem byłby subclassing. Po pierwsze rozszerzać się będzie klasę komponentu, po drugie, kod nowych funkcji i mechanizmów definiować się będzie poza klasą formularza, a po trzecie, zmiany obejmować będą wszystkie instancje klasy kontrolki, występujące w danym oknie. Problemem w dalszym ciągu będzie ilość kodu umieszczonego w module okna, a także przedefiniowywanie istniejących mechanizmów, zamiast ich definiowania jako domyślnych.

I tak nie zmienia to faktu, że stworzenie kontrolki od podstaw będzie najlepszym i najwygodniejszym rozwiązaniem.

Przychodzi mi do głowy że może chodzić o to że daje to większą kontrolę lub/i jest szybsze.

Nie o to chodzi – stworzenie osobnego komponentu daje o wiele więcej, niż sobie wyobrażasz.

Podstawowa sprawa to opakowanie danej funkcjonalności w samodzielnym obiekcie, uniwersalnym i niezwiązanym z żadnym projektem. Będziesz mógł wykorzystać nie jedną, a kilka instancji takiego edytora, bez konieczności duplikowania kodu (do tego nie w jednym projekcie, a w wielu). Jak zdarzy się bug to poprawisz kod w klasie komponentu, rekompilujesz go i tyle – wprowadzone zmiany będą obowiązywały we wszystkich obiektach, a tym samym we wszystkich projektach korzystających z tej kontrolki (o ile zostaną po modyfikacji przekompilowane).

To samo dotyczy TPaintBox - czemu uważasz że jest to źle rozwiązanie?

Bo kontrolka ta służy do malowania prostych rzeczy, nie implementując ani połowy funkcjonalności, jakiej wymaga Twój edytor. Jeśli pełną funkcjonalność edytora zawrzesz w klasie formularza, to wróżę niezły bajzel w jej module.

Można napisać klasę tylko do rysowania, która dziedziczy z TPaintBox. Bardzo interesuje mnie Twoje zdanie.

Rób jak uważasz, jednak moje zdanie znasz. Gdybym miał zrobić taki edytor to bez wahania zabrałbym się w pierwszej kolejności za napisanie komponentu edytora, nawet jeśli miałbym użyć jedynie jednej jego instancji. Kod kontrolki miałbym w osobnym module, więc o wiele łatwiej było by go rozwijać.

0

Zerknij na bibliotekę AggPas i jej przykłady http://www.crossgl.com/aggpas/documentation/

0

furious programming dzięki Ci jeszcze raz za poświęcony czas. Miejscami się nie zrozumieliśmy, ze względu na moje nietechniczne określenia.

Mówiąc o zrozumieniu kodu, chodziło mi o pytania które zadawałem. Wcześniej podszedłem do kodu jak do gotowca, a nie do szablonu który trzeba uzupełnić o kod. Zasadę działania rozumiem od samego początku.

Obecnie też dokształcam się od strony teoretycznej.

AggPas - słyszałem. Z chęcią poczytam, dzięki :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1