• 0
Error

tips and tricks Что не так с FindStyleResource и что делать.

Вопрос

ЧАВО:

Опишу "багофичу" на которую я наткнулся.

Предположим что вы пишете свой компонент, наследуемый от TStyledControl (или любого другого компонента, который происходит от  TStyledControl), для доступа к элементам стиля обычно используют FindStyleResource('ИмяРесурса') (есть вариант в виде FindStyleResource<Класс>('ИмяРесурса', Переменная)) , например компонент TImageControl получает Image так:

procedure TImageControl.ApplyStyle;
begin
  inherited;
  if FindStyleResource<TImage>('image', FImage) then
    UpdateImage;
end;

И FindStyleResource работает отлично, пока в дереве стиля искомый объект лежит на НЕ TStyledControl-ах, то есть FindStyleResource будет успешно находить объект, который расположен на TRectangle, но не найдет его же, но на TPanel!

Пример:

Код, в процедуре ApplyStyle:

procedure TEsImageSelection.ApplyStyle;
var
  T: TControl;
begin
  inherited ApplyStyle;
  if FindStyleResource<TControl>('selection', T) then
    ShowMessage('"selection" founded!');
end;

Что делает данный код? - При нахождении стилевого объекта выдает соответствующее сообщение.

Рассмотрим стиль:

zz.png

Как видите в варианте A, "Selection" лежит на НЕ наследнике TStyledControl. Запустив программу можно убедиться что FindStyleResource<TControl>('selection', T) найдет объект "Selection".

В варианте B, при запуске можно с удивлением обнаружить что FindStyleResource<TControl>('selection', T) не находит объект "Selection"!

Почему так?

Судя по исходникам поиск во вложенных TStyledControl-ах сломан специально, дабы не всплывали еще большие глюки\проблемы.(но я не изучал вопрос очень подробно, во внутренний код работы с загрузкой и поиском стилей - кромешный ад, с наслаиванием истории Fire-Monkey разных лет).

Как можно обойти проблему?

Я нашел следующее решение:

(Однако я совершенно не уверен в отсутствии побочных эффектов, возможно @Brovin Yaroslav сможет прокомментировать его?)

Данный код находит искомый стилевой объект, в отличии от FindStyleResource.

procedure TEsImageSelection.ApplyStyle;
var
  T: TControl;
begin
  inherited ApplyStyle;
  
  T := nil;
  EnumObjects(
    function (Obj: TFmxObject): TEnumProcResult
    begin
      if Obj.StyleName.ToLower = 'selection' then
      begin
        T := TControl(Obj);
        Result := TEnumProcResult.Stop;
      end else
        Result := TEnumProcResult.Continue;
    end);

  if T <> nil then
    ShowMessage('"selection" founded!');
end;

 

Процедура-замена FindStyleResource, работает как ожидается:

type
  TOpenStyledControl = class(TStyledControl);

function EsFindStyleResource(Self: TStyledControl; StyleName: string): TFmxObject;
var
  StyleObject: TFmxObject;
begin
  // если Self.ChildrenCount < 1 то в компоненте не загружен стиль,
  // т.к. известно что главный эллемент стиля ВСЕГДА находиться по нулевому индексу.
  if (TOpenStyledControl(Self).ResourceLink = nil) or (Self.ChildrenCount < 1) then
    Exit(nil);

  StyleObject := nil;

  Self.Children[0].EnumObjects(
    function (Obj: TFmxObject): TEnumProcResult
    begin
      if Obj.StyleName.ToLower = StyleName.ToLower then
      begin
        Result := TEnumProcResult.Stop;
        StyleObject := Obj;
      end else
        Result := TEnumProcResult.Continue;
    end);

  Result := StyleObject;
end;

 

Изменено пользователем Error
rareMax понравилось это

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

6 ответов на этот вопрос

  • 0

Не ищет потому что так и не планировалось. Так как компонент не обязательно используется в стиле.

Например по второй картинке:

selection является дочерним к панели. И есть предположение, что FindStyleResource должен найти его. С одной стороны логично.

С другой стороны такая же картинка может быть на форме. И в этом случае FindStyleResource уже не должен найти selection. По скольку в FindStyleResource нету контекста, в чем мы ищем, поэтому он всегда ищет объект только в стиле для стилевых компонентов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
  • 0

Да и вообще использование вложенных стилей не предусматривалось и никогда не рассматривалось в таких примерах. Поскольку любая фича для стилей сказывается плохо на производительности.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
  • 0
1 час назад, Brovin Yaroslav сказал:

Не ищет потому что так и не планировалось. Так как компонент не обязательно используется в стиле.

Например по второй картинке:

selection является дочерним к панели. И есть предположение, что FindStyleResource должен найти его. С одной стороны логично.

С другой стороны такая же картинка может быть на форме. И в этом случае FindStyleResource уже не должен найти selection. По скольку в FindStyleResource нету контекста, в чем мы ищем, поэтому он всегда ищет объект только в стиле для стилевых компонентов.

Собственно, я предполагал что это так, но насколько плох мой подход по обходу его?

Я проверял, да - если на мой компонент кинуть кнопку и прописать ей StyleName := 'selection', то при отсутствии в стиле объекта 'selection', мой метод найдет кнопку, что неверно, однако зачем вообще прописывать кнопке на форме StyleName?

 

Кроме того, я придумал другой метод, который ищет только среди объектов стиля, то есть не обладает недостатком, который я привел выше.

Требую критики :)

function EsFindStyleResource(Self: TStyledControl; StyleName: string): TFmxObject;
var
  StyleObject: TFmxObject;
begin
  // если Self.ChildrenCount < 1 то в компоненте не загружен стиль,
  // т.к. известно что главный эллемент стиля ВСЕГДА находиться по нулевому индексу.
  if Self.ChildrenCount < 1 then
    Exit(nil);

  StyleObject := nil;

  Self.Children[0].EnumObjects(
    function (Obj: TFmxObject): TEnumProcResult
    begin
      if Obj.StyleName.ToLower = StyleName.ToLower then
      begin
        Result := TEnumProcResult.Stop;
        StyleObject := Obj;
      end else
        Result := TEnumProcResult.Continue;
    end);

  Result := StyleObject;
end;

procedure TEsImageSelection.ApplyStyle;
var
  T: TControl;
begin
  inherited ApplyStyle;

  T := EsFindStyleResource(Self, 'selection') as TControl;

  if T <> nil then
    ShowMessage('"selection" founded!');
end;

Имхо, это не плохой метод, главное чтобы не было дополнительных подводных камней...

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
  • 0
57 минут назад, Brovin Yaroslav сказал:

Да и вообще использование вложенных стилей не предусматривалось и никогда не рассматривалось в таких примерах. Поскольку любая фича для стилей сказывается плохо на производительности.

Да производительность стилей, это конечно отдельная тема...

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
  • 0
2 часа назад, Error сказал:

Требую критики :)

Костыль: Self.Children[0] - Объект в стиле, использующий стиль может его и не загрузить, но при этом иметь вложенные контролы. Поэтому физически у него еще нет стиля, но один контрол внутри есть. (Подсказка, смотреть ResourceLink)

Error понравилось это

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
  • 0
22 минуты назад, Brovin Yaroslav сказал:

Костыль: Self.Children[0] - Объект в стиле, использующий стиль может его и не загрузить, но при этом иметь вложенные контролы. Поэтому физически у него еще нет стиля, но один контрол внутри есть. (Подсказка, смотреть ResourceLink)

Согласен, однако вызывать EsFindStyleResource я собираюсь только в ApplyStyle, после inherited ApplyStyle, и как я понимаю, стиль к этому моменту уже будет загружен...

Но костыль, да, поэтому с учетом замечания, я переписал процедуру EsFindStyleResource, теперь, как я понимаю все хорошо? :)

type
  TOpenStyledControl = class(TStyledControl);

function EsFindStyleResource(Self: TStyledControl; StyleName: string): TFmxObject;
var
  StyleObject: TFmxObject;
begin
  // если Self.ChildrenCount < 1 то в компоненте не загружен стиль,
  // т.к. известно что главный эллемент стиля ВСЕГДА находиться по нулевому индексу.
  if (TOpenStyledControl(Self).ResourceLink = nil) or (Self.ChildrenCount < 1) then
    Exit(nil);

  StyleObject := nil;

  Self.Children[0].EnumObjects(
    function (Obj: TFmxObject): TEnumProcResult
    begin
      if Obj.StyleName.ToLower = StyleName.ToLower then
      begin
        Result := TEnumProcResult.Stop;
        StyleObject := Obj;
      end else
        Result := TEnumProcResult.Continue;
    end);

  Result := StyleObject;
end;

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте аккаунт или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!


Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.


Войти сейчас

  • Похожие публикации

    • Автор: trl
      Не нашел ответа на форуме.  не могу понять почему FindStyleResource  не возвращает объекта.
      procedure TMainForm.Button1Click(Sender: TObject); var item :  TListBoxItem;       lbName:TLabel; begin     Item := TListBoxItem.Create(Self);     Item.Parent := ListBox;      Item.StyleLookup := 'lyItemBoxDetail';        lbName := Self.FindStyleResource('lbname') as TLabel; // Здесь получаю nil     ShowMessage(  Item.StylesData['lbname.Text'].AsString) // А этот код работает end;  
    • Автор: max7ka
      Подскажите где можно найти структуру StyleLookup для объектов, чтобы можно было использовать в FindStyleResource?
       
      Например
       
       CheckBox
          checkboxunchecked
          checkboxchecked
          checkmark
          ...
       
       TGrid
         header
         focus
         selection
         ...
    • Автор: Ra72
      Создал компонент:
      type TTube = class(TFmxObject) private FTheColor: TAlphaColor; public constructor Create(AOwner: TComponent); override; published property TheColor: TAlphaColor read FTheColor write FTheColor; end; constructor TTube.Create(AOwner: TComponent); begin inherited; FTheColor := $FFFFFFFF; end; Вставил его в StyleBook.
      object TTube StyleName = 'tube1' TheColor = claViolet end Ищу его в стилизованном контроле:
       
      var tube: TTube; begin tube := ATarget.FindStyleResource('tube1') as TTube; Не находится компонент. 
      Стандартные Text, Rectangle... находятся таким способом, а свой - нет.
      Что я делаю не так?
    • Автор: estra

       
      Редактирую стиль, добавляю к нему компонент TImage и для свойства RotateAngle создаю FloatAnimation (параметры анимации на скрине). Но при запуске никакой анимации нет. Почему? И как все же добиться работоспособности анимации для элемента стиля?
    • Автор: estra
      Почему метод FindStyleResource  в OnCreate и OnShow формы возвращает nil, а после запуска программы (в Button1.OnClick) возвращает указатель на объект? Как получить доступ к объекту стиля в момент запуска программы?
    • Автор: Nix0N
      Для примера:
       
      Есть объект ListBoxItem. В StyleBook загружен стиль по умолчанию. Для итема лист бокса задан стандартный стиль:
      ListBoxItem.Stylename = "listboxitemlabel" Если заглянуть в StyleBook, то мы увидим что "listboxitemlabel" это TLayout.
      Каким образом можно изменить/получить свойства этого объекта, к примеру текущую его высоту для нашего ListBoxItem?
  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу