Die Ajax Control Toolkit-Steuerelementbibliothek enthält einige erweiterte Steuerelemente, mit denen Sie problemlos Ajax-Effekte zu gewöhnlichen Steuerelementen hinzufügen können. Sie können beispielsweise das AutoCompleteExtender-Steuerelement verwenden, um Textfeldern automatische Vervollständigungseffekte hinzuzufügen. Natürlich geht es in diesem Artikel nicht darum, darüber zu sprechen.
Fügen Sie das Ajax Control Toolkit zur Visual Studio 2008-Toolbox hinzu, öffnen Sie eine neue ASPX-Datei und ziehen Sie eine TextBox hinein. Zu diesem Zeitpunkt geschah etwas Interessantes. Im SmartTasks-Bedienfeld von TextBox erschien tatsächlich ein Link zu „Erweiterung hinzufügen …“! Ich habe erneut versucht, eine Schaltfläche und ein Bedienfeld hineinzuziehen, und ausnahmslos wurde der Link „Erweiterung hinzufügen…“ am unteren Rand des SmartTasks-Bedienfelds jedes Steuerelements angezeigt.
Kürzlich plane ich, Funktionen wie das Speichern, Löschen und Schließen von Seiten in Aktionen zu abstrahieren. Jede Aktion entspricht einem benutzerdefinierten Websteuerelement. Nach dem Anhängen eines Aktionssteuerelements an ein Zielsteuerelement (z. B. Button) verfügt das Zielsteuerelement über Funktionen wie das Speichern, Löschen und Schließen von Seiten. Wie kann man im WebForm-Designer einfach Aktionen an ein Button-Steuerelement anhängen? Was ich möchte, ist so etwas wie „Erweiterung hinzufügen…“.
Freunde, die benutzerdefinierte Serversteuerelemente entwickelt haben, sollten wissen, dass Sie, wenn Sie SmartTasks zum Steuerelement hinzufügen möchten, die ActionLists-Eigenschaft von ControlDesigner überschreiben und Ihre eigene DesignerActionList implementieren müssen. Offensichtlich kennt eine TextBox die Existenz von AjaxControlToolkit nicht, daher wird durch „Erweiterung hinzufügen…“ ein solches DesignerActionMethodItem nicht hinzugefügt. Bietet das .net-Framework also eine Art Schnittstelle, die es uns ermöglicht, DesignerActionItem dynamisch in andere Steuerelemente einzufügen?
Durch Recherche zu AjaxControlToolKit.dll habe ich herausgefunden, dass der Designer dieser erweiterten Steuerelemente nicht für die Bereitstellung der Aktion „Erweiterung hinzufügen“ verantwortlich ist. Sie sind nur für die Bereitstellung des Erweiterungsinhalts verantwortlich, der der entsprechenden Erweiterung entspricht, daher stammt der einzige Eintrag von Webform-Designer von Visual Studio. Verwenden Sie den Reflektor, um Microsoft Visual Studio 9.0Common7IDEMicrosoft.Web.Design.Client.dll zu öffnen und die IWebSmartTasksProvider-Schnittstelle zu finden. Der Rückgabewert dieser Methode sollte der im SmartTasks-Bedienfeld angezeigte Inhalt sein . Diese Schnittstelle verfügt über drei Implementierungsklassen: DataFormDesigner, DataFormXslValueOfDesigner und ElementDesigner. Aus der Benennung dieser drei Klassen lässt sich ableiten, dass ElementDesigner die am häufigsten verwendete Implementierungsklasse sein sollte. Die GetDesignerActionLists-Methode von ElementDesigner ist wie folgt implementiert:
1: DesignerActionListCollection IWebSmartTasksProvider.GetDesignerActionLists()
2: {
3: DesignerActionListCollection ComponentActions = null;
4: if (this.Designer != null)
5: {
6: DesignerActionService service = (DesignerActionService) base.DesignerHost.GetService(typeof(DesignerActionService));
7: if (service != null)
8: {
9: ComponentActions = service.GetComponentActions(this.Designer.Component);
10: }
11: }
12: if (componentActions == null)
13: {
14: ComponentActions = new DesignerActionListCollection();
15: }
16: Komponentenaktionen zurückgeben;
17: }
18:
19:
20:
einundzwanzig:
Aus dem obigen Code können Sie ersehen, dass die endgültige DesignerActionListCollection durch GetComponentActions der System.ComponentModel.Design.DesignerActionService-Klasse unter der System.Design-Assembly und Microsoft.Web.Design.WebFormDesigner unter Microsoft.Web.Design.Client bestimmt wird. dll. +WebDesignerActionService erbt diese Klasse und ihre Implementierung ist wie folgt:
1: protected override void GetComponentDesignerActions(IComponent Component, DesignerActionListCollection actionLists)
2: {
3: Kontrollkontrolle = Komponente als Kontrolle;
4: ElementDesigner parent = null;
5: if (control != null)
6: {
7: parent = ElementDesigner.GetElementDesigner(control);
8: }
9: if ((parent == null) || !parent.InTemplateMode)
10: {
11: base.GetComponentDesignerActions(component, actionLists);
12: if ((parent != null) && (parent.Designer != null))
13: {
14: ControlDesigner designer = parent.Designer as ControlDesigner;
15: if ((designer != null) && (designer.AutoFormats.Count > 0))
16: {
17: actionLists.Insert(0, new AutoFormatActionList(parent));
18: }
19: }
20: if ((parent != null) && (parent.Element != null))
einundzwanzig: {
22: IWebDataFormElementCallback dataFormElementCallback = parent.Element.GetDataFormElementCallback();
23: if (dataFormElementCallback != null)
vierundzwanzig: {
25: DataFormElementActionList list = new DataFormElementActionList(parent, parent.Control, dataFormElementCallback);
26: actionLists.Add(list);
27: DataFormElementActionList.ModifyActionListsForListControl(actionLists, list);
28: }
29: }
30: if (((parent != null) && (parent.Designer != null)) && parent.DocumentDesigner.ExtenderControlHelper.ProvidesActionLists)
31: {
32: parent.DocumentDesigner.ExtenderControlHelper.AddActionLists(parent, actionLists);
33: }
34: }
35: if ((parent != null) && (parent.TemplateEditingUI != null))
36: {
37: actionLists.Add(new TemplateEditingActionList(parent.TemplateEditingUI, parent.Element));
38: }
39: }
40:
41:
42:
43:
In dieser Methode gibt es diesen Absatz:
1: if (((parent != null) && (parent.Designer != null)) && parent.DocumentDesigner.ExtenderControlHelper.ProvidesActionLists)
2: {
3: parent.DocumentDesigner.ExtenderControlHelper.AddActionLists(parent, actionLists);
4: }
Es scheint, dass hier die Aktion „Erweiterung hinzufügen“ hinzugefügt wurde. Schauen Sie sich weiterhin die Implementierung von ExtenderControlHelper.AddActionLists an:
1: public void AddActionLists(ElementDesigner-Element, DesignerActionListCollection-Listen)
2: {
3:LISTS.Add(new ControlExtenderActionList(element));
4: ExtenderControl-Komponente = element.Designer.Component as ExtenderControl;
5: Control control = element.Designer.Component as Control;
6: if ((component == null) && (control != null))
7: {
8: IExtenderInformationService service = (IExtenderInformationService) control.Site.GetService(typeof(IExtenderInformationService));
9: if (service != null)
10: {
11: foreach (Steuerung control3 in service.GetAppliedExtenders(control))
12: {
13:LISTS.Add(new HoistedExtenderActionList(element.Designer, control3));
14: }
15: }
16: }
17: }
18:
19:
20:
einundzwanzig:
Der erste Satz in dieser Methode lautet „lists.Add(new ControlExtenderActionList(element)“). Die Methode „ControlExtenderActionList“ ist wie folgt definiert:
1: öffentliche Überschreibung von DesignerActionItemCollection GetSortedActionItems()
2: {
3: Control Component = (Control) this._htmlDesigner.Component;
4: DesignerActionItemCollection items = new DesignerActionItemCollection();
5: IExtenderInformationService service = (IExtenderInformationService) Component.Site.GetService(typeof(IExtenderInformationService));
6: Zeichenfolgenkategorie = SR.GetString(SR.Ids.SmartTasksLabelExtenderSection, CultureInfo.CurrentUICulture);
7: if (service.IsControlExtendible(component))
8: {
9: string displayName = SR.GetString(SR.Ids.SmartTasksAddExtender, CultureInfo.CurrentUICulture);
10: items.Add(new DesignerActionMethodItem(this, "AddExtender", displayName, Category, true));
11: }
12: if (service.IsControlExtended(component))
13: {
14: string str3 = SR.GetString(SR.Ids.SmartTasksRemoveExtender, CultureInfo.CurrentUICulture);
15: items.Add(new DesignerActionMethodItem(this, "RemoveExtender", str3, Category, true));
16: }
17: Artikel zurücksenden;
18: }
19:
Nun ist klar, dass die Aktion „Erweiterung hinzufügen“ im Webformular-Designer von Visual Studio fest codiert ist. Das .net-Framework bietet keine entsprechende Schnittstelle für das Hinzufügen ähnlicher Aktionen. Der gewünschte Effekt besteht jedoch darin, eine Aktion „Aktion hinzufügen“ hinzuzufügen. Daher kann ich nicht auf die AjaxControlToolkit-Methode verweisen, um dies zu erreichen. Ich sollte nach anderen Methoden suchen.
Gehen Sie zurück und untersuchen Sie die GetComponentActions-Methode der Microsoft.Web.Design.WebFormDesigner+WebDesignerActionService-Klasse erneut und suchen Sie die Definition der Basisklasse System.Web.UI.Design.WebFormsDesignerActionService (unter der System.Design-Assembly) wie folgt:
1: protected override void GetComponentDesignerActions(IComponent Component, DesignerActionListCollection actionLists)
2: {
3: if (Komponente == null)
4: {
5: throw new ArgumentNullException("component");
6: }
7: if (actionLists == null)
8: {
9: throw new ArgumentNullException("actionLists");
10: }
11: IServiceContainer site = Component.Site as IServiceContainer;
12: if (site != null)
13: {
14: DesignerCommandSet service = (DesignerCommandSet) site.GetService(typeof(DesignerCommandSet));
15: if (service != null)
16: {
17: DesignerActionListCollection Listen = service.ActionLists;
18: if (lists != null)
19: {
20: actionLists.AddRange(lists);
einundzwanzig: }
zweiundzwanzig: }
23: if ((actionLists.Count == 0) || ((actionLists.Count == 1) && (actionLists[0] ist ControlDesigner.ControlDesignerActionList)))
vierundzwanzig: {
25: DesignerVerbCollection verbs = service.Verbs;
26: if ((verbs != null) && (verbs.Count != 0))
27: {
28: DesignerVerb[] array = new DesignerVerb[verbs.Count];
29: verbs.CopyTo(array, 0);
30: actionLists.Add(new DesignerActionVerbList(array));
31: }
32: }
33: }
34: }
35:
36:
37:
38:
Wenn wir den obigen Code studieren, können wir sehen, dass die DesignerActionListCollection vom ActionLists-Attribut des DesignerCommandSet-Dienstes zurückgegeben wird und dieser Dienst von der Site der Komponente abgerufen wird Die Seite gehört mir. Schreiben Sie einfach diesen Service. Endlich den Einstiegspunkt gefunden, hier ist die spezifische Methode.
Erstellen Sie zunächst wie folgt eine Klasse, die DesignerCommandSet erbt:
1: öffentliche Klasse MyDesignerCommandSet: DesignerCommandSet
2: {
3: privater ComponentDesigner _componentDesigner;
4:
5: öffentliches MyDesignerCommandSet(ComponentDesigner ComponentDesigner)
6: {
7: _componentDesigner = ComponentDesigner;
8: }
9:
10: öffentliche Überschreibung von ICollection GetCommands(String-Name)
11: {
12: if (name.Equals("ActionLists"))
13: {
14: return GetActionLists();
15: }
16: return base.GetCommands(name);
17: }
18:
19: private DesignerActionListCollection GetActionLists()
20: {
21: //Zuerst die ursprünglichen DesignerActionLists des Steuerelements abrufen
22: DesignerActionListCollection Listen = _componentDesigner.ActionLists;
dreiundzwanzig:
24: //Füge die DesignerActionList „Aktion hinzufügen“ hinzu
25:LISTS.Add(new ActionList(_componentDesigner));
26: Rückgabelisten;
27: }
28:
29: interne Klasse ActionList: DesignerActionList
30: {
31: private DesignerActionItemCollection _actions;
32:
33: öffentliche Aktionsliste (IDesigner-Designer)
34: : base(designer.Component)
35: {
36: }
37: öffentliche Überschreibung von DesignerActionItemCollection GetSortedActionItems()
38: {
39: if (_actions == null)
40: {
41: const string actionCategory = "Actions";
42: _actions = new DesignerActionItemCollection();
43: _actions.Add(new DesignerActionMethodItem(this, "AddAction", "Aktion hinzufügen...", actionCategory, true));
44: }
45: return _actions;
46: }
47:
48: public void AddAction()
49: {
50: //Aktionslogik hinzufügen, weggelassen
51: }
52: }
53: }
Der nächste Schritt besteht darin, den Site ServiceProvider der Komponente dazu zu bringen, seinen eigenen Dienst zurückzugeben. Die Methode besteht darin, selbst eine Site zu schreiben und die Site der Komponente zu einem Objekt der von Ihnen geschriebenen SIte-Klasse zu machen.
Die Definition der Site-Klasse, die ich geschrieben habe, lautet wie folgt:
1: öffentliche Klasse SiteProxy: ISite, IServiceContainer
2: {
3: private ISite_site;
4: privater ComponentDesigner _designer;
5:
6: öffentlicher SiteProxy (ISite-Site, ComponentDesigner-Designer)
7: {
8: _site = site;
9: _designer = Designer;
10:
11: }
12:
13: #region ISite-Mitglieder
14:
15: öffentliche IComponentComponent
16: {
17: get { return _site.Component }
18: }
19:
20: öffentlicher System.ComponentModel.IContainer-Container
einundzwanzig: {
22: get { return _site.Container }
dreiundzwanzig: }
vierundzwanzig:
25: öffentlicher boolDesignMode
26: {
27: get { return _site.DesignMode }
28: }
29:
30: öffentlicher String-Name
31: {
32: get { return _site.Name }
33: set { _site.Name = value;
34: }
35:
36: #endregion
37:
38: #region IServiceProvider-Mitglied
39:
40: öffentliches Objekt GetService (Typ serviceType)
41: {
42: Objektservice = _site.GetService(serviceType);
43:
44: if (serviceType == typeof(DesignerCommandSet) && !(_designer.Component is ExtenderControl))
45: {
46: if (service == null || !(service ist MyDesignerCommandSet))
47: {
48: if (service != null)
49: {
50: RemoveService(typeof(DesignerCommandSet));
51: }
52: //Das von Ihnen selbst geschriebene DesignerCommandSet zurückgeben
53: service = new MyDesignerCommandSet(_designer);
54: AddService(typeof(DesignerCommandSet), service);
55: }
56: }
57: Gegenleistung;
58: }
59:
60: #endregion
61:
62: #region IServiceContainer-Mitglied
63:
64: public void AddService(Type serviceType, ServiceCreatorCallback callback, bool promote)
65: {
66: (_site as IServiceContainer).AddService(serviceType, callback, promote);
67: }
68:
69: public void AddService(Type serviceType, ServiceCreatorCallback callback)
70: {
71: (_site as IServiceContainer).AddService(serviceType, callback);
72: }
73:
74: public void AddService(Type serviceType, object serviceInstance, bool promote)
75: {
76: (_site as IServiceContainer).AddService(serviceType, serviceInstance, promote);
77: }
78:
79: public void AddService(Typ serviceType, Objekt serviceInstance)
80: {
81: (_site as IServiceContainer).AddService(serviceType, serviceInstance);
82: }
83:
84: public void RemoveService(Typ serviceType, bool promote)
85: {
86: (_site as IServiceContainer).RemoveService(serviceType, promote);
87: }
88:
89: public void RemoveService(Type serviceType)
90: {
91: (_site as IServiceContainer).RemoveService(serviceType);
92: }
93:
94: #endregion
95: }
Bestimmen Sie in der GetService-Methode dieser Site den abzurufenden Diensttyp. Wenn es sich um ein DesignerCommandSet handelt, geben Sie das von Ihnen erstellte MyDesignerCommandSet zurück.
Der nächste Schritt besteht darin, die Site der Komponente in einen von Ihnen selbst geschriebenen SiteProxy zu ändern. Eine Methode besteht darin, ein benutzerdefiniertes Steuerelement hinzuzufügen und die Site anderer Steuerelemente im Container in der Initialize-Methode des ControlDesigners des Steuerelements zu ändern. Sie müssen das Steuerelement nur in das WebForm ziehen, um die Site anderer Steuerelemente zu ändern Methode besteht darin, ein vs-Paket zu schreiben und die entsprechenden Ereignisse des Webformular-Designers im Paket zu erfassen. Der erste Ansatz wird im Folgenden vorgestellt:
Fügen Sie ein neues von Control geerbtes Steuerelement mit dem Namen ActionManager hinzu. Für dieses Steuerelement müssen keine Funktionen hinzugefügt werden. Der Hauptcode seiner ControlDesigner-Klasse lautet wie folgt:
1: öffentliche Klasse ActionManagerDesigner: ControlDesigner
2: {
3: privater IDesignerHost _host;
4: private IDictionary<IComponent, ISite> _components;
5:
6: öffentliche Überschreibung void Initialize(IComponent-Komponente)
7: {
8: base.Initialize(component);
9:
10: _components = new Dictionary<IComponent, ISite>();
11:
12: _host = GetService(typeof(IDesignerHost)) as IDesignerHost;
13: if (_host != null)
14: {
15: //Ersetzen Sie die Site durch vorhandene Steuerelemente
16: ProcessComponent();
17:
18: IComponentChangeService-Dienst =
19: _host.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
20: if (service != null)
einundzwanzig: {
22: service.ComponentAdded += ComponentAdded;
23: service.ComponentRemoving += ComponentRemoving;
vierundzwanzig: }
25: }
26: }
27:
28: #regionProcessComponent
29:
30: private void ProcessComponent()
31: {
32: ComponentCollection Components = _host.Container.Components;
33: foreach (IComponent-Komponente in Komponenten)
34: {
35: if (Komponente ist ActionControl)
36: weiter;
37: ProcessComponentSite(component);
38: }
39: }
40:
41: #endregion
42:
43: #region Site ersetzen
44:
45: /// <Zusammenfassung>
46: /// Ersetzen Sie die ursprüngliche Site der Komponente durch SiteProxy
47: /// </summary>
48: private void ProcessComponentSite(IComponent-Komponente)
49: {
50: ComponentDesigner designer = _host.GetDesigner(component) as ComponentDesigner;
51: _components[component] = Component.Site;
52: Component.Site = new SiteProxy(component.Site, Designer);
53: }
54:
55: /// <Zusammenfassung>
56: /// Stellen Sie die ursprüngliche Site der Komponente wieder her
57: /// </summary>
58: /// <param name="component"></param>
59: private void RestoreComponentSite(IComponent-Komponente)
60: {
61: if (_components.ContainsKey(component))
62: {
63: ISite site = _components[component];
64: Komponente.Site = Site;
65: _components.Remove(component);
66: }
67: }
68:
69: #endregion
70:
71: #region auf Komponente hinzufügen, entfernen, ändern
72:
73: private void ComponentRemoving(object sender, ComponentEventArgs e)
74: {
75: if (e.Component ist ActionControl)
76: {
77: Rückkehr;
78: }
79: //Beim Löschen einer Komponente muss deren Site-Eigenschaft wiederhergestellt werden, andernfalls verbleibt die ursprüngliche Site im DesignerHost.
80: //Wenn Sie auf diese Weise eine Komponente mit demselben Namen hinzufügen, wird ein Fehler „doppelter Komponentenname“ gemeldet.
81: RestoreComponentSite(e.Component);
82: }
83:
84:
85: private void ComponentAdded(object sender, ComponentEventArgs e)
86: {
87: if (e.Component ist ActionControl)
88: {
89: Rückkehr;
90: }
91: ProcessComponentSite(e.Component);
92: }
93:
94: #endregion
95:
96: #region entsorgen
97:
98: protected override void Dispose(bool disposing)
99: {
100: if (_host != null)
101: {
102: IComponentChangeService-Dienst =
103: _host.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
104: if (service != null)
105: {
106: service.ComponentAdded -= ComponentAdded;
107: service.ComponentRemoving -= ComponentRemoving;
108: }
109: }
110: base.Dispose(entsorgen);
111: }
112:
113: #endregion
114: }
Solange Sie zu diesem Zeitpunkt ein ActionManager-Steuerelement in den Webformular-Designer ziehen, können Sie den Link „Aktion hinzufügen…“ im Smart-Task-Bereich anderer Steuerelemente sehen. Diese Methode erfordert jedoch das Platzieren eines zusätzlichen Steuerelements im Webform-Designer. Dieses Steuerelement ist nur zur Entwurfszeit nützlich und zur Laufzeit nutzlos. Es sieht seltsam aus, daher ist der beste Ansatz der zweite Ansatz, der darin besteht, ein vs.-Paket zu entwickeln. Registrieren Sie in der Initialize-Methode des Pakets das DesignerCreated-Ereignis von IDesignerEventService und erreichen Sie dann den Zweck, die Steuersite über IDesignerHost und IComponentChangeService zu ändern. Die spezifische Implementierung ähnelt der oben genannten, daher werde ich sie nicht mehr schreiben.