![]() |
Firemonkey Workarounds
Hallo,
da ich während meiner Entwicklung unter Firemonkey über einige Bugs gestolpert bin, die man teilweise recht einfach umgehend kann, möchte ich hier immer mal wieder einige Workarounds reinschreiben. Vielleicht klinkt sich ja der eine oder andere an. Und vielleicht geschieht ja ein Wunder und jemand von Embar liest mit ... :) Betrifft alles XE2, aktuell Hotfix 4 - OpenDialog ist unter Windows nicht Modal
Delphi-Quellcode:
- TTreeview: "Accept" in OnDragOver wird ignoriert
Uses {$IFDEF MSWINDOWS}Winapi.Windows, FMX.Platform.Win,{$ENDIF}
{$IFDEF MSWINDOWS} Try EnableWindow(FmxHandleToHWND(Self.Handle), False); Except End; {$ENDIF} Try OpenDialog.Execute; .... Finally {$IFDEF MSWINDOWS} SetActiveWindow(FmxHandleToHWND(Self.Handle)); EnableWindow(FmxHandleToHWND(Self.Handle), True); {$ENDIF} End; FMX.TreeView.pas und FMX.Defines.inc ins Projektverzeichnis kopieren, damit das Projekt immer die lokale verwendet. FMX.TreeView.pas öffnen und zu TCustomTreeView.DragOver springen. Dort den Anfang wie folgt auswechseln:
Delphi-Quellcode:
- TListbox: "Accept" in OnDragOver wird ignoriert, D&D funktioniert nicht
procedure TCustomTreeView.DragOver(const Data: TDragObject; const Point: TPointF;
var Accept: Boolean); var Obj: TTreeViewItem; begin // Änderungen // inherited; inherited DragOver(Data, Point, Accept); if assigned(OnDragOver) then exit; // ... FMX.Listbox.pas und FMX.Defines.inc ins Projektverzeichnis kopieren, damit das Projekt immer die lokale verwendet. FMX.Listbox.pas öffnen und zu TCustomListBox.DragOver springen. Dort den Anfang wie folgt auswechseln:
Delphi-Quellcode:
- Open-/Savedialog: Der Dialog wird unter Mac OS nicht geöffnet, Execute liefert false zurück
procedure TCustomListBox.DragOver(const Data: TDragObject; const Point: TPointF; var Accept: Boolean);
var Obj: TListBoxItem; begin // Änderungen // inherited; inherited DragOver(Data, Point, Accept); if assigned(OnDragOver) then exit; // ... Unter MacOS muss der Filter leer sein. Den Dialog daher wie folgt öffnen
Delphi-Quellcode:
- TDropTarget: "Accept" wird in OnDragOver nicht akzeptiert
{$IFDEF MACOS} Try OpenDlg.Filter := ''; Except End; {$ENDIF} // Geht unter Mac nicht
OpenDlg.Execute; ... FMX.ExtCtrls und FMX.Defines.inc ins Projektverzeichnis kopieren, damit das Projekt immer die lokale verwendet. FMX.ExtCtrls öffnen und zu TDropTarget.DragOver springen. Dort den Anfang wie folgt auswechseln:
Delphi-Quellcode:
- THeader: Werden HeaderItems hinzugefügt, ist die .FMX Datei anschließend defekt
procedure TDropTarget.DragOver(const Data: TDragObject; const Point: TPointF;
var Accept: Boolean); begin // Änderungen // inherited; inherited DragOver(Data, Point, Accept); // ... Beim laden der Datei kam es zum Meldung "Ungültige Eigenschaft Headeritem1.Width, außerdem sind plötzlich andere Komponenten auf der Form verschwunden. Einzig mir bekannter Workaround: THeaderItem dynamisch erzeugen:
Delphi-Quellcode:
In OnCreate dann:
TMainForm = class(TForm)
... privat HeaderItem1: THeaderItem; HeaderItem2: THeaderItem; ... public ...
Delphi-Quellcode:
HeaderItem1 := THeaderItem.Create(nil);
HeaderItem1.Parent := Header1; HeaderItem1.Width := 50; HeaderItem1.Text :='HeaderItem1'; Header1.AddObject(HeaderItem1); HeaderItem2 := THeaderItem.Create(nil); HeaderItem2.Parent := Header1; HeaderItem2.Width := 50; HeaderItem2.Text :='HeaderItem2'; Header1.AddObject(HeaderItem1); |
AW: Firemonkey Workarounds
Zitat:
(Was ja hieße es soll nicht gefixt werden, denn was man nicht weiß kann man auch nicht fixen.) Vor allem würden in der QC die Hinweise zu den Fixes auch von anderen gefunden werden, die damit Probleme haben. :wink: |
AW: Firemonkey Workarounds
|
AW: Firemonkey Workarounds
Oh wie schlimm ...
Nein im Ernst, ich habe kaum Lust bei QC zu suchen und versuche erstmal die Delphi-Quelle meiner Wahl: DP. |
AW: Firemonkey Workarounds
- Mac: Anwendung wird plötzlich nicht mehr gestartet
Nach einem Absturz wird die Anwendung plötzlich nicht mehr auf dem Mac gestartet. Hierzu die Datei Info.plist im Editor öffnen, diese befindet sich im Package im Verzeichnis Contents. Anschließend den Wert unter CFBundleIdentifier ändern, zum Beispiel eine 1 hinzufügen. |
AW: Firemonkey Workarounds
Ich packe das mal hier rein, da es passt:
- FMX.Treeview öffnet bei jedem TreeviewItem die Children und zeichnet jedes einzelne Element neu In FMX.Treeview.pas unter procedure TTreeViewItem.SetIsExpanded(const Value: Boolean); folgendes ausklammern:
Delphi-Quellcode:
und das FUpdating hinzufügen bei:
(* if FContent.ChildrenCount > 0 then
for i := FContent.ChildrenCount - 1 downto 0 do begin Item := nil; if FContent.Children[i] is TTreeViewItem then Item := TTreeViewItem(FContent.Children[i]); if Item <> nil then Item.IsExpanded := True; end;*)
Delphi-Quellcode:
if (FUpdating = 0) and (TreeView <> nil) then
TreeView.Realign; - unter OSX liefert das OnClick Ereignis bei den Menüs nicht das TMenuitem zurück. Unter FMX.Platform.MAC das DispatchMenu wie folgt ersetzen:
Delphi-Quellcode:
procedure TFMXOSMenuItem.DispatchMenuClick(Sender: Pointer);
begin try if Assigned(FMXMenuItem.OnClick) then FMXMenuItem.OnClick(FMXMenuItem); //++ except Application.HandleException(Self); end; end; |
AW: Firemonkey Workarounds
Zitat:
Ein wenig zur Ehrenrettung von EMBA: Im Quality Central gab es aber auch keinen einzigen Eintrag zu diesem Problem. Das habe ich nun nachgeholt und ein für die Prüfer reproduzierbares Beispiel beigefügt. Hier ist der Eintrag: ![]() |
AW: Firemonkey Workarounds
Außerdem habe ich nun mal den Workaround-Vorschlag von Greenmile aufgegriffen und eine kleine Prozedur draus gemacht, so dass man mit einem Aufruf den Header, die Höhe des Headers, die Items, die jeweilige Breite der Items, die Captions der HeaderItems, die Ausrichtung des Headers, den Parent und ein ClickEvent übergeben kann.
Sieht dann so aus (die Unit StrUtils und FMX.Header wird benötigt):
Delphi-Quellcode:
Das könnt Ihr dann so aufrufen (unterstellt, pnSizeBack ist hier z.B. ein TPanel):
Procedure CreateANewHeader (AH: THeader; AHeight: Integer; AClickEvent: TNotifyEvent; AParent: TFMXObject; AL: TAlignLayout; SInfo: String);
var DyA: TStringDynArray; s: string; HI: THeaderItem; P: Integer; begin AH := THeader.Create(AParent); AH.Parent := Aparent; AH.Height := Aheight; AH.Align := AL; DyA := SplitString (SInfo, ','); for s in DyA do begin P := Pos ('=', S); HI := THeaderItem.Create(AH); HI.Parent := AH; HI.Text := Copy (S, 1, p-1); HI.DragMode := TDragMode.dmManual; // Ohnen diesen Fix geht das ClickEvent nicht :-( HI.Width := StrToInt (Copy (S, p+1, Length (s)-P+1)); if Assigned (AClickEvent) then HI.OnClick := AClickEvent; end; end; var BillHeader: THeader;
Delphi-Quellcode:
Ein Beispiel für die Event-Prozedur:
procedure TF_Main.FormCreate(Sender: TObject);
begin CreateANewHeader (BillHeader, 21, HeaderItemXClick, pnSizeBack, TAlignLayout.alTop, 'Re-Datum=89,Re-Nr=64,Anl.=30,Name=176,Summe=100,ZS=24,Zahldatum=83'); ... end;
Delphi-Quellcode:
procedure TF_Main.HeaderItemXClick(Sender: TObject);
begin ShowMessage ('Clicked: ' + THeaderItem(sender).Text); end; |
AW: Firemonkey Workarounds
Warum keine Factory?
So muss man sich ja diesen String irgendwie zusammenbasteln. Geht, aber ist auch nicht wirklich schön.
Delphi-Quellcode:
Der Factory-Instanz gibt man alle relevanten Teile im
THeaderItemFactory = class
function Build( const AText : string; AWidth : Integer ) : THeaderItem; end;
Delphi-Quellcode:
mit und erzeugt dann die jeweiligen Items.
constructor
|
AW: Firemonkey Workarounds
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
|
AW: Firemonkey Workarounds
Zitat:
Würde aber dennoch gerne verstehen, welche Vorteile Du mit dem Factory-Ansatz siehst. Wird das dann nicht wieder mehr Schreiberei, oder wie ungefähr müsste man sich die Implementation vorstellen? |
AW: Firemonkey Workarounds
Zitat:
Du vermutest ja die TMS-Komponenten. Aber kann das sein, wenn ich in der Beispielform ja gar keine solchen Komponenten drin habe? Kann das alleinige Vorhanden sein des TMS-Packages auf dem PC so etwas verursachen? |
AW: Firemonkey Workarounds
Versuch erst mal das Beispiel in einem neuen Verzeichnis zu entpacken und dort zu reproduzieren - so habe ich das ja auch gemacht. Dann kannst Du immer noch ein "frisches" XE5 aufsetzen mit allen Updates und Hotfixes.
|
AW: Firemonkey Workarounds
Zitat:
|
AW: Firemonkey Workarounds
Zitat:
Stell dir einmal dieses Interface vor
Delphi-Quellcode:
dann kannst du das Erzeugen der Items in eigenen Routinen auslagern, ohne dort wissen zu müssen auf welche konkrete Instanz sich das auswirkt.
IHeaderBuilder = interface
function AddItem( const ATitle : string ) : IHeaderBuilder; overload; function AddItem( const ATitle : string; AWidth : Integer ) : IHeaderBuilder; overload; function AddItem( const ATitle : string; AWidth : Integer; AClickHandler : TNotifyEvent ) : IHeaderBuilder; overload; // ... kann beliebig fortgesetzt werden end;
Delphi-Quellcode:
Und wie wir sehen ist es nicht ersichtlich ob das jetzt für FMX oder VCL sein soll ... eben, das hängt einfach nur von der Implementierung ab. Die kann man jetzt gemütlich für
procedure TForm1.PrepareCustomersHeader( AHeader : IHeaderBuilder );
begin AHeader .AddItem( 'Name' ) .AddItem( 'Phone', 40 ) .AddItem( 'email', 80, SendMailOnClick ); end;
Delphi-Quellcode:
,
TListView
Delphi-Quellcode:
oder welche Listenkomponente auch immer implementieren und trotzdem kann der Anwendungs-Code gleich bleiben.
TStringGrid
|
AW: Firemonkey Workarounds
Ah ja, wirklich interessant!
Allerdings doch deutlich mehr Arbeit, als eben nur mal diesen kleinen WorkAround zu schreiben. Insofern bleibe ich doch erst mal bei meiner Lösung... [edit: die ich ja jetzt auch nicht mehr brauche, da ich letztlich doch das Problem aufklären konnte und jetzt wieder ganz normal die Komponente verwenden kann, siehe Eintrag #19 unten] |
AW: Firemonkey Workarounds
Zu dem Problem mit der beschädigten FMX-Datei:
Habe XE5 frisch installiert. Danach FMX-TMS-Komponenten, kein Problem, alles geht. Einige Sachen, die ich installiert habe, kann man nur einmal installieren, kann ich also nicht ein zweites Mal in die andere Umgebung installieren. Wie findet man das jetzt nur raus, wo das dran liegen könnte? |
AW: Firemonkey Workarounds
Du könntest Versuchen, Delphi mit Delphi zu debuggen (Mit Prozess verbinden). Also zuerst den Debuggee starten, im Taskmanager die Prozeß-ID merken und dann Delphi nochmals als Debugger starten (aus der Kommandozeile mit Parameter /np) und mit der BDS.exe mit der zuvor gemerkten ID verbinden. Dann in die erste Instanz wechseln und Fehler verursachen, in die zweite Instanz umschalten und schauen, ob Du was siehst (z.B. BPL-Namen).
Mist, merke gerade das geht mit XE nicht mehr, ausser man startet bei mit /np. |
AW: Firemonkey Workarounds
Und wieder mal ein Mysterium gelöst...
Union, Deine Vermutung, es lag an den TMS-Komponenten, stimmte!! Allerdings lag es nicht an den TMS-FMX-Komponenten, sondern an den TMS-VCL-Komponenten. Als ich die defekte FMX speicherte und wieder lud, fiel mir auf, dass plötzlich folgende Einträge in der Form.pas-Datei drin waren: GDIPCustomItem, GDIPTextItem, GDIPSectionItem, GDIPImageSectionItem, GDIPHeaderItem; Die gehören zu den TMS-VCL Komponenten und in der Unit GDIPHeaderItem ist auch eine Klasse "THeaderItem" drin. So kam es zu einem Konflikt. Wie das letztlich möglich sein kann, ist mir zwar ein Rätsel, aber gut. Nachdem ich also die Packages der TMS-VCL-Komponenten deaktiviert hatte, konnte ich die Datei ganz normal laden. So, jetzt muss ich das noch EMBA und TMS-Software erzählen... (na toll :pale:) Wie war das eigentlich noch mal: Kann man Delphi nicht so einstellen, dass für bestimmte Projekte nur bestimmte Packages geladen werden? Dann könnte man das Problem vielleicht umgehen? |
AW: Firemonkey Workarounds
Zitat:
|
AW: Firemonkey Workarounds
Liste der Anhänge anzeigen (Anzahl: 2)
So, ich habe mal einen weiteren Workaround entwickelt und zwar für die fehlenden Hints in FireMonkey. Zwar hatte himitsu da auch schon mal was gemacht, das war aber eine Komponente und wenn ich das richtig sehe, wurde die nicht für XE4 bzw. XE5 weiterentwickelt (wenn ich hier was übersehen haben sollte, bitte Info).
Die Komponente von himitsu (eine tolle Leistung, wie ich finde) hatte zwar noch einige weitere Fähigkeiten, wie etwa Grafikanzeige im Hint. Aber ich denke für die meisten Anwendungsfälle wird das gar nicht benötigt und ich persönlich ziehe immer die Verwendung einfacher units vor, alles in einer Datei, da gibt es auch keine Probleme beim Wechsel von einer Delphi-Version zur nächsten (also nicht erneut Package / Komponenten installieren, usw). Meine Lösung ist denkbar einfach (gerade mal 200 Zeilen) und bei Bedarf erweiterbar und funktioniert für XE2 bis XE5 sowohl unter Windows als auch auf dem MAC. Mann muss nur die Unit HS_FMXHints.pas in die jeweiligen Forms einbinden, wo man Hints verwenden möchte. Im Oncreate-Ereignis der Form (oder in einer separaten Form-Procedure) setzt man dann die Hints mit einfachen Aufrufen wie
Delphi-Quellcode:
Bei Bedarf kann man vorgegebene Standardeinstellungen für die Hints mit der Procedure
SetAHint (Button1, 'Dies ist ein Hint für Button1');
Delphi-Quellcode:
überschreiben.
SetHintSetting (TimeBeforeShow, TimeToShow: Integer; DynamicShow: Boolean; TC: TAlphaColor);
Bevor ich mir hier noch einen Wolf schreibe, verweise ich der Einfachheit halber auf ein kurzes Video, das ich auf YouTube gepostet habe. Hier ist der Link: ![]() Die Lösung hat 1-2 kleinere Einschränkungen, wird im Video angesprochen. Die Unit könnt Ihr frei verwenden wie Ihr wollt, Nutzung natürlich auf eigene Verantwortung. Das Teil ist sicher noch ausbaufähig (und kann man wohl auch ein wenig "schicker" schreiben), aber ich denke, dennoch ganz nützlich, insbesondere, wenn man sonst keine Möglichkeit kennt, Hints in FMX-Anwendungen zu verwenden. Ich gebe ja die Hoffnung nicht auf und erwarte das von Haus aus in einer der nächsten Delphi XE-Versionen. Screenshot und Unit anbei. |
AW: Firemonkey Workarounds
Liste der Anhänge anzeigen (Anzahl: 1)
Na, kaum gemacht, schon hat mich eine der "Einschränkungen" eingeholt. Ich benötige in einem Projekt doch die OnMouseEnter und OnMouseLeave Ereignisse, da wär es dann blöd, wenn die überschrieben werden.
Habe die HS_FMXHints.pas daher geändert, so dass eine Überschreibung nicht stattfindet, wenn für die Controls schon Events vorhanden sind:
Delphi-Quellcode:
In den dann bereits verwendeten Events muss man die Aufrufe auf die Hintunit selber ergänzen, also in
procedure SetAHint (ic: TObject; txt: string);
begin if ic is TButton then begin TButton (ic).Hint := txt; if not Assigned (TButton (ic).OnMouseEnter) then TButton (ic).OnMouseEnter := EventEnter; if not Assigned (TButton (ic).OnMouseLeave) then TButton (ic).OnMouseLeave := EventExit end; if ic is TSpeedbutton then begin TSpeedButton (ic).Hint := txt; if not Assigned (TSpeedButton (ic).OnMouseEnter) then TSpeedButton (ic).OnMouseEnter := EventEnter; if not Assigned (TSpeedButton (ic).OnMouseLeave) then TSpeedButton (ic).OnMouseLeave := EventExit end; if ic is TLabel then begin TLabel (ic).Hint := txt; if not Assigned (TLabel (ic).OnMouseEnter) then TLabel (ic).OnMouseEnter := EventEnter; if not Assigned (TLabel (ic).OnMouseLeave) then TLabel (ic).OnMouseLeave := EventExit; end; end; OnMouseEnter mit HintEnter (self, sender) OnMouseLeave mit HintExit (self, sender) Konkretes Beispiel:
Delphi-Quellcode:
Die aktualisierte Unit habe ich beigefügt.
procedure Tfrm_Main.sbNewMouseEnter(Sender: TObject);
begin TSpeedButton (sender).StyleLookup := 'sbNewStyle2'; {$IFDEF MSWINDOWS} if AktFormStyle = 'ObsedianStyle' then begin TSpeedButton (sender).FontColor := TAlphaColors.Black; end; {$ENDIF} HintEnter (self, sender); end; procedure Tfrm_Main.sbNewMouseLeave(Sender: TObject); begin TSpeedButton (sender).StyleLookup := 'sbNewStyle1'; {$IFDEF MSWINDOWS} if AktFormStyle = 'ObsedianStyle' then begin TSpeedButton (sender).FontColor := TAlphaColors.White; end; {$ENDIF} HintExit (self, sender); end; |
AW: Firemonkey Workarounds
Zitat:
Die viel sinnvollere Variante ist aber TVirtualMethodInterceptor zu benutzen und die Prozeduren MouseDown usw. direkt abzufangen ohne die Events überhaupt anzutasten... ![]() Denn die Events sollten wirklich nur für die direkte Benutzung einer spezifischen Komponente benutzt werden, nicht für so allgemeine Funktionalität. // EDIT: Höchstens ist die Frage, ob das ganze die Performance zu stark beeinträchtigt. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:33 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz