Posts Tagged ‘Delphi’

Ich habe mich entschieden …

Dienstag, April 19th, 2016

Nachdem ich die letzten Wochen voller Zweifel war, habe ich mich nun entschieden, wie es weitergehen soll. Nach einigen Testversuchen bin ich der Meinung, einen Weg gefunden zu haben, mit dem ich ein BVASystem für Desktop-Rechner und Android-Geräte gleichzeitig entwickeln kann, ohne das ich zwei getrennte Projekte stemmen muss. Nun ja, eigentlich will ich doch zwei Projekte stemmen, denn das jetzige BVASystem-Projekt ist mir zum einmotten zu schade.

Daher habe ich mich entschieden, ab jetzt zweigleisig zu fahren.  Auf der einen Seite werde ich das Delphi Windows Projekt fortführen. Bei der Weiterentwicklung geht es mir da aktuell hauptsächlich um die begonnene Suche nach ähnlichen Fotos. Größere Änderungen, wie die lokale SQLite-Datenbank, werde ich in dem Projekt aber wohl nicht mehr implementieren. Diese möchte ich nun erstmal über den neuen Weg probieren. Sollte dies gut funktionieren, werde ich dann Stück für Stück die bestehenden Funktionen im neuen Projekt nach implementieren. Das wichtigste dabei ist für mich, das auch das neue Projekt auf die bestehende Datenbank/Datenbankstruktur zugreifen kann. Sollte es wieder erwarten größere Probleme mit dem neuen Projekt geben, habe ich das Delphi Projekt um dort weiterzumachen.

In der Zeit, an der ich an beiden Projekten arbeite, wird es eine kleine Start-Anwendung für das BVASystem geben, in der ihr auswählen könnt, mit welchem der beiden Windows-Programme ihr arbeiten wollt. Wie ich allerdings die Android-Version veröffentlichen kann, weiß ich noch nicht. Durch meine Tests habe ich zwar bereits eine installierbare APK-Datei, allerdings lässt sie sich nur installieren, wenn man auf dem Android-Gerät Software aus unbekannten Quellen zulässt. Und ob der Google-Play-Store für eine Entwickler-Version der richtige Ort ist, bezweifele ich stark. Aber vielleicht geht es ja doch, wenn das Projekt etwas weiter fortgeschritten ist. Bis dahin müsst ihr aber wohl oder übel, mit Fotos von meinem Handy vorlieb nehmen …

Foto der Testanwendung auf einem Handy

Foto der Testanwendung auf einem Handy

In den nächsten Tagen werde ich mich um die Start-Anwendung kümmern und um die Berechnung der Merkmale für die Suche nach ähnlichen Bildern. Anschließend werde ich nochmal einen Blog schreiben, in dem ich euch die Technologie vorstellen möchte, die ich nun einsetzen möchte.

TStream -> Variant und zurück

Dienstag, Januar 20th, 2015

Zwar habe ich jetzt noch keine neue Version vom BVASystem fertig, aber trotzdem möchte ich meinen Entschluss, hier wieder öfters etwas zu schreiben, in die Tat umsetzen. Der Anlass dieses Blogs ist, das ich gerade sehr glücklich bin, da ich endlich sehen konnte, das meine internen Umstellarbeiten funktionieren. Bisher habe ich mehr oder weniger blind programmiert, ohne zu wissen, ob die gesetzten Ziele damit erreicht werden können. Jetzt, wo ich bereits 3 Worker-Tasks auf die generalisierte Datenbank umgestellt habe, weiß ich es. Und das schönste daran, ich komme nach der Umstellung mit deutlich weniger Code-Zeilen aus. Wiederkehrende Funktionen, wie zum Beispiel das Abfragen eines einzelnen Datenbankwertes habe ich allgemein gültig direkt in den generalisierten DB-Komponenten implementiert. So brauche ich jetzt nur noch eine einzige Zeile, wo ich vor der Umstellung zwischen 10 und 15 Zeilen gebraucht habe.

Als letztes Problem musste ich mich in den letzten Tagen noch um die Blob-Datenfelder kümmern. Diese ließen sich nämlich nicht  direkt wie alle anderen Parametern in einen SQL-Befehl codieren. Im speziellen musste ich ziemlich lange herumprobieren, bis ich ein Stream-Objekt in einen Variant und wieder zurück konvertieren konnte. Falls ihr mal vor einem ähnlichen Delphi-Problem stehen solltet ist hier meine Lösung:

function StreamToVariant(AStream:TStream):Variant;
var Hilf:Variant;
    P:Pointer;
begin
 VarClear(Hilf);
 Hilf := VarArrayCreate([0, AStream.Size−1], varByte);
 if AStream.Size > 0 then begin
  P := VarArrayLock(Hilf);
  AStream.ReadBuffer(P^, AStream.Size);
  VarArrayUnlock(Hilf);
 end;
 Result := Hilf;
end;
function VariantToStream(AVariant:Variant):TStream;
var S:TStream;
    P: Pointer;
begin
 S:= TMemoryStream.Create;
 P := VarArrayLock( AVariant );
 try
  S.Write(P^, VarArrayHighBound(AVariant,1)+1);
 finally
  VarArrayUnlock(AVariant);
 end;
 S.Position := 0;
 Result := S;
end;

Verbesserte Objekt-Stringlisten

Donnerstag, März 6th, 2014

Ich bin mittlerweile dazu übergegangen, Stringlisten mit Objekten zur Verwaltung meiner internen Datenstrukturen zu verwenden. Für mich haben sie den Vorteil, das man sie durch eine einfache Einstellung von Anfang an sortiert aufbauen kann. Gerade die Suche nach einem Element der Liste wird dadurch viel effizienter, da dafür eine binäre Suche angewendet wird. Allerdings habe ich recht häufig vergessen, das ein einfaches freigeben der Liste nicht reicht, um sämtlichen Speicherplatz wieder freizugeben. Die Objekte blieben dadurch häufig stehen. Um diese Fehlerquelle für immer auszuschließen, habe ich mich entschlossenen, die Stringlisten-Klasse zu erweitern. Dazu musste ich 2 Funktionen der Stringlistenklasse überschreiben und eine neue hinzufügen.

  • Der Destruktor „Destroy()“ leert nun die Stringliste, bevor das Listenobjekt freigegeben wird.
  • Die Funktion „Clear()“, die zum leeren der Liste dient, gibt nun eventuell vorhandene Objekte frei.
  • Die neue Funktion „DeleteObject()“ löscht ein Element aus der Stringliste und gibt das dazugehörige Objekt frei.

Weiterhin behinderte mich die Tatsache, das man die Zeichenkette eines Elementes nicht verändern konnte, wenn die Liste sortiert ist. Da ich nun bereits eine eigene Klasse hatte, kümmerte ich mich auch gleich um dieses Problem. Hier reichte es aus, eine einzelne Funktion zu überschreiben.

  • In der Funktion „Put()“ wird nun das alte  Element aus der Liste gelöscht und ein neues mit der geänderten Zeichenkette erstellt. Der Zeiger auf das Objekt wird dabei übernommen.

Hier der Quelltext der kompletten Klasse:

unit claNVStringList;

interface

uses classes;

type
TNVStringList = class(TStringList)
private
protected
public
{*** Destruktor der Klasse ***}
destructor Destroy; override;
{*** Löscht alle Items der Liste ***}
procedure Clear; override;
{*** Löscht ein Item und das dazugehörige Object aus der Liste ***}
procedure DeleteObject(AIndex:Integer);
{*** Austausch eines Strings in der Sortierten Liste ***}
procedure Put(AIndex: Integer; const AValue: string); override;
end;

implementation

{*** Destruktor der Klasse ***}
destructor TNVStringList.Destroy;
begin
Clear;

inherited Destroy;
end;

{*** Löscht alle Items der Liste ***}
procedure TNVStringList.Clear;
var I:Integer;
begin
for I := 0 to Count-1 do begin
if Objects[I] <> nil then begin
Objects[I].Free;
end;
end;

inherited Clear;
end;

{*** Löscht ein Item und das dazugehörige Object aus der Liste ***}
procedure TNVStringList.DeleteObject(AIndex:Integer);
begin
if Objects[AIndex] <> nil then begin
Objects[AIndex].Free;
end;
Delete(AIndex);
end;

{*** Austausch eines Strings in der Sortierten Liste ***}
procedure TNVStringList.Put(AIndex: Integer; const AValue: string);
var Obj:TObject;
begin
if Sorted then begin
Obj := Objects[AIndex];
Delete(AIndex);
if Obj = nil then begin
Add(AValue);
end else begin
AddObject(AValue,Obj);
end;
end else begin
inherited Put(AIndex,AValue);
end;
end;

end.

Fehlerkorrektur im Popup-Fenster Grundgerüst

Donnerstag, August 22nd, 2013

Vor einiger Zeit veröffentlichte ich einen Artikel, in dem ich beschrieb, wie man in Delphi ein Popupfenster implementiert. Gestern musste ich, als ich ein Popupfenster nicht auf dem Hauptdialog der Anwendung einbinden wollte feststellen, das sich in meinem Grundgerüst ein Fehler eingeschlichen hat. 

In dem WMACTIVATE Event wird eine Windowsbotschaft versendet, so das das bisher aktive Fenster auch weiterhin als aktives Fenster dargestellt wird. Der Fehler daran war, das ich die Botschaft immer an das Hauptfenster (Application.MainForm.Handle) der Anwendung versendet habe. Korrekter Weise müsste es an das Formular gesendet werden, von dem aus das Popup geöffnet wurde. Das korrigierte WMACTIVATE Event sieht wie folgt aus:

procedure TfrmBVAImageInfo.WmActivate(var Msg: TWMActivate);
var Parent:TCustomForm;
begin
if FOwner <> nil then begin
Parent := GetParentForm(TControl(FOwner));
if (Parent <> nil) and (not(csDestroying in Parent.ComponentState)) then begin
SendMessage(Parent.Handle, WM_NCACTIVATE, Ord(Msg.Active <> WA_INACTIVE), 0);
end;
end;

inherited;
end;

Außerdem musste ich noch folgenden Code in dem Construktor des Popup-Fensters einfügen, damit das Popup-Fenster auch immer vor allen anderen Dialogen angezeigt wird.

constructor TfrmBVAImageInfo.Create(aOwner: TComponent);
begin
inherited;
FOwner := aOwner;

SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_APPWINDOW or WS_EX_TOPMOST);
SetWindowLong(Handle, GWL_HWNDPARENT, GetDesktopWindow);
end;

Gestenerkennung mit TImage32

Montag, November 19th, 2012

Gestern musste ich feststellen, das die Maus-Gestenerkennung in der Komponente TImage32 vom Graphics32-Project nicht unterstütz wird. Die notwendige Eigenschaft Touch und auch das OnGesture() Event sind dort nicht vorhanden. Auch die Gestenerkennung der übergeordneten Komponente ließ sich nicht dazu bewegen, auf Maus-Gesten im TImage32-Bereich zu reagieren. Daher habe ich mir eine eigene Komponente geschrieben, die TImage32 um die Gesten erweitert.

unit claBVAImage32;

interface

uses
System.SysUtils, System.Classes, Vcl.Controls, GR32_Image;

type
TBVAImage32 = class(TImage32)
private
protected
public
constructor Create(AOwner: TComponent); override;
published
property Touch;
property OnGesture;
end;

implementation

constructor TBVAImage32.Create(AOwner: TComponent);
begin
inherited;

ControlStyle := ControlStyle + [csGestures];
end;

Prinzipiell müsste der Lösungsweg bei jeder Komponente funktionieren, bei der die Gesten nicht funktionieren, da die Implementation der Gestensteuerung bereits in TControl vorhanden ist. Im Falle von TImage32 fehlte einfach das „csGestures“ im ControlStyle.

Maus-Gestenerkennung mit Delphi

Freitag, November 16th, 2012

Von Handys bzw. von Tablet-PCs kennt man bereits die Erkennung von Gesten, die man per Finger mehr oder weniger auf die Displays schmiert. Spätestens mit Windows 8 dringt diese Technik nun auch in die Windows-Welt vor.  Daher habe ich mir vorgenommen, zu prüfen, ob der Einsatz einer Gestensteuerung im BVASystem sinnvoll ist und mit welchem Aufwand es sich umsetzten lässt. Als Ergebnis ist nun erstmal eine kleine Mini-Anleitung entstanden, die beschreibt, wie man Gesten in Delphi einsetzen kann.

Glücklicherweise muss man die Erkennung der Gesten in Delphi nicht selber entwickeln. Sogar einige gut funktionierende Standardgesten sind bereits definiert. Generell würde ich erst einmal empfehlen, keine eigenen Gesten zu definieren. Meine Versuche eine „6“ als Geste zu definieren, scheiterten daran, das sie zu nah an meiner Handschrift lagen. Durch einen vergrößerten Toleranzbereich konnte ich das Problem entschärfen. Allerdings wurden dann plötzlich auch andere Gesten als „6“ erkannt.

Um in der eigenen Anwendung Gesten zu verwenden, sind nur einige kleine Schritte notwendig:

Hinzufügen eines  TGesturemanagers

Der erste und einfachste Schritt ist das Hinzufügen einer Komponente vom Typ TGesturemanager zum Dialog, in dem die Gesten verwendet werden sollen. Den Gestenmanager kann man sich wie eine Gesten-Bibliothek vorstellen. Er dient zur Definition und Speicherung der Gesten, die verwendet werden sollen.  Nach dem Hinzufügen enthält er bereits 34 definierte Standardgesten. Zusätzliche eigene Gesten können definiert werden, indem man doppelt auf die Komponente klickt.

TGesturemangager

TGesturemangager

Verknüpfen des Gestenmanagers

Als nächstes muss die Komponente bzw. die Komponenten, die auf Gesten reagieren sollen, mit dem Gestenmanager verknüpft werden. Dazu sucht man im Objekt Inspektor nach der Eigenschaft „Touch“. Als Untereintrag davon findet man den Eintrag „Gesturemanager“ und wählt dort den eben hinzugefügten Gestenmanager aus.

Auswahl der Gesten

Anschließend kann man unter „Gestures“ die gewünschten Gesten auswählen, indem man auf die jeweiligen Checkboxen klickt. Nun hat man 2 Möglichkeiten:  Die einfachere Möglichkeit ist sicher, wenn man die Geste direkt im Objektinspektor mit einer TAction verknüpft.

Gestenauswahl im Objektinspektor

Gestenauswahl im Objektinspektor

Hat man keine TAction verwendet, so kann die gewünschte Funktionalität in dem Event OnGesture() implementieren. Der Quelltext dazu könnte zum Beispiel in etwa so aussehen:

procedure TBVAVollbild.ImageGesture(Sender: TObject; const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
case EventInfo.GestureID of
sgiLeft: begin
showmessage('Nach Links');
end;
sgiRight: begin
showmessage('Nach rechts');
end
else showmessage('Unbekannte Geste');
end;
end;

Anschließend kann nun bereits getestet werden, ob die ausgewählten Gesten funktionieren.

Wie erstelle ich ein Popupfenster?

Sonntag, Januar 29th, 2012

Popupmenüs kennt wohl jeder Windowsnutzer, aber warum sollte der Mechanismus nicht genutzt werden, um komplexere Aufgaben  zu übernehmen. Mit diesem Blog möchte ich kurz beschreiben, wie man ein Popupfenster erstellt, auf dem man dann beliebige Komponenten platzieren kann. Das Popupfenster basiert auf einem einfachen Formular, bei dem allerdings einige Dinge berücksichtigt werden müssen.

Keinen Rahmen

Damit das Popupfenster einem Popupmenü ähnlich sieht, darf das Formular keinen Rahmen haben. Setzt dafür einfach die Eigenschaft BorderStyle des Formulars auf bsNone. Zusätzlich platziere ich meist ein TPanel auf das Formular (alClient), damit ich einen sichtbaren Rand erhalte.

Schließen des Popups

Da das Forumlar keinen Rahmen besitzt, gibt es erstmal keine Möglichkeit, um dieses zu schließen, wenn es einmal angezeigt wird. Folgendes Event für  das Panel schafft Abhilfe:

procedure TfrmBVAImageInfo.pClientClick(Sender: TObject);
begin
 Close;
end;

Damit das Popup-Fenster ebenfalls geschlossen wird, wenn man außerhalb des Fensters auf die darunterliegende Anwendung klickt, wird folgendes Event benötigt:

procedure TfrmBVAImageInfo.FormDeactivate(Sender: TObject);
begin
 Close;
end;

Anzeige des Popupfensters

Damit das FormDeactivate Event ausgelöst werden kann, darf das Popupfenster nicht Modal geöffnet werden. Außerdem muss beim Anzeigen den Fensters die Position des Formulars, an die aktuelle Mausposition verschoben werden. Hierfür kann folgende Funktion genutzt werden:

procedure TBVAThumbnail.ShowImageInfo(aX,aY:Integer; aItem:TBVADLDataListItem);
begin
 if aX+FInfo.Width < Screen.DesktopWidth-10
  then FInfo.Left := aX
  else FInfo.Left := Screen.DesktopWidth-FInfo.Width-10;
 if aY+FInfo.Height < Screen.DesktopHeight -10
  then FInfo.Top  := aY
  else FInfo.Top  := Screen.DesktopHeight-FInfo.Height-10;
 FInfo.Show;
end;

FShow ist hierbei das Formular des Popupfensters und aX bzw aY die aktuelle Mausposition, die man aus dem jeweiligen Mausevent erhält, welches das Popup öffnen soll. Durch die beiden If-Abfragen wird sichergestellt, das das Popupfenster immer ganz sichtbar ist.

WMActivate

Zu guter letzt benötigt man noch einen kleinen Trick, damit während das Popupfenster angezeigt wird, das Hauptfenster der Anwendung weiterhin als aktives Fenster sichtbar bleibt:

procedure TfrmBVAImageInfo.WmActivate(var Msg: TWMActivate);
begin
  SendMessage(Application.MainForm.Handle, WM_NCACTIVATE, Ord(Msg.Active <> WA_INACTIVE), 0);
  inherited;
end;

Das fertige Popupfenster sieht dann wie folgt aus:

Bildinformationspopup

Bildinformationspopup

Komponentenentwicklung: Nutzung vom VCL Style

Donnerstag, Januar 19th, 2012

Heute ist es wieder einmal an der Zeit, einen kurzen technischen Artikel über Delphi zu schreiben. Und zwar geht es im Speziellen darum, wie man einzelne Elemente, die im VCL-Style definiert wurden, in eigenen Komponenten benutzen kann.

Ich nutze ganz gerne einfache Zeichenflächen um meine eigenen Komponenten zu entwickeln, da ich dort gestalterisch alle Freiheiten besitze. Um ein einheitliches Design zu gewährleisten, sollten hierbei aber trotzdem die VCL Style Elemente verwendet werden.

Um beispielsweise einen Knopf auf ein Bitmap zu zeichnen, kann man folgende Anweisungen benutzen:

if StyleServices.Available then begin
 //Auswahl des Styleelements
 case aMode of
  tpDown   : Details := StyleServices.GetElementDetails(ttbButtonPressed);
  tpUp     : Details := StyleServices.GetElementDetails(ttbButtonHot);
 end;
 //Zeichnen des Styleelements
 StyleServices.DrawElement(FBitmap.Canvas.Handle, Details, aButtonRect);
end else begin
 ... //Alternative Zeichenmethode, falls kein Style aktiv ist
end;

Mit der Variablen aMode wird festgelegt, ob der Button im gedrückten oder ungedrückten Zustand gezeichnet werden soll und aButtonRect definiert den Bildbereich über den sich der Button erstrecken soll.

Hintergrundfarbe eines TPanels ändern

Sonntag, Januar 8th, 2012

Das ich bereits nach wenigen Tagen Entwicklung mit der neuen Umgebung an eine Grenze des machbaren stoße, hätte ich nicht erwartet. Und dabei geht es eigentlich um etwas total banales: Die Änderung der Hintergrundfarbe eines Panels.

Worin besteht das Problem?

Das Konzept von Embarcadero sieht vor, das bei aktivem VCL-Stil, das komplette Aussehen der Steuerelemente durch den VCL-Stil definiert wird. Will man trotzdem eine Eigenschaft ändern, die Auswirkungen auf die Optik hätte, so passiert einfach garnichts. Im speziellen: Die Hintergrundfarbe kann nicht geändert werden. Alle Panels sehen genauso aus, wie sie im VCL-Stil Editor eingestellt wurden.

Mein großes Problem dabei ist, das mein zweifarbiges Design mit den grauen und hellbraunen Panels mit dem VCL-Stil nicht umsetzbar ist. Ich brauche den VCL-Stil aber, damit die Windows-Defaults nicht die Farbgestaltung zunichte machen.

Wie kann man das Problem umgehen?

Da Embarcadero das Verhalten wohl nicht ändern will, habe ich mir einen Workaround für das Panel gesucht, da ich auf mein Wunschdesign nicht verzichten will. Der Workaround besteht darin, einfach auf dem Panel, welches eine andere Farbe erhalten soll, ein maximiertes TShape Element zu plazieren. Die Hintergrundfarbe kann dann über das TShape gesteuert werden, da das eigentliche Panel nicht mehr sichtbar ist.

Damit das TShape nicht bei jedem Panel manuell hinzugefügt werden muss, habe ich einfach fix ein TBVAShapePanel entwickelt, auf dem das TShape Element bereits vorhanden ist. Die Eigenschaft „Color“ des Panels hab ich einfach überschrieben, da sie ja eh nutzlos gewesen ist. Das TShapePanel sieht wie folgt aus:

unit claBVAShapePanel;

interface

uses controls, SysUtils, classes, ExtCtrls, graphics;

type
 TBVAShapePanel = class(TPanel)
  private
   FShape  : TShape;
   FColor  : TColor;
  protected
   procedure SetColor(aValue:TColor);
  public
   constructor Create(AOwner: TComponent); override;
   destructor Destroy; override;
  published
   property Color:TColor read FColor write SetColor;
end;

procedure Register;

implementation

procedure TBVAShapePanel.SetColor(aValue:TColor);
begin
  FColor := aValue;
  FShape.Brush.Color := FColor;
end;

constructor TBVAShapePanel.Create(AOwner: TComponent);
begin
 inherited Create(AOwner);

 BevelKind          := bkNone;
 BevelOuter         := bvNone;
 FColor             := clRed

 FShape             := TShape.Create(Self);
 FShape.Parent      := Self;
 FShape.Align       := alClient;
 FShape.Brush.Color := FColor;
 FShape.Brush.Style := bsSolid;
end;

destructor TBVAShapePanel.Destroy;
begin
 FShape.Free;

 inherited Destroy;
end;

(******************************************************************************)

procedure Register;
begin
 RegisterComponents('BVA',[TBVAShapePanel]);
end;

end.

Natürlich kann das TBVAShapePanel noch nach belieben um weitere Eigenschaften erweitert werden. Anbieten würde sich zum Beispiel, das man den Rand des TShape ebenfalls variabel gestaltet.

Auch wenn ich für die Hintergrundfarbe nun eine Lösung gefunden habe, würde ich begrüßen, wenn Embarcadero sein Konzept nochmal überdenken würde. Prinzipell ist es richtig, das die Optik aus dem Stil geladen wird. Aber wenn zum Beispiel durch farbige Edit-Felder dem Anwender signalisiert werden soll, wo er fehlerhafte Daten eingetragen hat, so ist dies aktuell nicht möglich. Auch mit dem Trick mit dem TShape kommt man da wohl nicht weiter.

Scrollbars einer TScrollbox

Dienstag, Mai 10th, 2011

In der letzten Zeit scheinen sich die Delphi Problemchen wieder zu häufen. Dieses mal hat mich die TScrollbox fast 2 Tage gekostet. Aber der Reihe nach:

Ich bin gerade dabei, eine Komponente zu entwickeln, mit der Metadaten, die zu einem Fotoalbum gehören, definiert werden können. Da eine beliebige Anzahl von Metadaten definiert werden kann, ist es notwendig, die jeweiligen Steuerelemente in eine Scrollbox anzuordnen.

Da es die erste TScrollbox ist, die ich im BVASystem benutze, fiel mir gleich ein grundlegendes Problem auf: Die Scrollbars der Scrollbox sahen optisch anders aus, als die in den selbstgeschriebenen Komponenten.

In Delphi 2005  sehen  die Steuerelemente standardmäßig so aus wie unter Windows 2000. Durch das Einbinden der Komponente TXPManifest kann das Aussehen auf den XP Style umgestellt werden.

Durch die selbstgeschriebenen Komponenten habe ich bisher darauf verzichtet, das Manifest in die Bilddatenbank zu integrieren. Es gab zu viele unschöne Effekte, die ich hätte korrigieren müssen. Außerdem finde ich es unschön, wenn das komplett in Brauntönen gehaltene Design durch blaue Scrollbars gestört wird.

Das Problem bei der TScrollbox war nun, das die Scrollbars der Scrollbox automatisch im XP-Style sind, auch wenn kein Manifest eingebunden wurde. Warum das so ist, habe ich leider nicht herrausfinden können. Aber ich habe eine Möglichkeit gefunden, wie man den XP-Style der Scrollbox wieder deaktivieren kann:

SetWindowTheme(Scrollbox.Handle, '', '');

Mit diesem schnöden Einzeiler sind die Scrollbars zumindest erstmal wieder einheitlich. Früher oder später, werde ich mich allerdings damit auseinander setzen müssen, wie ich mit der Oberflächengestaltung weiter umgehen werde. Wirklich toll sehen die Scrollbars bei deaktiviertem XP-Style nicht aus. Schlussendlich bleiben 2 Möglichkeiten: 1. Eigene hübschere Scrollbars entwickeln oder 2. den XP-Style Anwendungsweit aktivieren.