(résolu) Bindings : je me relance...

LeChatNoirLeChatNoir Membre, Modérateur
décembre 2005 modifié dans API AppKit #1
Slt,

Bon je me relance dans les bindings. C'est pas que ca m'enchante mais bon, parait que ca fait gagner du temps  (mais moi, ca me rend B) ).

Donc voilà  le topo :

J'ai une hiérarchie d'objet perso sous forme d'arbre binaire (un objet a un père et n fils qui sont eux même des objet du meme type). Entre autre attribut, mes objets ont une description.

J'ai donc une outlineview qui représente visuellement cette hiérarchie (sans bindings).
Maintenant, j'aimerai que, quand on sélectionne un item dans l'outlineview, ca affiche le détail de l'objet sélectionné dans une autre vue. Quand je parles de détail, j'entend, les autres attributs de cet objet.

Et ça, j'aimerai que ca soit par bindings histoire de mettre un peu le nez dedans.

Donc comment faut il procéder ?

Je fais un objet tampon dans lequel je met les données de l'objet sélectionné dans mon outlineview et je "binde" cet objet là  avec ma vue ?

J'ai du mal à  voir par où commencer...

Réponses

  • 12:19 modifié #2
    Tiger: utiliser un NSTreeController de la meme manière qu'on utilise un NSArrayController avec une NSTableView (il faut juste spécifier la clé childs)

    Mais comme tu ne veux pas faire du Tiger only, le plus simple est de binder tous tes champs à  un NSObjectController. Puis tu utilises une méthode de délégué de NSOutlineView pour détecter le changement de ligne (outlineviewdidChangeSelection: de mémoire) dans laquelle tu modifies le content du nsobjectcontroller (tu peux récupérer le bon item avec itemAtRow:).
  • LeChatNoirLeChatNoir Membre, Modérateur
    12:19 modifié #3
    Merci Renaud,

    Je ne sais pas comment tu as deviné mais effectivement, je ne veux pas faire du Tiger Only. Je veux faire du "Panther at least  :P".

    Donc cet NSObjectController, je l'instancie direct dans IB (là , je fais appelle à  mes souvenirs de quand j'avais abordé les bindings).

    Ensuite, je binde tous mes champs visuels avec les propriétés de ce controller.


    Donc dans le modèle MVC, les bindings entre Vue et controller, ca va (facile, ca se fait sous IB).
    C'est la partie Controller/Modèle que je vois moins.

    D'après ce que tu suggères, je n'utilise pas les bindings ?
    Je vais simplement copier à  la main ce qu'il faut dans mon objet controller ?

    Et si j'ai une modification d'un champ (parce que ce sont des champs éditables), comment se passe la mise à  jour de l'objet impacté dans mon modèle (mon arbre) ?

    Merci !


  • décembre 2005 modifié #4
    Si si tu utilises les bindings, il y a juste la partie "lien" entre la sélection de la outline et le contenu du object controller qui est sans bindings, car sous Panther, les outline ne sont pas bindables. Tu ne fais que fournir à  l'objectcontroller un contenu, et lui va se charger de remplir les champs et mettre à  jour l'objet si modification il y a (et tout ça par bindings).

    dans 1134478993:

    Et si j'ai une modification d'un champ (parce que ce sont des champs éditables), comment se passe la mise à  jour de l'objet impacté dans mon modèle (mon arbre) ?


    J'avais oublié cet aspect. Dans le awakeFromNib du controller (celui que tu as fais, pas l'object controller), tu rajoutes des observer sur l'objectcontroller comme suit (par exemple pour la clé name):
    [objectController addObserver:self forKeyPath:@"selection.name" options:0 context:_nameContext];
    


    avec écrit avant le @implementation
    static NSString* _nameContext = @"nameContext";
    


    et tu rajoutes l'implémentation de la méthode qui sera exécutée pour lorsqu'il y aura modification d'une valeur observée:
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {<br />	if (context == _nameContext) {<br />		[outlineView reloadItem:[objectController content]];<br />	}<br />}
    


    L'utilisation la variable statique passée en argument de context permet de faire des comparaisons de pointeurs dans l'implémentation de observerValue, ce qui est nettement plus efficace que de passer par des [keypath isEqualToString:@selection.name];

    À répéter pour toutes les colonnes de ta outline.
  • LeChatNoirLeChatNoir Membre, Modérateur
    décembre 2005 modifié #5
    dans 1134481129:

    Tu ne fais que fournir à  l'objectcontroller un contenu, et lui va se charger de remplir les champs et mettre à  jour l'objet si modification il y a (et tout ça par bindings).


    Ok. C'est cette partie sur laquelle je m'interroge : comment binder mon objectcontroller avec mon modèle ?
    Sachant que mon modèle n'est pas instancié dans IB et qu'il s'agit d'un arbre.

    L'outline ne sert effectivement que de déclencheur.

    Mais quand je me relis, je me dis que je suis pas clair.

    Clarifions donc la chose :

    dans les préférences de Mail, tu as une liste avec les comptes paramétrés.
    Quand tu sélectionne un compte, tu as le détail qui s'affiche à  côté.

    C'est exactement ça que je veux avec une notion de hierarchie entre les comptes en plus (bon ca, c'est peut être pas important).

    J'ai donc mon arbre d'objets. Les noms et la hiérarchie sont affichées dans l'outlineview sans bindings.

    A la sélection d'un item, je veux avoir le détail de l'objet correspondant.
    Et pouvoir modifier ce détail.

    Mais je ne vois toujours pas comment faire ca par bindings...
    Comment binder mon arbre à  mon objet controller...


  • décembre 2005 modifié #6
    J'ai parfaitement compris ce que tu voulais faire. Les bindings se sont pas possible pour les outlineview sous panther, donc il n'y a pas moyen de faire ça par ce biais, il te faut donc passer par du code "classique".

    Tu ne peux pas le faire par bindings, vu que pour gérer les arbres, il faut utiliser NSTreeController et ça ce n'est pas possible sous Panther. Donc tout ce que tu peux faire c'est utiliser des méthodes de délégué de ta outline pour etre informé du changement de sélection et modifier le content du objectController par code pour qu'il corresponde à  la sélection (cf. première réponse).

    Donc en résumé:
    - le lien entre master et detail: code classique
    - le "detail": bindings
    - la mise à  jour du master en fonction du détail: KVO
  • LeChatNoirLeChatNoir Membre, Modérateur
    12:19 modifié #7
    Ah ben tu vas pas y croire mais je crois que j'ai compris  ::)

    Dis moi si je suis bon à  jeter ou pas :
    - le lien master et détail : au changement de sélection dans mon outline, je copie le détail dans mon objet controller
    - le détail : mon objet controller est bindé avec l'interface graphique => quand je change un champs de l'interface, le contenu de lo'objet controller change automatiquement,
    - la mise à  jour du master : dans mon controller principal, j'observe les changements des attributs de mon objet controller et les répercute sur mon vrai objet.

    Alors, à  la poubelle le ChatNoir ou pas encore ?

    Merci Renaud !
  • 12:19 modifié #8
    Presque ;)

    pour la mise à  jour du master: le objectController fait les modifications dans l'objet contenu dans ton arbre. Simplement ce qui est affiché dans la outline n'est pas à  jour et donc il faut la "forcer" à  se mettre à  jour (le reloadItem: est comparable à  un reloadData, mais c'est nettement plus sélectif comme mise à  jour).

    PS: un chat noir çe ne se jette pas à  la poubelle, ça se cloue sur la porte d'entrée.
  • LeChatNoirLeChatNoir Membre, Modérateur
    décembre 2005 modifié #9
    ah ? Je me raccroche au bord (de la poubelle) là Â  :(
    ou j'arrache un clou d'une de mes pattes (au choix).

    Alors je n'ai qu'à  observer l'attribut qui m'intéresse pour maj de l'outline.
    C'est ce que tu disais dans ton 2eme post que je comprend mieux maintenant.

    Mais je n'arrive pas à  comprendre comment diable cet objectController va faire le lien avec mon objet à  moi ?

    C'est là  qu'il faut le "binder". Par code donc ?
  • décembre 2005 modifié #10
    [tt]- (void)outlineViewSelectionDidChange:(NSNotification *)notification {
    [objectController setContent:[outlineView itemAtRow:[outlineView selectedRow]]];
    }[/tt]

    Le faire par binding est possible aussi, mais pas directement avec la table:
    1. Tu bindes le contentObject de objectController à  ton controller. Comme keyPath, tu mets par exemple selection.
    2. tu crées un variable d'instance  selection (ou _selection suivant tes conventions). Et dans la méthode mentionnée ci-dessus, tu modifies ta variable d'instance.

    Mais bon cela rajoute des étapes, et je ne sais pas si dans ton cas c'est vraiment utile.
  • LeChatNoirLeChatNoir Membre, Modérateur
    12:19 modifié #11
    à  ben on va pas y arriver...

    Dans ce sens, je comprend.
    Mais pour moi, on est dans le cas 1 - le lien entre master et détail.

    Mais dans l'autre sens ?

    Ou alors....

    Le setContent ne fait peut être pas une copie ?
    Et l'objectController pointe bien vers monObjetAMoi ?
    Donc mon objet est impacté à  chaque changement de l'objectController ?

    C'est ça ? ou je me jette par la fenêtre ?
  • décembre 2005 modifié #12
    setContent ne fait pas de copie, et donc c'est ton objet qui est modifié. ça m'avait échappé dans ton récapitulatif.

    Allez, il y a une place près du feu et une coupelle de lait.
  • LeChatNoirLeChatNoir Membre, Modérateur
    12:19 modifié #13
    Alleluia Renaud est grand  o:)

    Il m'a fallut du temps mais c'est ce pu&@#n de setContent qui m'a mis dedans...

    Merci, merci et encore merci !

    Je tente ça ASAP (oh temps... suspend ton vol !) et je te paye ma tournée  :p
    ...
    de lait  ::)
    si ça marche  :(
  • aranaudaranaud Membre
    12:19 modifié #14
    dans 1134473142:

    Tiger: utiliser un NSTreeController de la meme manière qu'on utilise un NSArrayController avec une NSTableView (il faut juste spécifier la clé childs)

    Serais tu où je peux avoir plus d'information sur cette classe : NSTreeController (en français si possible).
  • LeChatNoirLeChatNoir Membre, Modérateur
    12:19 modifié #15
    Salut,

    Bon j'ai fait mon objectController dans IB, je l'ai bindé à  un TextField de mon interface et dans mon controleur, j'essaye d'afficher le contenu.

    En gros, j'ai mis une key "host" et dans mon controleur, je fais un NSLog(@controleur : %@", [[moncontroleur selection] valueForKey:@host]);

    Et y a jamais rien  :'(
    Je pensais que cette simple manip permettait de binder l'objet controleur à  l'interface mais c'est pas très concluant, que je saisisse du texte ou non... (validé ou non)..

    Y a un truc qui m'échappe j'ai l'impression...
  • LeChatNoirLeChatNoir Membre, Modérateur
    décembre 2005 modifié #16
    Bon ben finalement, j'ai réussi !
    J'avais pas bien saisi le coup du type de l'objet gére par le controleur...
    C'est génial les bindings !
    J'arrive meme à  binder le "enable" de mes champs selon la sélection dans la outline !
    Bon je pense etre loin de tout maitriser mais le peu que je viens de decouvrir, c'est trop génial !

    Merci encore Renaud !
    o:)
  • GercofisGercofis Membre
    12:19 modifié #17
    L'un ou l'autre vous auriez pas une idée pour lier le NSArrayController a la TableView de façon a implémenter le D&D ?
  • LeChatNoirLeChatNoir Membre, Modérateur
    12:19 modifié #18
    Slt,

    je connais pas bien les TreeController mais il me semble que ca permet de binder un arbre ou une structure hiérarchique à  une outlineview (voire une tableview ?).

    Mais je vois pas trop le lien avec le D&Drop ?

    Ou alors, Tiger pousse vraiment le vice plus loin dans les bindings et je n'ai pas vraiment creuser (vu que je veux que mon appli tourne pour Panther).

    Pour le Drag&Drop dans une tableview, regarde la doc Apple sur les tablesview et plus particulièrement le topic "Drag&Drop with TableView".
    C'est bien expliqué.

    Et tu as le fameux exemple dans developper/examples/appkit et nommé DragNDrop OutlineView.

    a+
Connectez-vous ou Inscrivez-vous pour répondre.