février
2008
Pour faire suite à mes précédents billets, je vais vous exposer un problème concernant l’initialisation des valeurs d’un usercontrol et surtout leurs récupérations après un postback, lorsque le EnableViewstate = false.
Lorsqu’on a une Page qui utilise un controle utilisateur et qui souhaite passer des valeurs à ce controle utilisateur gràce à ses propriétés, on se heurte à un problème d’enchainement des événements.
En effet, l’événement OnInit du controle utilisateur va etre levé avant le OnInit de la page, ainsi (en reprenant l’exemple de mon ancien billet où on a décalé la logique des controles de la page dans un usercontrol), on se retrouve avec :
Dans le OnInit de la page :
{
EnableViewState = false;
TestUC.Valeurs = new List<List<string>>(new List<string>[] { new List<string>(new string[] { "abc", "def", "ghi" }), new List<string>(new string[] { "123", "456", "789" }) });
base.OnInit(e);
}
Dans le OnInit du controle utilisateur :
{
MonRepeater.DataSource = _valeurs;
MonRepeater.DataBind();
base.OnInit(e);
}
On se rend tout de suite compte qu’il va y avoir un problème. Le OnInit de l’user control étant déclenché avant celui de la page, on va essayer de mettre dans le DataSource du repeater une liste de valeur qui n’aura pas encore été initialisée (null). Ce qui a pour effet de nous montrer une page vide.
Vous me direz : On pourrait décaler l’initialisation de la liste dans le OnPreInit de la page :
{
EnableViewState = false;
TestUC.Valeurs = new List<List<string>>(new List<string>[] { new List<string>(new string[] { "abc", "def", "ghi" }), new List<string>(new string[] { "123", "456", "789" }) });
base.OnPreInit(e);
}
et vous aurez parfaitement raison
Ce n’est pas toujours possible, dans la mesure où nous n’avons pas toujours la main sur cet événement, ou que les imbrications d’userControl nous contraignent à faire cette déclaration dans le OnInit, ou tout simplement parce que nous avons besoin d’éléments que nous n’obtiendrons que dans le OnInit de la page… Bref … dans un cas où on a pas accès au OnPreInit.
Vous pourriez également me dire (mais je sais bien que vous n’allez pas me le dire ) : On a qu’à décaler l’affectation de la datasource dans le OnLoad de l’user control. Et je vous répondrais : noooooooon pas bien !
En effet, dans le cycle de vie de la page asp.net, notamment après un postback, nous avons dans l’ordre : OnInit, LoadViewState, LoadPostData et OnLoad. Conclusion, si on initialise la datasource dans le OnLoad (et c’est le cas de notre exemple), nous allons perdre la valeur du postback. Et dans notre exemple, nous allons perdre la valeur selectionnée de la dropdownlist.
Télécharger l’exemple non fonctionnel (18 Ko)
Comment faire alors pour que notre controle utilisateur puisse avoir ses valeurs renseignées alors que nous ne pouvons pas lui affecter correctement ? (hormis de passer par le OnPreInit bien sur)
La solution : passer les valeurs dans le constructeur du controle utilisateur.
Et pour ce faire, nous serons obligé d’ajouter notre controle utilisateur dynamiquement et nous devrons utiliser une méthode pour pouvoir instancier notre controle utilisateur avec un constructeur différent que le constructeur par défaut. L’ajout se fera à un placeholder déposé sur la page à cet effet.
Voici la méthode qui permet d’instancier un controle utilisateur avec des paramètres au constructeur :
{
List<Type> constParamTypes = new List<Type>();
foreach (object constParam in constructorParameters)
constParamTypes.Add(constParam.GetType());
UserControl ctl = (UserControl)page.LoadControl(UserControlPath);
ConstructorInfo constructor = ctl.GetType().BaseType.GetConstructor(constParamTypes.ToArray());
if (constructor == null)
throw new MemberAccessException("Le constructeur n'a pas été trouvé pour : " + ctl.GetType().BaseType);
else
constructor.Invoke(ctl, constructorParameters);
return ctl;
}
Et on va s’en servir dans le OnInit pour instancier notre controle en lui passant les valeurs adéquates.
{
EnableViewState = false;
TestUserControl testUc = (TestUserControl) LoadControl(Page, "~/TestUserControl.ascx", new List<List<string>>(new List<string>[]{new List<string>(new string[] {"abc", "def", "ghi"}),new List<string>(new string[] {"123", "456", "789"})}));
TestPlaceHolder.Controls.Add(testUc);
base.OnInit(e);
}
Dans notre usercontrol, nous allons donc créer un constructeur qui accueille un List<List<string>>
en paramètre (attention, n’oubliez pas de créer également le constructeur par défaut)
{}
public TestUserControl(List<List<string>> Valeurs)
{
_valeurs = Valeurs;
}
Puis on fait l’affectation de la datasource dans le OnInit et le problème est résolu car à ce moment là, vu qu’on a passé les valeurs dans le constructeur, la liste est cohérente dans le OnInit.
Télécharger l’exemple fonctionnel (20 ko)
Voilà, c’est tout
Commentaires récents
- [Tests] Arrange Act Assert, une traduction ? dans
- [UnitTest][C#] Tester une méthode privée dans
- Récupérer une valeur d’un contrôle depuis une autre Form / inclusions croisées et déclaration anticipée dans
- Tutoriel : Utiliser la ListBox et l’Isolated Storage dans vos applications Windows Phone 7 avec Silverlight dans
- Tutoriel : Utiliser la ListBox et l’Isolated Storage dans vos applications Windows Phone 7 avec Silverlight dans
Archives
- janvier 2013
- avril 2012
- janvier 2012
- juin 2011
- janvier 2011
- décembre 2010
- novembre 2010
- septembre 2010
- juin 2010
- mars 2010
- février 2010
- janvier 2010
- décembre 2009
- novembre 2009
- octobre 2009
- septembre 2009
- août 2009
- juillet 2009
- mai 2009
- avril 2009
- mars 2009
- janvier 2009
- décembre 2008
- novembre 2008
- octobre 2008
- septembre 2008
- août 2008
- juillet 2008
- juin 2008
- mai 2008
- avril 2008
- mars 2008
- février 2008
- janvier 2008
- décembre 2007
- novembre 2007
- octobre 2007
- septembre 2007
- août 2007
- juillet 2007
- juin 2007
- mai 2007