Archive for the ‘Delphi’ Category

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.

Neue Entwicklungsumgebung heißt neue Möglichkeiten und neue Probleme

Samstag, Dezember 31st, 2011

Mit der letzten Programmveröffentlichung hatte ich es bereits angemerkt, das ich wohl zu einer neueren Version meiner Entwicklungsumgebung umsteigen werde. Vor Weihnachten habe ich mir dann ein großes Geschenk gemacht und das neue Embarcadero Rad Studio XE2 bestellt. Vorgestern wurde es dann geliefert und ich habe es natürlich sogleich installiert. Nach einer problemlosen Installation war meine Neugier groß, wie die Konvertierung des BVASystem Projektes zur neuen Entwicklungsumgebung verlaufen wird.

Mittlerweile kann ich einen groben Überblick geben, was mich/euch erwartet. Die gute Nachricht: Das Programm lässt sich wieder übersetzen und es ist auf dem ersten Blick auch voll lauffähig. Die Schlechte Nachricht: Es gibt auch einige Baustellen (meist optischer Natur), die wohl relativ schnell abgearbeitet werden müssen.

Was wird sich in naher Zukunft ändern?

Im alten Delphi 2005 waren alle Steuerelemente nicht Unicode fähig. Spezielle Sonderzeichen, die nicht darstellbar waren, wurden einfach durch ein Fragezeichen ersetzt. Im Alltag ist dies sicher nicht weiter dramatisch gewesen, da man sich meist mit Alternativen behelfen konnte. Ich war dieses Jahr beispielsweise in Årgab im Urlaub und dieser Name konnte bisher nicht im BVASystem verwendet werden. Stattdessen hab ich einfach Argab beim Verwalten meiner Bilder genutzt, um dem Problem aus dem Weg zu gehen. Das Rad Studio XE2 arbeit nun komplett mit Unicode Zeichenketten.

In diesem Zusammenhang  zerbrech ich mir gerade ein wenig den Kopf, ob alle anderen Zeichenketten im Programm nun noch korrekt sind. Ich weiß gerade nichteinmal, ob die MYSQL-Datenbank so erzeugt wurde, das sie Unicode unterstützt. Ich werde die nächsten Tage wohl testen müssen, ob ein Fotoalbum „Årgab“ nun möglich ist oder ob Korrekturbedarf besteht. Gestern abend stolperte ich jedenfalls gleich in eine Unicode-Falle. Bisher habe ich recht gerne Zeichenketten bzw Stringstreams genutzt um binäre Daten zu verändern. Nun geht das nicht mehr so einfach, da ein falsch eingestelltes Encoding die Binärdaten zerstört.

Eine wunderschöne Neuerung sind die VCL-Styles. Sie ermöglichen es, das das Aussehen der Programmoberfläche komplett selbst gestaltet werden kann. Bisher habe ich mich damit herrumgequält, das die Anwendung meinen optischen Wünschen genügt. Bisher konnte ich kein XPManifest benutzen, da meine dezent braune Oberfläche durch blaue Scrollbars zerstört wurde. Daher sahen die Scrollbars im BVASystem bisher noch so aus, wie sie zur guten alten Windows95 Zeit ausgesehen haben. Im Rad Studio XE2 ist nun standardmäßig ein moderner Look aktiv. Er kann aber mit einem selbsterstellten VCL-Style überschrieben werden. Da ich immernoch keine blauen Scrollbars möchte, werde ich dies wohl auch tun müssen. Erste Versuche brachten folgendes:

Oberflächenentwurf mit eigenem VCL-Stil

Oberflächenentwurf mit eigenem VCL-Stil

Neu ist auch der 64bit Compiler. Kleinere Testanwendungen habe ich bereits als 32bit und als 64bit Variante übersetzen können. Ob ich das BVASystem ebendso einfach auf 64bit bringen kann, weiß ich noch nicht. Die Datenbankverbindung, die zur Zeit über die „libmySQL50.dll“ realisiert wird, muss ja ebendso durch eine 64bit Variante ersetzt werden. Ich würde es jedenfalls schön finden, wenn ich irgendwann einmal eine 64bit Version vom BVASystem anbieten kann. Aktuell ist dies allerdings noch nicht so wichtig.

Fazit

Im großen und ganzen bin ich mit dem Umstieg zufrieden. Die Entwicklungsumgebung wirkt vertraut, die Eingewöhnung wird also nicht all zuviel Zeit benötigen. Das BVASystem ist übersetzbar und läuft bis auf ein paar optische Unschönheiten auch. Insgesamt verspreche ich mir durch das modernere Erscheinungsbild der Software deutliche Vorteile. Allerdings muss ich dafür erst einmal die Konvertierung des Projektes abschließen.

Seltsames Hint-Verhalten

Mittwoch, Juni 15th, 2011
PageControl mit Hint

PageControl mit Hint

Ich habe ja schon viele komische Sachen in der Zeit erlebt, in der ich Delphi programmiere. Das Hint-Verhalten der TPageControl und TTabsheet Komponenten ist mir völlig unlogisch. Dem Tabsheets kann man wunderbar jeweils einen Hint zuweisen. Dieser wird aber nur angezeigt, wenn man sich innerhalb des Clientbereiches des Tabsheets bewegt. Wofür braucht man da bitte schön einen Hint? In dem Bereich liegen normalerweise weitere Oberflächenelemente mit eigenständigen Funktionen. Dort wo man den Hint bräuchte, nämlich auf dem Reiter zum wechseln der Tabsheets, erscheint er nicht. Auf den Reitern wird der Hint angezeigt, den man dem PageControl zugewiesen hat.  Also für alle Reiter der gleiche.

TBVAPageControl

Nun habe ich mir die Mühe gemacht, dieses Verhalten dahingehend zu korrigieren, das es für meine Zwecke einsetzbar ist. Ich habe eine  neue Komponente TBVAPageControl von TPageControl abgeleitet. Falls jemand das TBVAPageControl benötigen sollte: Hier ist es.

Das neue PageControl kann genauso wie das TPageControl verwendet werden, nur das Hint-Verhalten ist halt ein anderes. Beim TBVAPageControl werden die Hints nur noch auf den Reitern angezeigt. Sie sollen als Erklärung dienen, was dem Anwender beim Wechsel auf die jeweilige Seite erwartet. Die Tabsheets selber zeigen keine Hints an und auch das PageControl hat keinen eigenen Hint mehr. In der Entwicklungsumgebung setzt man seine Wunschhints einfach bei den jeweiligen Tabsheets. Der Hint des PageControls bleibt leer.

Wie funktioniert das ganze?

Eigentlich ist das TBVAPageControl ziemlich simpel. Im Mousemove Event wird überprüft, welcher der Reiter sich an der Mausposition befindet. Wenn ein Reiter bestimmt werden konnte, wird der Hint des jeweiligen Tabsheets dem PageControl zugewiesen. Befindet sich die Maus auf keinem Reiter oder verlässt die Maus das PageControl, so wird der Hint des PageControls wieder gelöscht.  Zu erwähnen ist vielleicht noch, das ich das PageControl um ein OnMouseEnter und ein OnMouseLeave Event erweitert habe, da diese in der Standardimplementierung nicht vorhanden waren.