• 0
Error

Что не так с 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

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


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

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)

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


Ссылка на сообщение
Поделиться на другие сайты
  • 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 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу