PopUpManager, Window et chargement de contenu

Portrait de titouille

Vu que ça m'a posé des problèmes, et que ça en pose à d'autres, j'en ai profité pour faire un petit exemple et le soumettre à tous pour étude Smile

Donc, tout d'abord, quel est le problème ??

La classe PopUpManager permet de créer, à l'aide du composant "Window", des fenêtres modales qui prennent le focus et demandent une validation avant de rendre le focus au reste de l'application. Très utile pour pouvoir afficher des alertes, des demandes de confirmation et bien d'autres choses.

Tout d'abord, un petit exemple, très simple :
créer un nouveau fla, et y coller le code suivant :

// importation de la classe
import mx.managers.PopUpManager;
var wndAsker;
wndAsker = PopUpManager.createPopUp( 
				this, mx.containers.Window, true, 
				{	title:"mon titre", 
					contentPath:"", 
					closeButton:false 	} );
// set size and position of popup
//
wndAsker.setSize( 370, 255 );
wndAsker._x = Stage.width / 2 - wndAsker._width / 2;
wndAsker._y = Stage.height / 2 - wndAsker._height / 2;

On compile, et... rien Smile Tout ceci est normal. Il ne faut pas oublier que nous avons besoin du composant Window pour afficher une popup. Il faut donc glisser une occurence du composant Window sur la scène, et la supprimer. Tout ceci simplement pour l'avoir dans la bibliothèque.

On recompile, tadaaaa !! une Window en plein centre de notre scène.

Maintenant, allons un peu plus loin. Le but est d'intégrer un contenu dans notre popup. Mais c'est là que se situe le problème. J'ai été confronté plusieurs fois à ce "bug", et j'ai pu constater sur les forums que je ne suis pas le seul. En fait, lorsqu'on charge un contenu composés de contrôles V2, il arrive que, même après l'évenement "complete", certaines propriétés des composants ne soient pas accessibles... L'exemple que je fournis démontre d'ailleurs ce problème avec le composant TextArea.
C'est le même problème qu'avec les Form. Grant Skinner à fait une classe GForm qui crée un setInterval et détermine lorsque les composants sont effectivement exploitables à 100%. Si on essaye de travailler les composants directement depuis le handler onLoad, certaines fonctionnalités sont encore indéfinies et on n'y a donc pas accès...

Continuons : nous allons créer un mc dans la bibliothèque, et y attacher une classe afin de gérer le comportement interne.

J'avais lu un jour en quelque part que pour les composants ayant un "contentPath" tels que le scrollpane, il fallait hériter de la classe mx.core.View afin de faire les choses correctement. Ce n'est pas une obligation, on peu très bien créer une classe héritant de MovieClip. Perso, je vais rester dans la première optique, qui me semble plus logique.

Je vais donc créer tout d'abord un mc contenant 1 TextArea nommé "ctaContent", ainsi que 2 boutons, "cbtOk" et "cbtCancel".

Je vais nommer ce mc "TestPopup", puis je vais afficher ses propriétés. Dans ce panneau, je vais exporter mon mc pour ActionScript, puis je vais lui mettre la classe "TestPopup" en tant que Classe AS2. Je valide le tout, puis j'ouvre mon éditeur pour y créer le fichier TestPopup.as, qui va représenter ma classe.
J'enregistre enfin le fla et la classe dans le même répertoire.

voici le code de la classe :
TestPopup.as

On peut constater différentes choses :

une méthode init(), qui abonne la classe à l'évenement "load".
une méthode load(), qui dispatche un évenement "loadPopup" et initialise le formulaire.
une méthode setMessage(msg:String) qui va ajouter (j'ai bien dit ajouter... +=) un contenu au TextArea
une méthode saveAction qui dispatche un évenement 'click'
une méthode cancelAction qui dispatche un évenement 'click'

Le broadcasting d'évenement est géré avec le composant Window, donc on évite de déclarer une nouvelle instance de la classe EventDispatcher...

Maintenant, voici le code du fla :

import mx.managers.PopUpManager;
//
// button handler to create popup
//
btn.addEventListener( 'click', click );
function click( evt )
	{
	evt.target._parent.createPopup();
	}
//
// create popup handler
//
function createPopup()
	{
	// create popup
	//
	var wndAsker;
	wndAsker = PopUpManager.createPopUp( 
					this, mx.containers.Window, true, 
					{	title:"fill text", 
						contentPath:"TestPopup", 
						closeButton:false 	} );
	//
	// set size and position of popup
	//
	wndAsker.setSize( 370, 255 );
	wndAsker._x = Stage.width / 2 - wndAsker._width / 2;
	wndAsker._y = Stage.height / 2 - wndAsker._height / 2;
	//
	// create object to handle events 
	// ('complete' event, and my own 'loadPopup' event)
	//
	var obj:Object = new Object();
	//
	// handler for 'complete' event
	//
	obj.complete = function( evt )
		{
		// simply add message
		wndAsker.content.setMessage( evt.target._parent.tInfoComplete.text );
		evt.target._parent.cchComplete.selected = true;
		}
	//
	// handler for my own 'loadPopup' event
	//
	obj.loadPopup = function( evt )
		{
		// already simply add message
		wndAsker.content.setMessage( evt.target._parent.tInfoLoad.text );
		evt.target._parent.cchLoad.selected = true;
		}
	//
	// set popup to handle events
	wndAsker.addEventListener( 'complete', obj );
	wndAsker.addEventListener( 'loadPopup', obj );
	wndAsker.addEventListener( 'click', onResultTxtArea );
	}
 
function onResultTxtArea( evt )
	{
	if (evt.detail == 1)
		tInfoResult.text = evt.msg;
	//
	evt.target.deletePopUp()
	cchComplete.selected = false;
	cchLoad.selected = false;
	}

Les premières lignes de code gèrent le click du bouton. une fonction 'click' y est dédiée. Cette dernière appelle la fonction createPopup. C'est cette fonction qui nous intéresse.

J'y crée d'abord ma popup, en lui passant l'identifiant d'export de mon mc (TestPopup). Puis je déclare un objet qui va intercepter l'évenement standard 'complete' ainsi que l'évenement personnalisé 'loadPopup'. Dans les 2 cas, je fais appel à la méthode interne de TestPopup pour ajouter le texte concernant l'évenement appelé.
Enfin, j'abonne la popup à l'évenement 'click' qui est généré en interne dans la classe TestPopup, et qui retourne un détail à respectivement 0 pour une annulation, et 1 pour une validation. Dans le cas d'un détail valide, l'évenement contient une propriété "msg" contenant la valeur du message retourné.

Maintenant, on peut constater dans le code que la classe fait bien une concaténation au niveau de la méthode setMessage, elle n'écrase pas à chaque fois les valeurs, et pourtant, le texte concernant l'évenement 'complete' n'apparait jamais... Mais celui de mon évenement personnalisé fonctionne Smile

Voici donc un bon moyen d'alerter la scène lorsque mon contenu est réellement accessible, et non pas lorsque il est completement chargé, mais que les composants ne sont pas encore totalement exploitables.

les liens :

Exemple
TestPopup.as
sources





Salut,
je viens de suivre des conseils concernant le composant Window, et j'arrive effectivement bien à créer la PopUp, et l'ensemble du contenu du movie clip qu'elle renferme est bien intialisé.
Mais il subsite un problème que je n'arrive pas à résoudre. Je fais une application avec des forms flash. J'ai un accès Admin, et pour cet accès, je voulais créé une pop up où les administrateur mette leur login et mot de passe. Mais le bouton qui créé la popup est un bouton que j'ai créé moi-même, et n'est pas un composant Bouton, et donc, j'ai remarqué que je n'arrivais à utiliser les méthodes addEventListener, mais seulement les onRelease = function() {}. La popup se créé facilement, mais après je n'arrive pas utiliser convenablement owner._parent.dispatchEvent( {type:"click", detail:0} ); car wndAsker.addEventListener( "click", Delegate.create(this,onResultPopUp) ); se trouve dans le onRelease du bouton, et la fonction onResultPopUp se trouve dans mon formulaire... et donc pas dans le _level0, et donc je n'ai aucun événement qui écoute le "click", c'est très embettant :'(

J'avoue avoir beaucoup de problèmes avec les forms, en effet je débute. Si tu avais une solution à me proposer, j'en serai heureux. Si tu ne comprends pas ce que j'ai raconté, on peut toujours se voir sur msn.

Un grand merci

Pour continuer avec mon commentaire précédent et pour éclaircir les idées, tout fonctionne correctement comme exmpliqué ici sur le site lorsque j'utilise un composant bouton auquel je peux associer la méthode addEventListener. Par contre, dès que je fais un de mes propres boutons, plus rien ne fonctionne... Alors, ma question, plus précise, est, comment finalement, faire fonctionner un bouton fait de manière classique dans Flash, comme un composant Button, ou alors comment facilement changé le design d'une instance de bouton de composant Button. (associer la classe mx.controls.Button à mon bouton n'a rien changé...)
Un grand merci pour votre réponse,

William.

Portrait de titouille

Hello William !

Tu devrai te pencher un peu sur l'utilisation du Delegate, ça te permettrai de comprendre ou se trouve ton erreur.

Le Delegate permet d'exécuter du code dans une autre portée que la portée habituelle. Donc à la place d'utiliser "this" qui dans ton code, va représenter le bouton, tu devrai simplement lui passer "this._parent", et ainsi, pointer sur la scène (_level0):

En espérant que ça t'aide Wink

Salut Titouille,
merci pour ton conseil, en effet, il y avait une erreur à ce niveau-là, mais... :'( le problème reste le même. c'est la méthode addEventListener qui n'a pas l'air de réagir. Comme je l'ai dis dans mon post précédent, avec un composant Button, tout fonctionne à merveille maintenant, mais par contre avec un bouton que je fais moi-même de manière classique dans Flash, c la misère, la popup s'affiche, mais les boutons n'obéissent à plus aucune action...

Portrait de titouille

A première vue, ça semble être typiquement un problème de portée...
Fais des traces un peu partout pour savoir ou tu te trouve, dans quelle portée selon l'action.

Pour donner à un bouton standard le même comportement qu'un bouton v2, tu peux simplement valoriser les gestionnaire onPress, onRelease, etc... Maintenant, il faut voir comment tu crée les gestionnaires... D'après ce que je pense, tu dois cibler les mauvais éléments...
Mets un "trace( this )" dans ton gestionnaire Press du bouton, pour voir qui est "this" à cet endroit, et fait les liaisons en conséquence.

Bon courage

Salut,

Je viens de tomber sur ton site car justement je faisais une recherche sur le composant window concernant l'accessibilité du contenu. Donc article apporte une reponse a ce probleme et je t'en remercie.
Mais j'en profite pour te demander si tu penses qu'il existes une autre méthode qui éviterai d'associer une classe pour chaque clip ou element qui forme le contenu de la window ?

Merci mec Wink

Portrait de titouille

Hello SunShine.
Si tu veux éviter de passer par une classe AS2 à associer au contenu, tu peux directement valoriser le contentPath du composant Window pour lui passer un movieClip, sans pour autant que ce dernier soit lié à une classe. Mais pour ce qui est de la gestion et la récupération du contenu, il faudra nécessairement du code pour pouvoir intercepter les actions.

A+