Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Composants FMX Delphi Discussion :

Changer de style au runtime


Sujet :

Composants FMX Delphi

  1. #1
    Rédacteur/Modérateur

    Changer de style au runtime
    Bonjour, (10.3.3)

    Il m'est venu à l'idée de charger le style principal de mon application à partir d'une base de données (objectif : donner une couleur différente en fonction de la société en cours)
    Voilà en gros le code de chargement du style :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    AQuery:=TFDQuery.Create(Self);
      try
        AQuery.Connection:=ConnexionBase;
        AQuery.SQL.Text:='SELECT * FROM SETTINGS WHERE ID=0';
        AQuery.Active:=True;
        // Changement des FormatSettings en fonction de la société 
        if not AQuery.FieldByName('ID').IsNull then
           TFormatSettings.Create(AQuery.FieldByName('IDLANG').asInteger);
        // Changement de style 
        if not AQuery.FieldByName('Style').isNull then
         begin
           AStream:=TmemoryStream.Create;
           try
            TBlobField(AQuery.FieldByName('STYLE')).SaveToStream(AStream);
            AStream.Position:=0;
            StyleSociete.LoadFromStream(AStream);
           finally
             AStream.Free;
           end;
         end
        else StyleSociete.Clear;
        AQuery.Active:=False;
      finally
        AQuery.Free;
      end;

    Le hic, quand je change de société et que je charge le style le cadre windows avec son titre et ses boutons "système" disparait
    Je dois louper quelque chose de tout bête !
    - si après chargement je fais un ApplyStyleLookup cela ne change rien
    La seule chose absolue dans un monde comme le nôtre, c'est l'humour. » Albert Einstein

    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Tokyo, Rio) et peut être quelques autres
    SGBD : Firebird 2.5, 3, SQLite
    générateurs Etats : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Ubuntu, Androïd

  2. #2
    Rédacteur/Modérateur

    Bon, on pourra dire que ce truc m'a fait perdre pas mal de temps et de patience

    Il m'a fallu faire un petit programme parallèle à l'application que je développais et pas mal d'arrachage de cheveux pour comprendre.

    Plusieurs tentatives infructueuses avec les streams m'ont poussées à utiliser un fichier
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     if not AQuery.FieldByName('Style').isNull then
         begin
            TBlobField(AQuery.FieldByName('STYLE')).SaveToFile(parametres.StyleFileName);
            StyleSociete.LoadFromFile(Parametres.StyleFileName);
         end
        else StyleSociete.Clear;

    passer par un stylebook (StyleSociete) se révélant non concluant j'ai encore changé de direction pour utiliser
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
     
    TStyleManager.SetStyleFromFile(nomdefichier)

    Et pas de stylebook
    Malgré ça si l'application du premier style fonctionnait, il n'en allait plus de même ensuite sur un point (le plus important pour moi) : un bouton de clôture d'onglets, truc que j'avais rajouté tout d'abord dans un style séparé.
    C'est là que je me suis aperçu que lors du changement de style la procédure assignée pour le onClick sur le bouton était réinitialisée


    Pas de stylebook ? Oui, je suis revenu dessus pour le cas où aucun style ne serait appliqué


    Le piège était donc dans le fait que le chargement d'un style effaçait l'assignation de la procédure onClick.
    Un autre piège, j'avais des types différents dans mes styles, un TButton pour (diamond style et le stylebook), un TSpeedButton pour l'autre (ubuntu style), je ne comprenais pas pourquoi l'assignation ne se faisait pas pour ce dernier jusqu'à ce que j'utilise la classe ancêtre
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
     
    unit EssaiChgtStyle;
     
    interface
     
    uses
      System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
      FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.TabControl,
      FMX.Controls.Presentation, FMX.StdCtrls, FMX.Styles;
     
    type
     
     
      TForm102 = class(TForm)
        Button1: TButton;
        Button2: TButton;
        TabControl1: TTabControl;
        Button3: TButton;
        BtnNoStyle: TButton;
        StyleBook1: TStyleBook;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
        procedure Button3Click(Sender: TObject);
        procedure BtnNoStyleClick(Sender: TObject);
      private
        { Déclarations privées }
        soc : String;
        procedure closetab (sender: TObject);
        procedure stylechange;
      public
        { Déclarations publiques }
      end;
     
    var
      Form102: TForm102;
     
    implementation
     
    {$R *.fmx}
     
    procedure TForm102.BtnNoStyleClick(Sender: TObject);
    begin
     Soc :='';
     TStyleManager.SetStyle(nil);
     StyleChange;
    end;
     
    procedure TForm102.Button1Click(Sender: TObject);
    var ATabItem : TTabItem;
    begin
      ATabItem := TabControl1.Add;
      ATabItem.StyleLookup:='tabitemclosestyle';
      try
      TCustomButton(ATabItem.FindStyleResource('btnclosetab')).OnClick:=CloseTab;
      TCustomButton(ATabItem.FindStyleResource('btnclosetab')).TagString:='Test nostyle';
      except
     
      end;
    end;
     
    procedure TForm102.Button2Click(Sender: TObject);
    var
      i: Integer;
    begin
    Soc := ' Ubuntu';
    TStyleManager.SetStyleFromFile('D:\XE8\FMX\Ubuntu.win.Style');
    StyleChange;
    end;
     
    procedure TForm102.Button3Click(Sender: TObject);
    var
      i: Integer;
    begin
    Soc := ' Diamond';
    TStyleManager.SetStyleFromFile('D:\XE8\FMX\Diamond.win.Style');
    StyleChange;
    end;
     
    procedure TForm102.closetab(sender: TObject);
    begin
    Showmessage(TCustomButton(TabControl1.ActiveTab.FindStyleResource('btnclosetab')).TagString);
    end;
     
    procedure TForm102.stylechange;
    var
      i: Integer;
    begin
    for i:=0 to TabControl1.TabCount-1 do
     begin
    //   TabControl1.Tabs[i].StyleLookup:='tabitemclosestyle';
       TabControl1.Tabs[i].ApplyStyleLookup;
       TCustomButton(TabControl1.Tabs[i].FindStyleResource('btnclosetab')).OnClick:=CloseTab;
       TCustomButton(TabControl1.Tabs[i].FindStyleResource('btnclosetab')).TagString:='Test '+Soc;
     end;
    end;
     
    end.
    La seule chose absolue dans un monde comme le nôtre, c'est l'humour. » Albert Einstein

    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Tokyo, Rio) et peut être quelques autres
    SGBD : Firebird 2.5, 3, SQLite
    générateurs Etats : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Ubuntu, Androïd

  3. #3
    Expert éminent sénior
    Citation Envoyé par SergioMaster Voir le message
    Il m'est venu à l'idée de charger le style principal de mon application à partir d'une base de données (objectif : donner une couleur différente en fonction de la société en cours)
    J'avais eu la même idée en VCL en particulier pour la logistique qui gérait les flux de deux sociétés et devait en faire l'aiguillage, en plus de la couleur, il y a un avait le logo de la société et un bouton pour passer de l'un à l'autre rapidement
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  4. #4
    Rédacteur/Modérateur

    Bonjour,

    Pour VCL, cela faisait un bail que je proposais la couleur de fond pour distinguer les diverses sociétés mais en FMX c'était un peu plus complexe ou plus exactement piègeux (pièges non indiquées dans la doc à ma connaissance).

    Du coup, cela réglé, j'ai voulu aller un peu plus loin en me proposant de montrer la "saveur" du style (en fait le background utilisé) dans un petit rectangle.
    Dans cette discussion https://www.developpez.net/forums/d1...ouleur-defaut/ je pensais trouver le moyen de le faire mais non, car dans la solution présentée c'est le StyleManager qui est utilisé pour obtenir les infos de couleurs, or, dans le cas qui m'intéresse, je veux charger dans un StyleBook à partir d'un fichier, et, sans appliquer le stylebook en extraire les infos
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    aStyle:=TStyleBook.Create(nil);
    try
      aStyle.LoadFromFile('D:\XE8\FMX\ubuntu.win.Style');
      // comment interroger le style aStyle qui n'est pas appliqué ?
      // Rectangle1.Fill:=TRectangle(afmxObj).Fill;
      // Label1.Text:=aStyle.StyleName;
    finally
      aStyle.Free;
    end;
    La seule chose absolue dans un monde comme le nôtre, c'est l'humour. » Albert Einstein

    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Tokyo, Rio) et peut être quelques autres
    SGBD : Firebird 2.5, 3, SQLite
    générateurs Etats : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Ubuntu, Androïd

  5. #5
    Rédacteur/Modérateur

    Bonjour,

    En s'acharnant on fini par trouver après moult essais, ce qui je pense pourrait fournir une jolie FAQ !

    En voilà déjà le schéma principal
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var
      aStyle : TStyleBook;
      aFmxObj : TFMXObject;
    begin
    if Opendialog1.Execute then
    begin
      aStyle := TStyleBook.Create(nil);
      try
        aStyle.LoadfromFile(opendialog1.filename);
        aFmxObj:=aStyle.Style.FindStyleResource('backgroundStyle');
        Image1.Bitmap:=TImage(aStyle.Style.FindStyleResource(TStyleObject(afmxObj).sourcelookup)).Bitmap;
      finally
        aStyle.Free;
      end;
    end;



    Reste que tous les styles ne sont pas sur le même schéma, si ce code fonctionne pour les deux styles que j'ai, pour l'instant, adopté il n'en va pas de même pour la "collection" qu'Embarcadero fourni.
    La ligne 11 est à améliorer
    La seule chose absolue dans un monde comme le nôtre, c'est l'humour. » Albert Einstein

    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Tokyo, Rio) et peut être quelques autres
    SGBD : Firebird 2.5, 3, SQLite
    générateurs Etats : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Ubuntu, Androïd

  6. #6
    Membre chevronné
    Pour info, sur les précédentes versions de Delphi tu pouvait accéder au style en mode texte (voir les messages que j'ai posté il y a quelques années à ce sujet). Il est toujours possible de récupérer le texte du style en copiant la racine du style dans le presse papier. Je ne suis pas allé plus loin dans me recherches.

  7. #7
    Rédacteur/Modérateur

    Bonjour Alain

    Oui, dans mes pistes de recherche il y avait aussi la solution "parser un fichier texte" et même ce que tu nommes la "sérialisation/dé-sérialisation" d'objets.

    Ma méthode, "passer par un stylebook" est avantageuse dans le sens où le "parser" est inutile et remplacée par un "simple" FindStyleResource. Cependant il faut quand même "intimement" connaitre le contenu du fichier style, pour mes recherches j'ai utilisé à la fois le style designer et Notepad++

    Entre-temps ma solution à évoluée
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
     
    procedure TForm102.EllipsesEditButton1Click(Sender: TObject);
    var
      aStyle : TStyleBook;
      aFmxObj : TFMXObject;
      aBitmap : TBitmap;
      aBounds : TBounds;
      aRect : TRect;
    begin
    if Opendialog1.Execute then
    begin
      Edit1.Text:=OpenDialog1.FileName;
      aStyle := TStyleBook.Create(nil);
      aBitmap:=TBitmap.Create;
      try
        aStyle.LoadfromFile(opendialog1.filename);
        aFmxObj:=aStyle.Style.FindStyleResource('backgroundstyle');
        if aFMXObj is TStyleObject then
         begin
          if Assigned(TStyleObject(aFMXObj).SourceLink) then
           begin
            aBounds:=TBitmapLinks(TStyleObject(aFMXObj).SourceLink).Links[0].SourceRect;
            aRect:=TRect.Create(Trunc(aBounds.Left),Trunc(aBounds.Top),Trunc(aBounds.Right),Trunc(aBounds.Bottom));
            ABitmap.Width:=ARect.Right-ARect.Left;
            ABitmap.Height:=ARect.Bottom-ARect.Top;
            aBitmap.CopyFromBitmap(TImage(aStyle.Style.FindStyleResource(TStyleObject(afmxObj).sourcelookup)).Bitmap,aRect,0,0);
           end
          else ABitmap:=TImage(aStyle.Style.FindStyleResource(TStyleObject(afmxObj).sourcelookup)).Bitmap;
         end;
        if aFMXObj is TRectangle then
          begin
            aBitmap.Width:=Trunc(Image1.Width);
            aBitmap.Height:=Trunc(Image1.Height);
            aBitmap.Clear(TRectangle(aFMXObj).Fill.Color);
          end;
        Image1.Bitmap:=aBitmap;
      finally
        aBitmap.Free;
        aStyle.Free;
      end;
     end;
    end;

    Il doit y avoir des simplifications à faire ce n'est encore qu'une ébauche
    Cela à l'air de fonctionner pour tous les styles windows, pour les styles à la pomme ou au droïd impossible de savoir, pour l'instant, si cela ne fonctionne pas à cause du fait que je teste sous windows ou s'il me manque quelque chose genre sélection de l'élément de collection. Je penche pour l'hypothèse "si le style n'est pas supporté, il n'est pas chargé", hypothèse que j'avais du déjà émettre (j'ai une impression de déjà vu)
    Pour pallier ce problème un test pour savoir si le style à charger est supporté sera nécessaire.

    P.S. Je viens de découvrir également comment obtenir titre, auteur, cibles, etc.
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    TStyleDescription(aStyle.Style.FindStyleResource('description')).Title
    TStyleDescription(aStyle.Style.FindStyleResource('description')).Author
    TStyleDescription(aStyle.Style.FindStyleResource('description')).AuthorURL
    TStyleDescription(aStyle.Style.FindStyleResource('description')).PlatFormTarget
    La seule chose absolue dans un monde comme le nôtre, c'est l'humour. » Albert Einstein

    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Tokyo, Rio) et peut être quelques autres
    SGBD : Firebird 2.5, 3, SQLite
    générateurs Etats : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Ubuntu, Androïd