MikeMon 12 Posted May 10, 2020 How can I programmatically change properties of a custom style? Basically, I have created a custom style for TLabel and have added a rectangle to be able to control its background. How can I programmatically change its color so that all TLabels that have that style are automatically updated with the new color? Share this post Link to post
mausmb 13 Posted May 10, 2020 Something in this direction ? procedure ColorLabel(const pColor : TAlphaColor; const pString:String; mylabel:Tlabel); var prect:Trectangle; ptext:TText; begin 1. creation if mylabel.ChildrenCount=1 then begin // creating trrectangle on fly prect:=Trectangle.Create(mylabel); prect.Parent:=mylabel; prect.Align:=TAlignLayout.Client; prect.HitTest:=False; prect.Margins.Left:=-1; prect.Margins.Right:=-1; prect.Margins.Top:=-1; prect.Margins.Bottom:=-1; prect.Sides:=[]; // no borders prect.Fill.Color:=pColor; ptext:=TText.Create(mylabel); ptext.Parent:=prect; ptext.Align:=TAlignLayout.Client; ptext.Text:=pString; ptext.TextSettings:= mylabel.TextSettings; ptext.HitTest:=False; mylabel.Text:=''; end 2. changing color if mylabel.Children[1].ClassName=Trectangle.ClassName then Begin // change color (mylabel.Children[1] as Trectangle).Fill.Color:=pColor; // pCOlor is parameter for custom color if (mylabel.Children[1] as Trectangle).Children[0].classname=TText.classname then Begin // chanhe text ((mylabel.Children[1] as Trectangle).Children[0] as TTExt).Text:=pString; // pString is parameter for TEXT ((mylabel.Children[1] as Trectangle).Children[0] as TTExt).TextSettings.Fontcolor:=mylabel.TextSettings.Fontcolor; End; End; end; br, m Share this post Link to post
MikeMon 12 Posted May 10, 2020 25 minutes ago, mausmb said: Something in this direction ? Hi. Thank you for your answer. Your approach is irrelevant to what I am trying to do. What I want to do is change properties of a style in a Stylebook and expect all objects having that style in their stylelookup field to be updated automatically. Currently, as a workaround, I'm recursively checking all objects and their children and looking for TLabels with stylelookup the style that I want, and changing the fill color of the rectangle in the style. There should be a better way, i.e. just change the fill color of the style in the stylebook. This is the code I'm currently using as a workaround: procedure ChangeAllLabelStyles(ChangeToColor: Cardinal); procedure ChangeObjectRecursive(CurrentComponent: TComponent); var i: Integer; begin if CurrentComponent.ClassNameIs('TLabel') and (TLabel(CurrentComponent).StyleLookup = 'RedButtonLabelStyle') then TLabel(CurrentComponent).StylesData['Rectangle1Style.Fill.Color']:= ChangeToColor; for i:= 0 To CurrentComponent.ComponentCount - 1 do ChangeObjectRecursive(CurrentComponent.Components[i]); end; begin ChangeObjectRecursive(Self); end; Share this post Link to post
MikeMon 12 Posted May 12, 2020 The code above works for the children of the "RedButtonLabelStyle" resource. How do I change for the resource itself. E.g.: when you use a "Custom Style" for a TPanel, it creates a custom style e.g. Panel1Style1. Panel1Style1 doesn't have any children. How can I change its properties (e.g. Fill.Color) by code? The above code's approach (e.g. StylesData['Panel1Style1.Fill.Color'] or StylesData['Fill.Color']) doesn't work. Share this post Link to post
Serge_G 87 Posted May 13, 2020 As I understand what is in your mind, I think it's your ChangeObjectRecursive function the problem. A component (your panel) as style but style is not the "container" of the panel's childs. And therefore, you change wrong "styledata" property for a label See this : // Changing all labels of a panel randomly procedure TForm112.Button1Click(Sender: TObject); var aColor : TAlphaColor; procedure ChangePanelLabels(const Panel : TPanel; const ChangeToColor : TAlphaColor); var i : integer; begin if SameText(Panel.StyleLookup,'panel1style1') then begin for i:=0 to Panel.Children.Count - 1 do begin if Panel.Children[i].ClassNameIs('TLabel') then TLabel(Panel.Children[i]).StylesData['Text.TextSettings.FontColor']:= ChangeToColor; end; end; end; begin randomize; aColor:=Random($FFFFFF) OR $FF000000; // add alpha layer ChangePanelLabels(Panel1,aColor); end; Share this post Link to post
MikeMon 12 Posted May 13, 2020 1 hour ago, Serge_G said: As I understand what is in your mind, I think it's your ChangeObjectRecursive function the problem. A component (your panel) as style but style is not the "container" of the panel's childs. And therefore, you change wrong "styledata" property for a label Hi Serge Thank you for your answer. The custom-styled TPanel has no children. This is what I'm doing: 1.) Drop a TPanel on the form. Call it Panel1 2.) Right-click on the TPanel and choose "Edit Custom Style..." 3.) Close the designer 4.) Make sure the "StyleLookup" field of Panel1 is "Panel1Style1" What I need to do is to programmatically change the "Fill.Color" of "Panel1Style1". I can't seem to find a way how. However, if I drop a TLabel and do the same, "Label1Style1" will be created. Label1Style1 has a child stylename "Text". I could easily access and change its properties by my method above, i.e. Label1.StylesData['text.Scale.X']:= 2, or Label1.StylesData['text.Text']:= 'OK' Share this post Link to post
Serge_G 87 Posted May 13, 2020 Panel is no more than a TRectangle (depending style file I think) I don't know why but if you try to change TPanel fill color this does not work with 'windows 10 desktop style' , works during design time with 'default' Stylecollection but not at runtime 🐛something to do with the fact that this TRectangle is at the root and not in a TLayout ? Best suggestion I can do is : use a TRectangle. I do not use anymore TPanel now, preferring Layouts and TRectangle 😉 1 Share this post Link to post
MikeMon 12 Posted May 13, 2020 3 minutes ago, Serge_G said: Panel is no more than a TRectangle (depending style file I think) I don't know why but if you try to change TPanel fill color this does not work with 'windows 10 desktop style' , works during design time with 'default' Stylecollection but not at runtime 🐛something to do with the fact that this TRectangle is at the root and not in a TLayout ? Best suggestion I can do is : use a TRectangle. I do not use anymore TPanel now, preferring Layouts and TRectangle 😉 Yes, I can change the color of TPanel during design time, but not at runtime. To be honest I use TRectangle, too. The TPanel in my question was just for reference because it is simple to demonstrate. Generally, I can't access and change the properties of the first-level style. Another example which I still haven't found a workaround for is TSwitch. I use a TSwitchObject for a TSwitch style. Again, because it is the first-level style, I can't access and change its properties, e.g. "ThumbOn.Color". Share this post Link to post
Rollo62 536 Posted May 13, 2020 The implementations can also vary between the different platforms. You cannot rely that one element is accessible in same way under all platforms. This is one of the biggest drawbacks of FMX styles IMHO. 1 Share this post Link to post
MikeMon 12 Posted May 13, 2020 2 hours ago, Rollo62 said: The implementations can also vary between the different platforms. You cannot rely that one element is accessible in same way under all platforms. This is one of the biggest drawbacks of FMX styles IMHO. Let me find a solution for 1 platform and I'll do the rest!! lol Share this post Link to post
Rollo62 536 Posted May 14, 2020 (edited) You could try to simply add another rectangle on top of the CustomStyle Panel background in the StyleDesigner, with giving a new StyleName it should be accessible. Not tested in your case, but Ido similar things with other controls. As proposed above, try to use TRectangle instead of TPanel, or better even TLayout as base container for your other components. Edited May 14, 2020 by Rollo62 Share this post Link to post
MikeMon 12 Posted May 14, 2020 1 hour ago, Rollo62 said: You could try to simply add another rectangle on top of the CustomStyle Panel background in the StyleDesigner, with giving a new StyleName it should be accessible. Not tested in your case, but Ido similar things with other controls. As proposed above, try to use TRectangle instead of TPanel, or better even TLayout as base container for your other components. Hi @Rollo62 As I said earlier, I do use TRectangle instead of TPanel. But my example above was just for illustration to show that first-level stylename's properties of a style can not be modified whereas its children's can. E.g. with a TSwitchObject, there is nothing I can do. The TSwitchObject stylename is at the first-level and I can't access any of its properties programmatically. I tried adding a TLayout in the style at the first-level and have the TSwitchObject as its child, but it is not working properly. E.g., the isChecked:= True is not changing the TSwitch's state. Share this post Link to post
Gustav Schubert 25 Posted May 14, 2020 On 5/12/2020 at 10:13 PM, MikeMon said: The above code's approach (e.g. StylesData['Panel1Style1.Fill.Color'] or StylesData['Fill.Color']) doesn't work. I found out why and I post the test code without talking too much: unit FrmMain; interface uses System.SysUtils, System.Classes, System.UITypes, System.Rtti, FMX.Types, FMX.Forms, FMX.StdCtrls, FMX.Objects, FMX.Controls, FMX.Controls.Presentation; type TMyPanel = class(TPanel) public function FindStyleResource(const AStyleLookup: string; const AClone: Boolean = False): TFmxObject; override; end; TFormMain = class(TForm) procedure FormCreate(Sender: TObject); private Button: TButton; Panel: TPanel; procedure Button1Click(Sender: TObject); end; var FormMain: TFormMain; implementation {$R *.fmx} procedure TFormMain.FormCreate(Sender: TObject); begin Panel := TMyPanel.Create(Self); Panel.Parent := Self; Panel.Position.X := 100; Panel.Position.Y := 100; Button := TButton.Create(Self); Button.Parent := Self; Button.Text := 'Test'; Button.OnClick := Button1Click; end; procedure TFormMain.Button1Click(Sender: TObject); begin Panel.StylesData['Something.Fill.Color']:= TAlphaColors.Dodgerblue; // Panel.StylesData['.Fill.Color']:= TAlphaColors.Dodgerblue; end; { TMyPanel } function TMyPanel.FindStyleResource(const AStyleLookup: string; const AClone: Boolean): TFmxObject; function FindOnTopLevel(const Name: string): TFmxObject; var Child: TFmxObject; begin for Child in Children do begin if SameText(Child.StyleName, Name) then begin Exit(Child); end; end; Result := nil; end; begin result := nil; { New: try something even if ResourceLink has no children ! } if ResourceLink.ChildrenCount = 0 then begin result := FindOnTopLevel(''); if result is TRectangle then Exit; end; inherited; end; (* There is a problem when FindStyleResource is called on a FResourceLink instance which has no children. function TFmxObject.FindStyleResource(const AStyleLookup: string; const Clone: Boolean = False): TFmxObject; begin if (AStyleLookup <> '') and (FChildren <> nil) and (FChildren.Count > 0) then begin // not executed because TPanel.ResourceLink has no children end; end; FMX.Types.TFmxObject.FindStyleResource('',False) FMX.Controls.TStyledControl.FindStyleResource(???,???) FMX.Controls.Presentation.TPresentedControl.FindStyleResource('',False) FMX.Controls.TStyledControl.StyleDataChanged(???) FMX.Controls.Presentation.TPresentedControl.StyleDataChanged('.Fill.Color') FMX.Controls.TStyledControl.SetStyleData('.Fill.Color') FrmMain.TFormMain.Button1Click($3F30880) *) end. Share this post Link to post
Rollo62 536 Posted May 14, 2020 (edited) The styles data is not always accessible, better if you touch them only via the OnApplyStyleLookup event. At that time should be guaranteed that all presentation layers are created. Edited May 14, 2020 by Rollo62 Share this post Link to post