Firemonkey: каскадное изменение стиля поиска для FMXObject, где другие объекты наследуют стилевое имя

Я не уверен, возможно ли это, но мне кажется, что так и должно быть. По сути, я хочу, чтобы все компоненты обновляли свои стили при изменении StyleLookup.

У меня есть FMXComponent, который называется TLabel BaseStyleLabel, StyleName Свойство компонента - BaseStyle. Он сам ищет свой стиль из ресурса стиля, поэтому его StyleLookup свойство установлено в "BaseStyle1". У меня также есть "BaseStyle2", "BaseStyle3"...

У меня есть зависимый Tlabel, который называется MyTextLabel И его StyleLookup свойство установлено в BaseStyle то есть StyleName BaseStyleLabel,

Кажется, что все работает нормально. я вижу это MyTextLabel наследует стиль от MyBaseStyle BaseStyle1.

Когда я выполняю эту строку кода

 BaseStyleLabel.StyleLookup := 'BaseStyle2';
 Self.repaint; // repaint whole form

Я ожидаю что BaseStyleLabel меняется на "BaseStyle2" (что он делает). Тем не мение, MyTextLabel также должен изменить стиль и затем выглядеть как "BaseStyle2", но это не так: он остается BaseStyle1;

Спецификатор заключается в том, что BaseLabel и MyTextLabel также являются источниками стилей. На самом деле они не являются компонентом, созданным в форме, они создаются стилем.

Итак, мой вопрос заключается в следующем.

  1. Этот подход действителен?
  2. Существует ли стандартный подход, например, использование объекта стиля, который я могу обновить?
  3. Разве я не обновил правильную вещь или;
  4. использовал неправильный метод для обновления, может быть, ApplyStyle?

... РЕДАКТИРОВАТЬ.... Ниже запрошенный пример...

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    MyTextLabel: TLabel;
    StyleBook1: TStyleBook;
    BaseStyleLabel: TLabel;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Set up BaseStyleLabel as a Style Source
  BaseStyleLabel.StyleName := 'BaseStyle';
  // Set its Style to a Resources Style "STYLE ONE"
  BaseStyleLabel.StyleLookup := 'BaseStyle1';

 // Point MyTextLabel to whatever "BaseStyleLabel" is styled as...
   MyTextLabel.StyleLookup := 'BaseStyle'; // also says "STYLE ONE"

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  //Change BaseStyle Label to "STYLE TWO" (works OK)
  BaseStyleLabel.StyleLookup := 'BaseStyle2';

  // ... BUT MyTextLabel stays as "STYLE ONE"
  // can I repaint???
  //   Auto Cascade?
  //   What about when Other TLabels are part of a different component style?

end;

end.

Используйте следующий файл стиля

object TStyleContainer
  object TLabel
    StyleName = 'BaseStyle1'
    DesignVisible = False
    Height = 17.000000000000000000
    Position.X = 521.000000000000000000
    Position.Y = 432.000000000000000000
    Text = 'STYLE ONE'
    Width = 120.000000000000000000
  end
  object TLabel
    StyleName = 'BaseStyle2'
    DesignVisible = False
    Height = 17.000000000000000000
    Position.X = 521.000000000000000000
    Position.Y = 432.000000000000000000
    Text = 'STYLE TWO'
    Width = 120.000000000000000000
  end
  object TLabel
    StyleName = 'BaseStyle3'
    Height = 17.000000000000000000
    Position.X = 521.000000000000000000
    Position.Y = 432.000000000000000000
    Text = 'STYLE 3'
    Width = 120.000000000000000000
  end
end

1 ответ

ОК - я работал через все это, может придумал решение.

Я просмотрел класс предков для TStyledControl, Не существует глобального триггера, который бы вызывал каскадный поиск стилей. Тот факт, что отдельный компонент обновляет свой стиль, не приводит к обновлению каких-либо компонентов (которые зависят от этих компонентов StyleName). Таким образом, кажется, что вы должны найти все затронутые компоненты вручную и обновить их все.

Это было недостаточно для меня, поэтому я написал вспомогательный метод для TStyledControl.

unit FMX.CascadingStyleLookup;
// Written by Glen Kleidon - 2018 - twitter: @sobaldrick4
interface

uses System.SysUtils, FMX.Types, FMX.Forms, FMX.Controls;

Type
  TCascadingStyleLookup = Class Helper for TStyledControl
    Procedure CascadeStyleLookup(AStyleLookup: String); overload;
    procedure CascadeStyleLookup(AStyleLookup: string;
      AComponent: TFMXObject); overload;
    Function FindUltimateParentForm(AChild: TFMXObject): TFMXObject;
  End;

implementation

procedure TCascadingStyleLookup.CascadeStyleLookup(AStyleLookup: String);
var
  lUltimateParent: TFMXObject;
begin
  // Apply New style to myself.
  Self.styleLookup := AStyleLookup;

  // Check if I have dependent Styles.
  if (length(Self.StyleName) = 0) then
    exit;

  // Re-Apply my own style to everything.
  lUltimateParent := Self.FindUltimateParentForm(Self);
  if lUltimateParent <> nil then
    CascadeStyleLookup(Self.StyleName, lUltimateParent);
end;

procedure TCascadingStyleLookup.CascadeStyleLookup(AStyleLookup: string;
  AComponent: TFMXObject);
var
  lChild: TFMXObject;
  lStyledControl: TStyledControl;
begin
  if (AComponent = nil) or (AComponent.Children = nil) then
    exit;

  if (AComponent.InheritsFrom(TStyledControl)) then
  begin
    lStyledControl := AComponent as TStyledControl;
    if SameText(AStyleLookup, lStyledControl.styleLookup) then
    begin
      // re-apply
      lStyledControl.styleLookup := AStyleLookup;
      // Re-cascade this style.
      if (length(lStyledControl.StyleName) > 0) and
        (NOT(SameText(lStyledControl.styleLookup, AStyleLookup))) then
        CascadeStyleLookup(lStyledControl.StyleName);
    end;
  end;
  if AComponent.Children = nil then
    exit;

  // Check the children of this component for the style.
  for lChild in AComponent.Children do
    CascadeStyleLookup(AStyleLookup, lChild);

end;

function TCascadingStyleLookup.FindUltimateParentForm(AChild: TFMXObject)
  : TFMXObject;
begin
  Result := nil;
  if (AChild.Parent <> nil) and (NOT(AChild.Parent.InheritsFrom(TForm))) then
    Result := FindUltimateParentForm(AChild)
  else
    Result := AChild.Parent;
end;

end.

Это позволяет автоматически искать и обновлять любые вложенные свойства stylelookup для всей формы, в которой находится компонент.

Я обновил свой оригинальный пример с дополнительным компонентом MyTextLabel2 который зависит от стиля MyTextLabel,

Итак, теперь настройка BaseStyleLabel любой из *BaseStyle1, 2 или 3* будет каскадно соединен с MyTextLabel. Когда MyTextLabel меняет свой стиль, это будет каскадно к MyTextLabel2. Таким образом, все три метки будут отображать "STYLE TWO", когда BaseStyleLabel обновляется простым вызовом BaseStyleLabel.CascadeStyleLookup('BaseStyle2');

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants,
  FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
  FMX.CascadingStyleLookup
  ;

type
  TForm1 = class(TForm)
    MyTextLabel: TLabel;
    StyleBook1: TStyleBook;
    BaseStyleLabel: TLabel;
    Button1: TButton;
    MyTextLabel2: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Set up BaseStyleLabel as a Style Source
  BaseStyleLabel.StyleName := 'BaseStyle';
  // Set its Style to a Resources Style "STYLE ONE"
  BaseStyleLabel.StyleLookup := 'BaseStyle1';

  // Point MyTextLabel to whatever "BaseStyleLabel" is styled as...
  MyTextLabel.StyleName := 'MyTextBaseStyle';
  MyTextLabel.StyleLookup := 'BaseStyle'; // also says "STYLE ONE"

  // New component MyTextLabel2 points to whatever "MyTextLabel" is styled as...
  MyTextLabel2.StyleLookup := 'MyTextBaseStyle'; // also says "STYLE ONE",
                                                 // (from MyTextLabel)

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  // Change BaseStyle Label to "STYLE TWO" (now cascades to all)
  BaseStyleLabel.CascadeStyleLookup('BaseStyle2');

end;
Другие вопросы по тегам