IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
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

Windows Presentation Foundation Discussion :

[WPF-XAML] freeBrowser 1.0 - Système de skin


Sujet :

Windows Presentation Foundation

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Février 2004
    Messages
    23
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 23
    Par défaut [WPF-XAML] freeBrowser 1.0 - Système de skin
    Bonsoir à tous

    Petite présentation :

    Je suis le développeur d'un projet open source media center pour la freebox, le web et le PC, et ma petite idée est de migrer le moteur de rendu de la version PC vers WPF.


    Je vous présente ma question, puis vous explique en gros le contexte :

    "Comment faire en sorte qu'une source de données ait des paramètres qui proviennent d'une autre source de données?"


    Petit screen :



    Pour plus de détails et de screens, vous pouvez voir ce lien : http://forum.freebrowser.fr/viewtopic.php?t=2276
    (il ne s'agit que de la version freebox, la version PC étant justement en cours de dev)

    La technique :


    Le logiciel utilise un système de skin accompagné par l'achitecture qui définie ces axes :

    - Une couche de plugins qui permet d'alimenter en contenu. Le contenu est organisé de manière arborescente, ex pour le plugin permettant d'afficher la liste des répertoires, fichiers, lecteurs :



    - Un langage de template capable d'interroger en live les plugins et organiser leur contenu dans la page de rendu final.

    Ex (pour un rendu HTML orienté freebox) :
    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
     
    <content Type=Jukebox>
    <table> 
       <CM.Content.Jukebox.Album> 
          <tr> 
             <td> 
                <u>Nom de l'album : <CM.Content.Jukebox.Album.AlbumName/></u> 
             </td> 
          </tr> 
          <tr> 
             <td> 
                <b>Liste des musiques :</b> 
             </td> 
          </tr> 
          <CM.Content.Jukebox.Album.Song> 
          <tr> 
             <td> 
                <CM.Content.Jukebox.Album.Song.SongNumber/> - 
                <CM.Content.Jukebox.Album.Song.SongName/> 
             </td> 
          </tr> 
          </CM.Content.Jukebox.Album.Song> 
       </CM.Content.Jukebox.Album> 
    </table>
    </content>
    - D'autres couches comme le media player interchangeable, l'interface de configuration qui peut etre un serveur HTTP, un menu déroulant via une icône dans le systray ou tout autre chose, et la couche de présentation qui permet justement de gérer les différentes interfaces : freeplayer, web, Standalone WPF.

    Chaque couche est composée de plugins (ont peut donc facilement ajouter de nouveaux composants pour chaque couche) :

    • freeBrowser.ViewControl.Freeplayer.dll
    • freeBrowser.ViewControl.Standalone.dll
    • freeBrowser.ViewControl.Web.dll


    • freeBrowser.Plugin.Browser.dll
    • freeBrowser.Plugin.Playlist.dll
    • freeBrowser.Plugin.Jukebox.dll
      (plus de 30 plugins)


    • freeBrowser.ConfigManager.Win32Systray.dll
    • freeBrowser.ConfigManager.HttpServer.dll


    • freeBrowser.MediaPlayer.VLC.dll



    au milieu de tout ca, il y'a un noyau qui charge toutes les couches au démarrage.
    L'application est développée en .net 2.0 / mono

    Voila pour le petit tour d'horizon technique.


    Maintenant la question :} :

    [B]La Question : [B]

    Pour la version PC qui utilisera XAML, il y'aura toujours un système de skin qui chargra les fichiers .xaml depuis le disque pour les interpréter dynamiquement.

    Ma question porte sur la gestion des données.

    En WPF il existe plusieurs objets dont un qui m'interesse plus particulièrement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    <XmlDataProvider  x:Key="data" Source="/Data/MaSource.xml" />

    Mon idée est de pouvoir faire des skins entièrement en XAML, sans utiliser le langage de template que j'ai développé pour l'occasion. Cela permettra de faire des skins directement avec Expression Blend (anciennement Interactive designer).

    L'idée est de sous classer le XmlDataProvider afin qu'il ait 2 rôles :

    1 : Un fournisseur de données au format XML (donc arborescent), tout comme l'est XmlDataProvider, en spécifiant une ressource via la propriété Source= . Cela est utile pour pouvoir designer une skin avec du contenu factice afin d'avoir une idée du rendu final.


    2 : 2 nouvelles propriétés (c'est sur ces propriétés que portent ma question, j'y arrive enfin :} )


    ContentType= , pour définir quel est le plugin à interroger. Ex :
    ContentType="Files" ou en core ContentType="Jukebox"

    ContentParameters= , pour définir les paramètres du plugin. Par exemple pour spécifier le répertoire courant pour le plugin Files.

    C'est sur ce paramètre que ma question se pose.

    Je voudrais pouvoir binder depuis une autre source de données le contenu de ContentParameters, Cela donnerait un code dans ce genre là :


    <fB:fB_XmlDataProvider
    x:Key="dataFiles"
    Source="\XmlData\Files.xml"
    ContentType="Files"
    ContentParameters="{Binding Mode=Default,
    Source={StaticResource dataParameters},
    XPath=CurrentDirectory}" />



    dataParameters est une autre source de données.

    Cette syntaxe ne fonctionne pas, car la propriété ContentParameters n'est pas de type DependecyProperty.

    De plus on ne peut mettre des propriétés de type DependencyProperty que dans des objets qui héritent de DependencyObject
    , ce qui n'est pas mon cas car je sous classe XmlDataProvider :/

    Mon autre idée était de composer avec une autre classe qui elle porte des propriétés DepencyProperty, mais cela ne semble pas fonctionner non plus :/


    Pour résumer :

    "Comment faire en sorte qu'une source de données ait des paramètres qui proviennent d'une autre source de données?"

    Il y'a peut etre un élément de réponse du côté de DataObjectProvider qui est capable de gérer une liste de paramètres, mais là non plus ca ne semble pas non plus bindable.


    Merci d'avance pour les réponses, je sais que la question est assez pointue, c'est pour cela que j'ai voulu être le plus exhaustif sur la question, en espérant que ca ne rebutera pas les forumeurs

  2. #2
    Rédacteur
    Avatar de Thomas Lebrun
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    9 161
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 9 161
    Par défaut
    Salut


    Tout d'abord, bravo pour ton projet: personnellement, je n'utilise pas Freebox mais cela semble fort prometteur et intéressant

    Pour répondre à ta question, je pense que le mieux est que tu tu créés un CustomXmlDataProvider (qui hérites de XmlDataProvider) et que tu lui ajoutes 2 DependencyProperty: ContentType et ContentParameters.

    En effet, tu ne peux pas binder/Animer/transformer des propriétés qui ne sont pas de DP. Et de cette façon, tu pourras le faire


    Par contre, je me permet une remarque:

    Citation Envoyé par GeffD
    Pour la version PC qui utilisera XAML, il y'aura toujours un système de skin qui chargra les fichiers .xaml depuis le disque pour les interpréter dynamiquement.
    Il faut savoir que le chargement dynamique de XAML fonctionne, pas de soucis à condition que cela soit ce que l'on appelle du "Loose XAML", autrement dît du XAML dans lequel tu n'as pas d'évèmenent, etc..

    Ex: Si tu essayes de de charger un fichier de ce type, cela ne fonctionnera pas:

    <Button x:Name="MyBtn" Content="Play" Click="BtnClick" />

    Et il me semble également que si ton fichier XAML fat référence à d'autre RessourceDictionnary, là encore, cela risque de ne pas fonctionner....


    Si tu as d'autres questions, n'hésites pas


    A+

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Février 2004
    Messages
    23
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 23
    Par défaut
    Bonjour Thomas, merci d'avoir pris le temps de répondre


    En fait j'ai exactement fait comme tu le proposes, à savoir hériter fB_XmlDataProvider de XmlDataProvider.

    Cependant on ne peut pas ajouter de DependyProperty sur un tel objet car pour se faire il faut que la classe soit de type DependencyObject.


    Mon astuce a été ensuite de composer avec une autre classe perso, qui elle hérite de DependencyObject et qui porte les 2 propriétés à binder.

    Là non plus ca ne semble pas fonctionner, je vais refaire l'essai pour te montrer les messages d'erreur.


    Par contre, je me permet une remarque:
    Toutesl es remarques sont bonnes à prendre, je suis même extrêment demandeur, histoire de ne pas faire fausse route

    Je vais apporter quelques précisions :

    Il y'a bien des évènements qui sont gérés, mais pour cela j'ai développé quelques contrôles simples à savoir :

    fB_Image, fB_Label, fB_Button

    J'ai sous classé ces contrôles car ils doivent tous avoir 2 propriétés en plus pour permettre la navigation (Action et ActionParameters)

    Au chargement du fichier .xaml, le logiciel créé automatiquement les liaisons avec les évènements de base.

    Les 3 contrôles sont alors liés à un même évènement, qui permet la navigation ou le lancement d'une action lors du click.

    Il y'a une totale désolidaristation entre les fichiers .xaml et le logiciel. Tout est lié dynamiquement au chargement.

    Mes essais sont concluants, le temps de réponse n'est pas mauvais non plus.

    Donc je vais retenter avec ma classe fB_XmlDataProvider pour t'apporter plus de précisions sur les erreurs que m'affiche WPF.

    Merci encore

  4. #4
    Rédacteur
    Avatar de Thomas Lebrun
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    9 161
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 9 161
    Par défaut
    Citation Envoyé par GeffD
    Cependant on ne peut pas ajouter de DependyProperty sur un tel objet car pour se faire il faut que la classe soit de type DependencyObject.
    Effectivement, je n'avais pas fait attention au fait que XmlDataProvider n'hérite pas de DependencyObject.

    Là non plus ca ne semble pas fonctionner, je vais refaire l'essai pour te montrer les messages d'erreur.
    Ok


    Au chargement du fichier .xaml, le logiciel créé automatiquement les liaisons avec les évènements de base.

    Les 3 contrôles sont alors liés à un même évènement, qui permet la navigation ou le lancement d'une action lors du click.
    Je serais curieux de voir comment tu fais, car lors de mes tests avec XamlReader.Load, le code que je t'ai donné ci-dessus ne fonctionnait pas.....

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Février 2004
    Messages
    23
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 23
    Par défaut
    Je viens de refaire un essai.

    Mon essais porte ce principe :

    • Une Classe fB_XmlDataProvider qui hérite de XmlDataProvider et qui compose avec une autre classe : fB_ContentProperties
    • Une classe fB_ContentProperties qui hérite de DependencyObject et qui porte 2 propriétés de type DependencyProperties


    Code de la classe fB_XmlDataProvider
    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
     
    namespace freeBrowser.ViewControl
    {
        /// <summary>
        /// Interaction logic for fB_XmlDataProvider.xaml
        /// </summary>
     
        public partial class fB_XmlDataProvider : XmlDataProvider
        {
            public fB_XmlDataProvider()
                : base()
            {
                if (freeBrowser.ViewControl.StandaloneViewer.IsLoading)
                {
                    this.Source = null;
                    this.IsAsynchronous = false;
                }
     
                this.contentProperties = new fB_ContentProperties();
            }
     
            private fB_ContentProperties contentProperties = null;
     
            public string ContentType
            {
                get{ return contentProperties.ContentType;}
                set{contentProperties.ContentType=value;}
            }
     
            public string ContentParameters
            {
                get { return contentProperties.ContentParameters; }
                set { contentProperties.ContentParameters = value; }
            }
     
            protected override void EndInit()
            {
                base.EndInit();
            }

    code de la classe fB_ContentProperties :

    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
     
    namespace freeBrowser.ViewControl
    {
        public class fB_ContentProperties : DependencyObject
        {
            public fB_ContentProperties()
            {
                this.ContentParameters = "";
                this.ContentType = "";
            }
     
            public static DependencyProperty ContentTypeProperty = DependencyProperty.Register("ContentType", typeof(string), typeof(fB_ContentProperties));
            public string ContentType
            {
                get { return (string)this.GetValue(ContentTypeProperty); }
                set { this.SetValue(ContentTypeProperty, value); }
            }
     
            public static DependencyProperty ContentParametersProperty = DependencyProperty.Register("ContentParameters", typeof(string), typeof(fB_ContentProperties));
            public string ContentParameters
            {
                get { return (string)this.GetValue(ContentParametersProperty); }
                set { this.SetValue(ContentParametersProperty, value); }
            }
        }
    }

    Voici maintenant le code XAML de la page concernant les 2 sources de données :

    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
     
    <!-- La source de données du second fB_XmlDataProvider -->
     
    <fB:fB_XmlDataProvider 
    x:Key="dataParameters" 
    Source="\XmlData\Parameters\Parameters_Explorateur.xml" 
    ContentType="Parameters" 
    XPath="Parameters" />
     
     
    <!--- La source de données qui prend ses paramètres de l'autre source de données, pour initialiser son contenu -->
    <fB:fB_XmlDataProvider 
    x:Key="dataSubdirectories" 
    Source="\XmlData\SubDirectoriesDB.xml" 
    ContentType="Parameters" 
    XPath="Parameters" 
    ContentParameters="{Binding Mode=Default, Source={StaticResource dataParameters}, XPath=/Parameters/Directory}" />
    Le problème porte sur la dernière ligne :

    ContentParameters="{Binding Mode=Default, Source={StaticResource dataParameters}, XPath=/Parameters/Directory}"


    Voici l'erreur renvoyée :
    A 'Binding' cannot be set on the 'ContentParameters' property of type 'fB_XmlDataProvider'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
    Visiblement mon astuce de composition avec un DependencyObject ne fonctionne pas :/


    Je serais curieux de voir comment tu fais, car lors de mes tests avec XamlReader.Load, le code que je t'ai donné ci-dessus ne fonctionnait pas.....
    Pour ma part j'utilise des contrôles utilisateurs qui héritent de leur contrôle de base, voici un exemple de code :

    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
     
    public partial class fB_Image : System.Windows.Controls.Image, IfB_Control
        {
            public fB_Image()
            {
                try
                {
                    if(freeBrowser.Core.ViewControlManager.ViewControlWorker.CurrentViewControl != null)
                        this.MouseLeftButtonUp += new MouseButtonEventHandler(freeBrowser.ViewControl.Standalone.CurrentStandaloneViewer.Action);
                }
                catch { }
            }
     
            private string _action;
            public string Action
            {
                get { return _action; }
                set { _action = value; }
            }
     
            private string _actionParameters;
            public string ActionParameters
            {
                get { return _actionParameters; }
                set { _actionParameters = value; }
            }
        }


    Action est une méthode qui gère tous les clicks de tous les contrôles spécifiques à freeBrowser.

    Cependant, si tu veux lier des contrôles classiques avec des évènements, tu peux très bien faire ca de manière récursive, voici un exemple :

    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
     
            private void LoadXamlFile(string strXamlFile)
    {
                    DependencyObject rootElement = (DependencyObject)XamlReader.Load(strXamlFile);
                    IEnumerator en = LogicalTreeHelper.GetChildren(rootElement).GetEnumerator();
                    LinkButton(en);
    }
     
     
            private void LinkButton(IEnumerator en)
            {
                while (en.MoveNext())
                {
                    if (en.Current is Button)
                    {
                        Button button = (Button)en.Current;
                        button.Click += new RoutedEventHandler(button_Click);
                    }
                    else if (en.Current is DependencyObject)
                    {
                        IEnumerator en2 = LogicalTreeHelper.GetChildren((DependencyObject)en.Current).GetEnumerator();
                        LinkButton(en2);
                    }
     
                }
            }
    Par contre, il ne faut pas que ton bouton ai un évènement Click de défini dans ton code XAML.
    Au pire, tu peux parser ton fichier XAML au chargement pour voir l'évènement Click, détecter la méthode dans ton code et l'enlever du xml, puis enfin charger le fichier XAML.
    Cependant c'est un peu du bricolage :]


    En tout cas mon problème est toujours le même :/

    Apparement il n'est pas possible de dire au fB_XmlDataProvider quels sont ses paramètres de manière dynamique (via une autre source de données).

    Peut etre que je devrait faire un autre user control qui porte cette propriété et qui pointe vers le contrôle fB_XmlDataProvider.
    Cette solution fonctionnera sans doute mais n'est pas très élégante je trouve :/

    D'autres idées, remarques?
    Merci d'avance pour les éventuelles réponses

  6. #6
    Rédacteur
    Avatar de Thomas Lebrun
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    9 161
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 9 161
    Par défaut
    Citation Envoyé par GeffD
    Le problème porte sur la dernière ligne :

    ContentParameters="{Binding Mode=Default, Source={StaticResource dataParameters}, XPath=/Parameters/Directory}"



    Visiblement mon astuce de composition avec un DependencyObject ne fonctionne pas :/
    Il est normal que cela ne fonctionne pas car tu ne changes pas le problème.
    Je te l'ai dit: pour pouvoir être bindée, une propriété doit être une Dependency Property.
    Donc, que tu fasses le binding directement ou en passant comme tu l'as fait, le problème reste le même: tu veux faire du binding donc il te faut une DP.

    Le mieux pour toi est donc, sans doute, de créer ton propre XmlDataProvider: après tout, ce n'est qu'un objet qui lit un fichier XML, effecue une requête XPath dessus et renvoit le résultat.
    Tu devrais aisément pouvoir créer un CustomXmlDataProvider qui hérite de Dependency Object et qui fait les 2 taches citées ci-dessus



    Cependant, si tu veux lier des contrôles classiques avec des évènements, tu peux très bien faire ca de manière récursive, voici un exemple :

    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
     
            private void LoadXamlFile(string strXamlFile)
    {
                    DependencyObject rootElement = (DependencyObject)XamlReader.Load(strXamlFile);
                    IEnumerator en = LogicalTreeHelper.GetChildren(rootElement).GetEnumerator();
                    LinkButton(en);
    }
     
     
            private void LinkButton(IEnumerator en)
            {
                while (en.MoveNext())
                {
                    if (en.Current is Button)
                    {
                        Button button = (Button)en.Current;
                        button.Click += new RoutedEventHandler(button_Click);
                    }
                    else if (en.Current is DependencyObject)
                    {
                        IEnumerator en2 = LogicalTreeHelper.GetChildren((DependencyObject)en.Current).GetEnumerator();
                        LinkButton(en2);
                    }
     
                }
            }
    Par contre, il ne faut pas que ton bouton ai un évènement Click de défini dans ton code XAML.
    OK donc c'est bien ce que je pensais: tu triches

    Tu attaches les évènements par le code C# et pas en XAML.



    A+

Discussions similaires

  1. WPF, XAML, Visual Studio 2010 Designer : exception non gérée
    Par nicopulse dans le forum Windows Presentation Foundation
    Réponses: 4
    Dernier message: 09/06/2010, 22h41
  2. [WPF & XAML] Personnaliser control button
    Par NeoKript dans le forum Windows Presentation Foundation
    Réponses: 2
    Dernier message: 23/02/2010, 08h56
  3. [WPF / XAML] Propriété topmost = true mais fenetre en arriére plan
    Par UNi[FR] dans le forum Windows Presentation Foundation
    Réponses: 4
    Dernier message: 16/01/2008, 14h32
  4. Réponses: 3
    Dernier message: 16/01/2008, 11h45

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo