La biblioteca de controles Ajax Control Toolkit contiene algunos controles extendidos. Con estos controles extendidos, puede agregar fácilmente efectos Ajax a controles normales. Por ejemplo, puede usar el control AutoCompleteExtender para agregar efectos ajax de finalización automática a los cuadros de texto. Por supuesto, esto no es lo que este artículo quiere discutir.
Agregue el kit de herramientas de control Ajax a la caja de herramientas de Visual Studio 2008, abra un nuevo archivo aspx y arrastre un cuadro de texto hacia él. En ese momento, sucedió algo interesante en el panel SmartTasks de TextBox, ¡apareció un enlace para "Agregar extensión ..."! Intenté arrastrar un Botón y un Panel nuevamente y, sin excepción, apareció el enlace "Agregar extensión..." en la parte inferior del panel SmartTasks de cada control.
Recientemente, estoy planeando abstraer funciones como guardar, eliminar y cerrar páginas en acciones. Cada acción corresponde a un control web personalizado. Después de adjuntar un control de acción a un control de destino (como un botón), el control de destino tendrá funciones. como guardar, eliminar y cerrar páginas. ¿Cómo adjuntar fácilmente acciones a un control Botón en el diseñador WebForm? Lo que quiero es algo como "Agregar extensión...".
Los amigos que han desarrollado controles de servidor personalizados deben saber que si desea agregar SmartTasks al control, debe anular la propiedad ActionLists de ControlDesigner e implementar su propia DesignerActionList. Obviamente, un TextBox no conoce la existencia de AjaxControlToolkit, por lo que "Agregar extensión..." no agrega tal DesignerActionMethodItem. Entonces, ¿el marco .net proporciona algún tipo de interfaz que nos permita "inyectar dinámicamente" DesignerActionItem en otros controles?
A través de la investigación sobre AjaxControlToolKit.dll, descubrí que el Diseñador de estos controles extendidos no es responsable de proporcionar la acción "Agregar extensión". Solo es responsable de proporcionar el contenido de la extensión correspondiente, por lo que la única entrada es del. Diseñador de formularios web de Visual Studio. Ven y estudia. Utilice el reflector para abrir Microsoft Visual Studio 9.0Common7IDEMicrosoft.Web.Design.Client.dll y busque la interfaz IWebSmartTasksProvider. Esta interfaz tiene un método GetDesignerActionLists. El valor de retorno de este método debe ser el contenido que se muestra en el panel SmartTasks. . Esta interfaz tiene tres clases de implementación, DataFormDesigner, DataFormXslValueOfDesigner y ElementDesigner. Se puede inferir del nombre de estas tres clases que ElementDesigner debería ser la clase de implementación más utilizada. El método GetDesignerActionLists de ElementDesigner se implementa de la siguiente manera:
1: DesignerActionListCollection IWebSmartTasksProvider.GetDesignerActionLists()
2: {
3: DesignerActionListCollection componentesAcciones = nulo;
4: si (este.Diseñador! = nulo)
5: {
6: Servicio DesignerActionService = (DesignerActionService) base.DesignerHost.GetService(typeof(DesignerActionService));
7: si (servicio! = nulo)
8: {
9: componenteAcciones = servicio.GetComponentActions (este.Designer.Component);
10: }
11: }
12: si (accionescomponentes == nulo)
13: {
14: ComponenteAcciones = nueva DesignerActionListCollection();
15: }
16: devolver acciones de componente;
17: }
18:
19:
20:
veintiuno:
En el código anterior, puede ver que la DesignerActionListCollection final está determinada por GetComponentActions de la clase System.ComponentModel.Design.DesignerActionService en el ensamblado System.Design y Microsoft.Web.Design.WebFormDesigner en Microsoft.Web.Design.Client. dll. +WebDesignerActionService hereda esta clase y su implementación es la siguiente:
1: anulación protegida nula GetComponentDesignerActions (componente IComponent, listas de acciones DesignerActionListCollection)
2: {
3: Control control = componente como Control;
4: ElementDesigner padre = nulo;
5: si (control! = nulo)
6: {
7: padre = ElementDesigner.GetElementDesigner(control);
8: }
9: si ((padre == nulo) || !padre.InTemplateMode)
10: {
11: base.GetComponentDesignerActions (componente, listas de acciones);
12: si ((padre! = nulo) && (padre.Designer! = nulo))
13: {
14: ControlDesigner diseñador = parent.Designer como ControlDesigner;
15: si ((diseñador! = nulo) && (diseñador.AutoFormats.Count > 0))
16: {
17: actionLists.Insert(0, nueva AutoFormatActionList(padre));
18: }
19: }
20: si ((padre! = nulo) && (padre.Elemento! = nulo))
veintiuno: {
22: IWebDataFormElementCallback dataFormElementCallback = parent.Element.GetDataFormElementCallback();
23: si (dataFormElementCallback! = nulo)
veinticuatro: {
25: lista DataFormElementActionList = nueva DataFormElementActionList (padre, parent.Control, dataFormElementCallback);
26: listas de acciones.Agregar (lista);
27: DataFormElementActionList.ModifyActionListsForListControl(actionLists, lista);
28: }
29: }
30: if (((padre! = nulo) && (padre.Designer! = null)) && padre.DocumentDesigner.ExtenderControlHelper.ProvidesActionLists)
31: {
32: padre.DocumentDesigner.ExtenderControlHelper.AddActionLists(padre, actionLists);
33: }
34: }
35: si ((padre! = nulo) && (padre.TemplateEditingUI! = nulo))
36: {
37: actionLists.Add(new TemplateEditingActionList(parent.TemplateEditingUI, parent.Element));
38: }
39: }
40:
41:
42:
43:
En este método, existe este párrafo:
1: if (((padre! = nulo) && (padre.Designer! = null)) && padre.DocumentDesigner.ExtenderControlHelper.ProvidesActionLists)
2: {
3: padre.DocumentDesigner.ExtenderControlHelper.AddActionLists (padre, listas de acciones);
4: }
Parece que aquí se agrega la acción "Agregar extensión". Continúe observando la implementación de ExtenderControlHelper.AddActionLists:
1: AddActionLists públicas vacías (elemento ElementDesigner, listas DesignerActionListCollection)
2: {
3: listas.Add(new ControlExtenderActionList(elemento));
4: Componente ExtenderControl = element.Designer.Component como ExtenderControl;
5: Control de control = elemento.Designer.Component como Control;
6: si ((componente == nulo) && (control!= nulo))
7: {
8: Servicio IExtenderInformationService = (IExtenderInformationService) control.Site.GetService(typeof(IExtenderInformationService));
9: si (servicio! = nulo)
10: {
11: foreach (Control control3 en servicio.GetAppliedExtenders(control))
12: {
13: listas.Add(new HoistedExtenderActionList(element.Designer, control3));
14: }
15: }
16: }
17: }
18:
19:
20:
veintiuno:
La primera oración de este método es listas.Add (nuevo ControlExtenderActionList (elemento)). ControlExtenderActionList hereda System.ComponentModel.Design.DesignerActionList Su método GetSortedActionItems se define de la siguiente manera:
1: anulación pública DesignerActionItemCollection GetSortedActionItems()
2: {
3: Componente de control = (Control) this._htmlDesigner.Component;
4: elementos DesignerActionItemCollection = nuevo DesignerActionItemCollection();
5: Servicio IExtenderInformationService = (IExtenderInformationService) componente.Site.GetService(typeof(IExtenderInformationService));
6: categoría de cadena = SR.GetString(SR.Ids.SmartTasksLabelExtenderSection, CultureInfo.CurrentUICulture);
7: si (servicio.IsControlExtendible(componente))
8: {
9: cadena displayName = SR.GetString(SR.Ids.SmartTasksAddExtender, CultureInfo.CurrentUICulture);
10: items.Add(new DesignerActionMethodItem(this, "AddExtender", displayName, categoría, verdadero));
11: }
12: si (servicio.IsControlExtended (componente))
13: {
14: cadena str3 = SR.GetString(SR.Ids.SmartTasksRemoveExtender, CultureInfo.CurrentUICulture);
15: items.Add(new DesignerActionMethodItem(this, "RemoveExtender", str3, categoría, verdadero));
16: }
17: artículos devueltos;
18: }
19:
Ahora está claro que la acción "Agregar extensión" está codificada en el diseñador de formularios web de Visual Studio. El marco .net no proporciona una interfaz correspondiente para que agreguemos acciones similares. Pero el efecto que quiero es agregar una acción "agregar acción", por lo que no puedo consultar el método AjaxControlToolkit para lograrlo. Debo buscar otros métodos.
Regrese y vuelva a examinar el método GetComponentActions de la clase Microsoft.Web.Design.WebFormDesigner+WebDesignerActionService y busque la definición de la clase base System.Web.UI.Design.WebFormsDesignerActionService (en el ensamblado System.Design), de la siguiente manera:
1: anulación protegida nula GetComponentDesignerActions (componente IComponent, listas de acciones DesignerActionListCollection)
2: {
3: si (componente == nulo)
4: {
5: lanza una nueva ArgumentNullException ("componente");
6: }
7: si (listas de acciones == nulo)
8: {
9: lanza una nueva ArgumentNullException("actionLists");
10: }
11: sitio IServiceContainer = componente.Sitio como IServiceContainer;
12: si (sitio! = nulo)
13: {
14: Servicio DesignerCommandSet = (DesignerCommandSet) sitio.GetService(typeof(DesignerCommandSet));
15: si (servicio! = nulo)
16: {
17: Listas DesignerActionListCollection = service.ActionLists;
18: si (listas! = nulo)
19: {
20: actionLists.AddRange(listas);
veintiuno: }
Veintidós: }
23: if ((actionLists.Count == 0) || ((actionLists.Count == 1) && (actionLists[0] es ControlDesigner.ControlDesignerActionList)))
veinticuatro: {
25: verbos DesignerVerbCollection = servicio.Verbos;
26: if ((verbos!= nulo) && (verbos.Count!= 0))
27: {
28: DesignerVerb[] matriz = nuevo DesignerVerb[verbs.Count];
29: verbos.CopyTo(matriz, 0);
30: actionLists.Add(new DesignerActionVerbList(matriz));
31: }
32: }
33: }
34: }
35:
36:
37:
38:
Al estudiar el código anterior, podemos ver que DesignerActionListCollection es devuelto por el atributo ActionLists del servicio DesignerCommandSet, y este servicio se obtiene del sitio del componente. Siempre que escriba otro DesignerCommandSet y me asegure de que el DesignerCommandSet se extraiga del. El sitio es mío. Solo escribe este servicio. Finalmente encontré el punto de entrada, aquí está el método específico.
Primero, cree una clase que herede DesignerCommandSet, de la siguiente manera:
1: clase pública MyDesignerCommandSet: DesignerCommandSet
2: {
3: ComponentDesigner privado _componentDesigner;
4:
5: público MyDesignerCommandSet (ComponentDesigner componenteDesigner)
6: {
7: _componentDesigner = componenteDesigner;
8: }
9:
10: anulación pública ICollection GetCommands (nombre de cadena)
11: {
12: si (nombre.Equals ("Listas de acciones"))
13: {
14: devuelve GetActionLists();
15: }
16: retorno base.GetCommands(nombre);
17: }
18:
19: Colección DesignerActionList privada GetActionLists()
20: {
21: //Obtenga primero las DesignerActionLists originales del control
22: Listas DesignerActionListCollection = _componentDesigner.ActionLists;
veintitrés:
24: //Agregue la DesignerActionList "Agregar acción"
25: listas.Add(new ActionList(_componentDesigner));
26: listas de devolución;
27: }
28:
29: clase interna ActionList: DesignerActionList
30: {
31: DesignerActionItemCollection privado _acciones;
32:
33: Lista de acciones pública (diseñador IDesigner)
34: : base(diseñador.Componente)
35: {
36: }
37: anulación pública DesignerActionItemCollection GetSortedActionItems()
38: {
39: si (_acciones == nulo)
40: {
41: const string actionCategory = "Acciones";
42: _acciones = nueva DesignerActionItemCollection();
43: _actions.Add(new DesignerActionMethodItem(this, "AddAction", "Agregar acción...", actionCategory, true));
44: }
45: devolver _acciones;
46: }
47:
48: AddAction pública vacía()
49: {
50: //Agregar lógica de acción, omitido
51: }
52: }
53: }
El siguiente paso es cómo hacer que el Site ServiceProvider del componente devuelva su propio servicio. El método consiste en escribir un sitio usted mismo y convertir el sitio del componente en un objeto de la clase SIte que usted escribió.
La definición de la clase de sitio que escribí es la siguiente:
1: SiteProxy de clase pública: ISite, IServiceContainer
2: {
3: ISite_site privado;
4: diseñador de componentes privado _designer;
5:
6: SiteProxy público (sitio ISite, diseñador ComponentDesigner)
7: {
8: _sitio = sitio;
9: _diseñador = diseñador;
10:
11: }
12:
13: #región miembros del sitio ISite
14:
15: Componente IComponente público
16: {
17: obtener {return _site.Component};
18: }
19:
20: Contenedor público System.ComponentModel.IContainer
veintiuno: {
22: obtener {return _site.Container};
veintitrés: }
veinticuatro:
25: modo de diseño de bool público
26: {
27: obtener { return _site.DesignMode }
28: }
29:
30: nombre de cadena pública
31: {
32: obtener {return_site.Name};
33: establecer { _sitio.Nombre = valor }
34: }
35:
36: #endregion
37:
38: #región miembro de IServiceProvider
39:
40: objeto público GetService (Tipo tipo de servicio)
41: {
42: servicio de objeto = _site.GetService(serviceType);
43:
44: si (serviceType == typeof(DesignerCommandSet) &&!(_designer.Component es ExtenderControl))
45: {
46: if (servicio == null || !(el servicio es MyDesignerCommandSet))
47: {
48: si (servicio! = nulo)
49: {
50: EliminarServicio(tipode(DesignerCommandSet));
51: }
52: // Devuelve el DesignerCommandSet escrito por ti mismo
53: servicio = nuevo MyDesignerCommandSet(_designer);
54: AddService (tipo de (DesignerCommandSet), servicio);
55: }
56: }
57: servicio de devolución;
58: }
59:
60: #endregion
61:
62: miembro de #region IServiceContainer
63:
64: AddService público vacío (Tipo tipo de servicio, devolución de llamada ServiceCreatorCallback, promoción bool)
65: {
66: (_sitio como IServiceContainer).AddService(tipo de servicio, devolución de llamada, promoción);
67: }
68:
69: AddService público vacío (Tipo tipo de servicio, devolución de llamada ServiceCreatorCallback)
70: {
71: (_sitio como IServiceContainer).AddService(serviceType, devolución de llamada);
72: }
73:
74: AddService público vacío (tipo tipo de servicio, instancia de servicio de objeto, promoción bool)
75: {
76: (_sitio como IServiceContainer).AddService(serviceType, serviceInstance, promover);
77: }
78:
79: AddService público vacío (Tipo tipo de servicio, instancia de servicio de objeto)
80: {
81: (_sitio como IServiceContainer).AddService(serviceType, serviceInstance);
82: }
83:
84: RemoveService público vacío (Tipo tipo de servicio, promoción bool)
85: {
86: (_sitio como IServiceContainer).RemoveService(tipo de servicio, promover);
87: }
88:
89: RemoveService público vacío (Tipo tipo de servicio)
90: {
91: (_sitio como IServiceContainer).RemoveService(serviceType);
92: }
93:
94: #regiónfinal
95: }
En el método GetService de este Sitio, determine el tipo de servicio que se obtendrá. Si es un DesignerCommandSet, devuelva el MyDesignerCommandSet que creó.
El siguiente paso es cómo cambiar el sitio del componente a un SiteProxy escrito por usted mismo. Un método es agregar un control personalizado y cambiar el Sitio de otros controles en el Contenedor en el método Inicializar del ControlDesigner del control. Solo necesita arrastrar el control al WebForm para cambiar el Sitio de otros controles; El método consiste en escribir un paquete vs y capturar los eventos correspondientes del diseñador de formularios web en el paquete. El primer enfoque se presenta a continuación:
Agregue un nuevo control heredado de Control, llamado ActionManager. Este control no necesita agregar ninguna función. Solo necesita crear un ControlDesigner para él. El código principal de su clase ControlDesigner es el siguiente:
1: clase pública ActionManagerDesigner: ControlDesigner
2: {
3: IDesignerHost privado _host;
4: IDictionary privado <IComponent, ISite> _components;
5:
6: anulación pública de anulación de inicialización (componente IComponent)
7: {
8: base.Inicializar(componente);
9:
10: _components = nuevo Diccionario<IComponent, ISite>();
11:
12: _host = GetService(typeof(IDesignerHost)) como IDesignerHost;
13: si (_host! = nulo)
14: {
15: //Reemplazar el sitio con controles existentes
16: Componente de proceso();
17:
18: Servicio IComponentChangeService =
19: _host.GetService(typeof(IComponentChangeService)) como IComponentChangeService;
20: si (servicio! = nulo)
veintiuno: {
22: servicio.ComponentAdded += ComponentAdded;
23: service.ComponentRemoving += ComponentRemoving;
veinticuatro: }
25: }
26: }
27:
28: #regionProcessComponent
29:
30: componente de proceso vacío privado()
31: {
32: Componentes ComponentCollection = _host.Container.Components;
33: foreach (componente IComponent en componentes)
34: {
35: si (el componente es ActionControl)
36: continuar;
37: ProcessComponentSite(componente);
38: }
39: }
40:
41: #endregión
42:
43: #region reemplaza el sitio
44:
45: /// <resumen>
46: /// Reemplazar el sitio original del componente con SiteProxy
47: /// </summary>
48: ProcessComponentSite vacío privado (componente IComponent)
49: {
50: Diseñador ComponentDesigner = _host.GetDesigner(componente) como ComponentDesigner;
51: _components[componente] = componente.Sitio;
52: componente.Sitio = nuevo SiteProxy(componente.Sitio, diseñador);
53: }
54:
55: /// <resumen>
56: /// Restaurar el sitio original del Componente
57: /// </summary>
58: /// <param nombre="componente"></param>
59: RestoreComponentSite vacío privado (componente IComponent)
60: {
61: si (_components.ContainsKey(componente))
62: {
63: Sitio ISite = _components[componente];
64: componente.Sitio = sitio;
65: _components.Remove(componente);
66: }
67: }
68:
69: #endregion
70:
71: #region en componente Agregar, eliminar, cambiar
72:
73: Eliminación de componentes vacíos privados (remitente del objeto, ComponentEventArgs e)
74: {
75: si (e.Component es ActionControl)
76: {
77: regreso;
78: }
79: //Al eliminar un Componente, se debe restaurar su propiedad del Sitio; de lo contrario, el Sitio original permanecerá en DesignerHost.
80: //Al agregar un componente con el mismo nombre de esta manera, se informará un error de "nombre de componente duplicado".
81: RestoreComponentSite (e.Componente);
82: }
83:
84:
85: ComponentAdded vacío privado (remitente del objeto, ComponentEventArgs e)
86: {
87: si (e.Component es ActionControl)
88: {
89: regreso;
90: }
91: ProcessComponentSite (e.Componente);
92: }
93:
94: #regiónfinal
95:
96: #región eliminar
97:
98: anulación protegida de eliminación nula (eliminación bool)
99: {
100: si (_host! = nulo)
101: {
102: Servicio IComponentChangeService =
103: _host.GetService(typeof(IComponentChangeService)) como IComponentChangeService;
104: si (servicio! = nulo)
105: {
106: servicio.ComponentAdded -= ComponentAdded;
107: service.ComponentRemoving -= ComponentRemoving;
108: }
109: }
110: base.Dispose(eliminación);
111: }
112:
113: #región final
114: }
En este punto, siempre que arrastre un control ActionManager al diseñador de formularios web, podrá ver el enlace "Agregar acción..." en el panel de tareas inteligentes de otros controles. Sin embargo, este método requiere colocar un control adicional en el diseñador de formularios web. Este control solo es útil en tiempo de diseño y es inútil en tiempo de ejecución. Parece extraño, por lo que el mejor enfoque es el segundo enfoque, que es desarrollar un paquete vs. en el método Inicializar del paquete, registre el evento DesignerCreated de IDesignerEventService y luego implemente IDesignerHost e IComponentChangeService para lograr el propósito de cambiar el sitio de control. La implementación específica es similar a la anterior, por lo que no la escribiré más.