La bibliothèque de contrôles Ajax Control Toolkit contient des contrôles étendus. À l'aide de ces contrôles étendus, vous pouvez facilement ajouter des effets Ajax aux contrôles ordinaires. Par exemple, vous pouvez utiliser le contrôle AutoCompleteExtender pour ajouter des effets Ajax de complétion automatique aux zones de texte. Bien entendu, ce n’est pas de cela que cet article souhaite discuter.
Ajoutez Ajax Control Toolkit à la boîte à outils Visual Studio 2008, ouvrez un nouveau fichier aspx et faites-y glisser une TextBox. À ce moment-là, quelque chose d'intéressant s'est produit. Dans le panneau SmartTasks de TextBox, un lien vers "Ajouter une extension..." est en fait apparu ! J'ai essayé de faire glisser à nouveau un bouton et un panneau, et sans exception, le lien "Ajouter une extension..." est apparu en bas du panneau SmartTasks de chaque contrôle.
Récemment, je prévois d'abstraire des fonctions telles que l'enregistrement, la suppression et la fermeture de pages en actions. Chaque action correspond à un contrôle Web personnalisé. Après avoir attaché un contrôle d'action à un contrôle cible (tel qu'un bouton), le contrôle cible aura des fonctions. telles que l'enregistrement, la suppression et la fermeture de pages. Comment attacher facilement des actions à un contrôle Button dans le concepteur WebForm ? Ce que je veux, c'est quelque chose comme "Ajouter une extension...".
Les amis qui ont développé des contrôles serveur personnalisés doivent savoir que si vous souhaitez ajouter des SmartTasks au contrôle, vous devez remplacer la propriété ActionLists de ControlDesigner et implémenter votre propre DesignerActionList. Évidemment, un TextBox ne connaît pas l'existence d'AjaxControlToolkit, donc "Ajouter une extension..." un tel DesignerActionMethodItem n'est pas ajouté par celui-ci. Alors, le framework .net fournit-il une sorte d'interface qui nous permet d'"injecter dynamiquement" DesignerActionItem dans d'autres contrôles ?
Grâce à des recherches sur AjaxControlToolKit.dll, j'ai découvert que le concepteur de ces contrôles étendus n'est pas responsable de fournir l'action "Ajouter une extension". Il est uniquement responsable de fournir le contenu de l'extension correspondant à l'extension correspondante, la seule entrée provient donc de l'extension correspondante. concepteur de formulaires Web de Visual studio. Venez étudier. Utilisez Reflector pour ouvrir Microsoft Visual Studio 9.0Common7IDEMicrosoft.Web.Design.Client.dll et recherchez l'interface IWebSmartTasksProvider. Cette interface possède une méthode GetDesignerActionLists. La valeur de retour de cette méthode doit être le contenu affiché dans le panneau SmartTasks. . Cette interface comporte trois classes d'implémentation, DataFormDesigner, DataFormXslValueOfDesigner et ElementDesigner. On peut déduire de la dénomination de ces trois classes qu'ElementDesigner devrait être la classe d'implémentation la plus couramment utilisée. La méthode GetDesignerActionLists d'ElementDesigner est implémentée comme suit :
1 : DesignerActionListCollection IWebSmartTasksProvider.GetDesignerActionLists()
2 : {
3 : DesignerActionListCollection composantActions = null ;
4 : si (this.Designer != null)
5 : {
6 : Service DesignerActionService = (DesignerActionService) base.DesignerHost.GetService(typeof(DesignerActionService));
7 : si (service != null)
8 : {
9 : composantActions = service.GetComponentActions(this.Designer.Component);
10 : }
11 : }
12 : si (componentActions == null)
13 : {
14 : componentActions = new DesignerActionListCollection();
15 : }
16 : renvoie les actions des composants ;
17 : }
18 :
19 :
20 :
vingt-et-un:
À partir du code ci-dessus, vous pouvez voir que le DesignerActionListCollection final est déterminé par GetComponentActions de la classe System.ComponentModel.Design.DesignerActionService sous l'assembly System.Design et Microsoft.Web.Design.WebFormDesigner sous Microsoft.Web.Design.Client. dll.+WebDesignerActionService hérite de cette classe et son implémentation est la suivante :
1 : remplacement protégé void GetComponentDesignerActions (composant IComponent, DesignerActionListCollection actionLists)
2 : {
3 : Contrôle contrôle = composant comme contrôle ;
4 : parent ElementDesigner = null ;
5 : si (contrôle != null)
6 : {
7 : parent = ElementDesigner.GetElementDesigner(control);
8 : }
9 : si ((parent == null) || !parent.InTemplateMode)
10 : {
11 : base.GetComponentDesignerActions(composant, actionLists);
12 : si ((parent != null) && (parent.Designer != null))
13 : {
14 : concepteur ControlDesigner = parent.Designer comme ControlDesigner ;
15 : si ((designer != null) && (designer.AutoFormats.Count > 0))
16 : {
17 : actionLists.Insert(0, new AutoFormatActionList(parent));
18 : }
19 : }
20 : si ((parent != null) && (parent.Element != null))
vingt-et-un: {
22 : IWebDataFormElementCallback dataFormElementCallback = parent.Element.GetDataFormElementCallback();
23 : si (dataFormElementCallback != null)
vingt-quatre: {
25 : liste DataFormElementActionList = new DataFormElementActionList (parent, parent.Control, dataFormElementCallback);
26 : actionLists.Add(liste);
27 : DataFormElementActionList.ModifyActionListsForListControl(actionLists, liste);
28 : }
29 : }
30 : if (((parent != null) && (parent.Designer != null)) && parent.DocumentDesigner.ExtenderControlHelper.ProvidesActionLists)
31 : {
32 : parent.DocumentDesigner.ExtenderControlHelper.AddActionLists(parent, actionLists);
33 : }
34 : }
35 : si ((parent != null) && (parent.TemplateEditingUI != null))
36 : {
37 : actionLists.Add(new TemplateEditingActionList(parent.TemplateEditingUI, parent.Element));
38 : }
39 : }
40 :
41 :
42 :
43 :
Dans cette méthode, il y a ce paragraphe :
1 : si (((parent != null) && (parent.Designer != null)) && parent.DocumentDesigner.ExtenderControlHelper.ProvidesActionLists)
2 : {
3 : parent.DocumentDesigner.ExtenderControlHelper.AddActionLists(parent, actionLists);
4 : }
Il semble que l'action "Ajouter une extension" soit ajoutée ici. Continuez à examiner l’implémentation de ExtenderControlHelper.AddActionLists :
1 : public void AddActionLists (élément ElementDesigner, listes DesignerActionListCollection)
2 : {
3 : lists.Add(new ControlExtenderActionList(element));
4 : composant ExtenderControl = element.Designer.Component comme ExtenderControl ;
5 : Contrôle contrôle = element.Designer.Component comme contrôle ;
6 : if ((composant == null) && (contrôle != null))
7 : {
8 : service IExtenderInformationService = (IExtenderInformationService) control.Site.GetService(typeof(IExtenderInformationService));
9 : si (service != null)
10 : {
11 : foreach (Contrôle control3 dans service.GetAppliedExtenders(control))
12 : {
13 : lists.Add(new HoistedExtenderActionList(element.Designer, control3));
14 : }
15 : }
16 : }
17 : }
18 :
19 :
20 :
vingt-et-un:
La première phrase de cette méthode est lists.Add(new ControlExtenderActionList(element)). ControlExtenderActionList hérite de System.ComponentModel.Design.DesignerActionList. Sa méthode GetSortedActionItems est définie comme suit :
1 : remplacement public DesignerActionItemCollection GetSortedActionItems()
2 : {
3 : Composant de contrôle = (Contrôle) this._htmlDesigner.Component ;
4 : éléments DesignerActionItemCollection = new DesignerActionItemCollection();
5 : service IExtenderInformationService = (IExtenderInformationService) composant.Site.GetService(typeof(IExtenderInformationService));
6 : catégorie de chaîne = SR.GetString(SR.Ids.SmartTasksLabelExtenderSection, CultureInfo.CurrentUICulture);
7 : si (service.IsControlExtendible(composant))
8 : {
9 : chaîne displayName = SR.GetString(SR.Ids.SmartTasksAddExtender, CultureInfo.CurrentUICulture);
10 : items.Add(new DesignerActionMethodItem(this, "AddExtender", displayName,category, true));
11 : }
12 : si (service.IsControlExtended(composant))
13 : {
14 : chaîne str3 = SR.GetString(SR.Ids.SmartTasksRemoveExtender, CultureInfo.CurrentUICulture);
15 : items.Add(new DesignerActionMethodItem(this, "RemoveExtender", str3,category, true));
16 : }
17 : retourner les articles ;
18 : }
19 :
Il est désormais clair que l'action « Ajouter une extension » est codée en dur dans le concepteur de formulaires Web de Visual Studio. Le framework .net ne fournit pas d'interface correspondante pour nous permettre d'ajouter des actions similaires. Mais l'effet que je souhaite est d'ajouter une action "ajouter une action", je ne peux donc pas me référer à la méthode AjaxControlToolkit pour y parvenir, je devrais chercher d'autres méthodes.
Revenez en arrière et réexaminez la méthode GetComponentActions de la classe Microsoft.Web.Design.WebFormDesigner+WebDesignerActionService et recherchez la définition de la classe de base System.Web.UI.Design.WebFormsDesignerActionService (sous l'assembly System.Design), comme suit :
1 : remplacement protégé void GetComponentDesignerActions (composant IComponent, DesignerActionListCollection actionLists)
2 : {
3 : si (composant == null)
4 : {
5 : lancer un nouveau ArgumentNullException("component");
6 : }
7 : si (actionLists == null)
8 : {
9 : lancer une nouvelle ArgumentNullException("actionLists");
10 : }
11 : site IServiceContainer = composant.Site en tant que IServiceContainer ;
12 : si (site != null)
13 : {
14 : Service DesignerCommandSet = (DesignerCommandSet) site.GetService(typeof(DesignerCommandSet));
15 : si (service != null)
16 : {
17 : Listes DesignerActionListCollection = service.ActionLists ;
18 : if (listes != null)
19 : {
20 : actionLists.AddRange(listes);
vingt-et-un: }
vingt-deux : }
23 : if ((actionLists.Count == 0) || ((actionLists.Count == 1) && (actionLists[0] est ControlDesigner.ControlDesignerActionList)))
vingt-quatre: {
25 : verbes DesignerVerbCollection = service.Verbs ;
26 : if ((verbes != null) && (verbs.Count != 0))
27 : {
28 : tableau DesignerVerb[] = nouveau DesignerVerb[verbs.Count] ;
29 : verbes.CopyTo(array, 0);
30 : actionLists.Add(new DesignerActionVerbList(array));
31 : }
32 : }
33 : }
34 : }
35 :
36 :
37 :
38 :
En étudiant le code ci-dessus, nous pouvons voir que le DesignerActionListCollection est renvoyé par l'attribut ActionLists du service DesignerCommandSet, et ce service est obtenu à partir du site du composant tant que j'écris un autre DesignerCommandSet et que je m'assure que le DesignerCommandSet est retiré du site. Le site est à moi. Écrivez simplement ce service. Enfin trouvé le point d'entrée, voici la méthode spécifique.
Commencez par créer une classe qui hérite de DesignerCommandSet, comme suit :
1 : classe publique MyDesignerCommandSet : DesignerCommandSet
2 : {
3 : privé ComponentDesigner _componentDesigner ;
4 :
5 : public MyDesignerCommandSet (ComponentDesigner composantDesigner)
6 : {
7 : _componentDesigner = composantDesigner ;
8 : }
9 :
10 : remplacement public ICollection GetCommands (nom de chaîne)
11 : {
12 : si (nom.Equals("ActionLists"))
13 : {
14 : retour GetActionLists();
15 : }
16 : retour base.GetCommands(nom);
17 : }
18 :
19 : privé DesignerActionListCollection GetActionLists()
20 : {
21 : //Récupérez d'abord les DesignerActionLists d'origine du contrôle
22 : Listes DesignerActionListCollection = _componentDesigner.ActionLists ;
vingt-trois:
24 : //Ajouter la DesignerActionList "Ajouter une action"
25 : lists.Add(new ActionList(_componentDesigner));
26 : listes de retour ;
27 : }
28 :
29 : classe interne ActionList : DesignerActionList
30 : {
31 : _actions privées de DesignerActionItemCollection ;
32 :
33 : ActionList publique (concepteur IDesigner)
34 : base (concepteur. Composant)
35 : {
36 : }
37 : remplacement public DesignerActionItemCollection GetSortedActionItems()
38 : {
39 : si (_actions == null)
40 : {
41 : const string actionCategory = "Actions" ;
42 : _actions = new DesignerActionItemCollection();
43 : _actions.Add(new DesignerActionMethodItem(this, "AddAction", "Ajouter une action...", actionCategory, true));
44 : }
45 : retour _actions ;
46 : }
47 :
48 : public vide AddAction()
49 : {
50 : //Ajouter une logique d'action, omis
51 : }
52 : }
53 : }
L'étape suivante consiste à faire en sorte que le Site ServiceProvider du composant renvoie son propre service. La méthode consiste à écrire vous-même un site et à faire du site du composant un objet de la classe SIte que vous avez écrite.
La définition de la classe Site que j'ai écrite est la suivante :
1 : classe publique SiteProxy : ISite, IServiceContainer
2 : {
3 : ISite_site privé ;
4 : privé ComponentDesigner _designer ;
5 :
6 : SiteProxy public (site ISite, concepteur ComponentDesigner)
7 : {
8 : _site = site ;
9 : _designer = concepteur ;
10 :
11 : }
12 :
13 : membres de la #région ISite
14 :
15 : composant IComponent public
16 : {
17 : obtenir { return _site.Component } ;
18 : }
19 :
20 : conteneur public System.ComponentModel.IContainer
vingt-et-un: {
22 : obtenir { return _site.Container }
vingt-trois: }
vingt-quatre:
25 : public boolDesignMode
26 : {
27 : obtenir { return _site.DesignMode }
28 : }
29 :
30 : nom de chaîne publique
31 : {
32 : obtenir { return _site.Name }
33 : définir { _site.Name = valeur ;
34 : }
35 :
36 : #endregion
37 :
38 : membre de la #région IServiceProvider
39 :
40 : objet public GetService (Type serviceType)
41 : {
42 : objet service = _site.GetService(serviceType);
43 :
44 : if (serviceType == typeof(DesignerCommandSet) && !(_designer.Component is ExtenderControl))
45 : {
46 : if (service == null || ! (le service est MyDesignerCommandSet))
47 : {
48 : si (service != null)
49 : {
50 : RemoveService(typeof(DesignerCommandSet));
51 : }
52 : //Renvoyer le DesignerCommandSet écrit par vous-même
53 : service = nouveau MyDesignerCommandSet(_designer);
54 : AddService(typeof(DesignerCommandSet), service);
55 : }
56 : }
57 : service retour ;
58 : }
59 :
60 : #endregion
61 :
62 : membre #region IServiceContainer
63 :
64 : public void AddService (Type serviceType, rappel ServiceCreatorCallback, bool Promo)
65 : {
66 : (_site comme IServiceContainer).AddService(serviceType, callback, Promote);
67 : }
68 :
69 : public void AddService (Type serviceType, rappel ServiceCreatorCallback)
70 : {
71 : (_site comme IServiceContainer).AddService(serviceType, callback);
72 : }
73 :
74 : public void AddService (Type serviceType, objet serviceInstance, bool Promo)
75 : {
76 : (_site comme IServiceContainer).AddService(serviceType, serviceInstance, Promote);
77 : }
78 :
79 : public void AddService (Type serviceType, objet serviceInstance)
80 : {
81 : (_site comme IServiceContainer).AddService(serviceType, serviceInstance);
82 : }
83 :
84 : public void RemoveService (Type serviceType, bool Promouvoir)
85 : {
86 : (_site comme IServiceContainer).RemoveService(serviceType, Promouvoir);
87 : }
88 :
89 : public void RemoveService (Type serviceType)
90 : {
91 : (_site comme IServiceContainer).RemoveService(serviceType);
92 : }
93 :
94 : #endregion
95 : }
Dans la méthode GetService de ce Site, déterminez le type de service à obtenir. S'il s'agit d'un DesignerCommandSet, renvoyez le MyDesignerCommandSet que vous avez créé.
L'étape suivante consiste à transformer le site du composant en un SiteProxy écrit par vous-même. Une méthode consiste à ajouter un contrôle personnalisé et à modifier le site des autres contrôles dans le conteneur dans la méthode Initialize du ControlDesigner du contrôle. Il vous suffit de faire glisser le contrôle dans le WebForm pour modifier le site des autres contrôles. La méthode consiste à écrire un package vs et à capturer les événements correspondants du concepteur de formulaire Web dans le package. La première approche est présentée ci-dessous :
Ajoutez un nouveau contrôle hérité de Control, appelé ActionManager. Ce contrôle n'a pas besoin d'ajouter de fonctions. Il vous suffit de créer un ControlDesigner pour celui-ci. Le code principal de sa classe ControlDesigner est le suivant :
1 : classe publique ActionManagerDesigner : ControlDesigner
2 : {
3 : hôte _host IDesignerHost privé ;
4 : composants privés IDictionary<IComponent, ISite> ;
5 :
6 : remplacement public void Initialize (composant IComponent)
7 : {
8 : base.Initialize(composant);
9 :
10 : _components = new Dictionary<IComponent, ISite>();
11 :
12 : _host = GetService(typeof(IDesignerHost)) comme IDesignerHost ;
13 : si (_host != null)
14 : {
15 : //Remplacer le Site par les contrôles existants
16 : ProcessComponent();
17 :
18 : service IComponentChangeService =
19 : _host.GetService(typeof(IComponentChangeService)) en tant que IComponentChangeService ;
20 : si (service != null)
vingt-et-un: {
22 : service.ComponentAdded += ComponentAdded ;
23 : service.ComponentRemoving += ComponentRemoving ;
vingt-quatre: }
25 : }
26 : }
27 :
28 : #regionProcessComponent
29 :
30 : vide privé ProcessComponent()
31 : {
32 : composants ComponentCollection = _host.Container.Components ;
33 : foreach (composant IComponent dans les composants)
34 : {
35 : si (le composant est ActionControl)
36 : continuer ;
37 : ProcessComponentSite (composant) ;
38 : }
39 : }
40 :
41 : #endregion
42 :
43 : #région remplacer le site
44 :
45 : /// <résumé>
46 : /// Remplacer le site d'origine du composant par SiteProxy
47 : /// </summary>
48 : privé void ProcessComponentSite (composant IComponent)
49 : {
50 : Concepteur ComponentDesigner = _host.GetDesigner(component) en tant que ComponentDesigner ;
51 : _components[component] = composant.Site ;
52 : composant.Site = nouveau SiteProxy (composant.Site, concepteur) ;
53 : }
54 :
55 : /// <résumé>
56 : /// Restaurer le site d'origine du Composant
57 : /// </summary>
58 : /// <param name="component"></param>
59 : RestoreComponentSite privé vide (composant IComponent)
60 : {
61 : si (_components.ContainsKey(component))
62 : {
63 : site ISite = _components[composant] ;
64 : composant.Site = site ;
65 : _components.Remove(composant);
66 : }
67 : }
68 :
69 : #endregion
70 :
71 : #region sur le composant Ajouter, supprimer, modifier
72 :
73 : private void ComponentRemoving (expéditeur de l'objet, ComponentEventArgs e)
74 : {
75 : si (e.Component est ActionControl)
76 : {
77 : retour ;
78 : }
79 : //Lors de la suppression d'un Composant, sa propriété Site doit être restaurée, sinon le Site d'origine restera dans le DesignerHost.
80 : //Lors de l'ajout d'un composant portant le même nom de cette manière, une erreur "nom de composant en double" sera signalée.
81 : RestoreComponentSite (e.Component);
82 : }
83 :
84 :
85 : private void ComponentAdded (expéditeur de l'objet, ComponentEventArgs e)
86 : {
87 : si (e.Component est ActionControl)
88 : {
89 : retour ;
90 : }
91 : ProcessComponentSite(e.Component);
92 : }
93 :
94 : #endregion
95 :
96 : #région disposer
97 :
98 : remplacement protégé void Dispose (bool disposition)
99 : {
100 : si (_host != null)
101 : {
102 : service IComponentChangeService =
103 : _host.GetService(typeof(IComponentChangeService)) en tant que IComponentChangeService ;
104 : si (service != null)
105 : {
106 : service.ComponentAdded -= ComponentAdded ;
107 : service.ComponentRemoving -= ComponentRemoving ;
108 : }
109 : }
110 : base.Dispose(élimination);
111 : }
112 :
113 : #endregion
114 : }
À ce stade, tant que vous faites glisser un contrôle ActionManager dans le concepteur de formulaire Web, vous pouvez voir le lien « Ajouter une action… » sur le panneau des tâches intelligentes des autres contrôles. Cependant, cette méthode nécessite de placer un contrôle supplémentaire dans le concepteur de formulaire Web. Ce contrôle n'est utile qu'au moment de la conception et est inutile au moment de l'exécution. Cela semble étrange, la meilleure approche est donc la deuxième approche, qui consiste à développer un package vs. dans la méthode Initialize du package, enregistrez l'événement DesignerCreated de IDesignerEventService, puis atteignez l'objectif de modifier le site de contrôle via IDesignerHost et IComponentChangeService. L'implémentation spécifique est similaire à celle ci-dessus, je ne l'écrirai donc plus.