A biblioteca de controle do Ajax Control Toolkit contém alguns controles estendidos. Usando esses controles estendidos, você pode adicionar facilmente efeitos Ajax a controles comuns. Por exemplo, você pode usar o controle AutoCompleteExtender para adicionar efeitos Ajax de conclusão automática a caixas de texto. Claro, não é isso que este artigo pretende discutir.
Adicione o Ajax Control Toolkit à caixa de ferramentas do Visual Studio 2008, abra um novo arquivo aspx e arraste um TextBox para ele. Neste momento, algo interessante aconteceu no painel SmartTasks do TextBox, um link para "Adicionar extensão..." realmente apareceu! Tentei arrastar um botão e um painel novamente e, sem exceção, o link "Adicionar extensão..." apareceu na parte inferior do painel SmartTasks de cada controle.
Recentemente, estou planejando abstrair funções como salvar, excluir e fechar páginas em ações. Cada ação corresponde a um controle da Web personalizado. Depois de anexar um controle de ação a um controle de destino (como um botão), o controle de destino terá funções. como salvar, excluir e fechar páginas. Como anexar facilmente ações a um controle Button no designer WebForm? O que eu quero é algo como "Adicionar extensão...".
Amigos que desenvolveram controles de servidor personalizados devem saber que se você quiser adicionar SmartTasks ao controle, precisará substituir a propriedade ActionLists do ControlDesigner e implementar seu próprio DesignerActionList. Obviamente, um TextBox não sabe da existência do AjaxControlToolkit, então "Adicionar extensão..." tal DesignerActionMethodItem não é adicionado por ele. Então, a estrutura .net fornece algum tipo de interface que nos permite "injetar dinamicamente" DesignerActionItem em outros controles?
Através de pesquisas em AjaxControlToolKit.dll, descobri que o Designer desses controles estendidos não é responsável por fornecer a ação "Adicionar extensão". Eles são responsáveis apenas por fornecer o conteúdo da extensão correspondente à extensão correspondente, portanto, a única entrada é do. webform designer do Visual Studio Venha estudar. Use o refletor para abrir o Microsoft Visual Studio 9.0Common7IDEMicrosoft.Web.Design.Client.dll e localize a interface IWebSmartTasksProvider. Essa interface possui um método GetDesignerActionLists. . Essa interface possui três classes de implementação, DataFormDesigner, DataFormXslValueOfDesigner e ElementDesigner. Pode-se inferir a partir da nomenclatura dessas três classes que ElementDesigner deve ser a classe de implementação mais comumente usada. O método GetDesignerActionLists de ElementDesigner é implementado da seguinte forma:
1: DesignerActionListCollection IWebSmartTasksProvider.GetDesignerActionLists()
2: {
3: DesignerActionListCollection componenteActions = null;
4: if (this.Designer! = null)
5: {
6: Serviço DesignerActionService = (DesignerActionService) base.DesignerHost.GetService(typeof(DesignerActionService));
7: if (serviço! = nulo)
8: {
9: componenteActions = service.GetComponentActions(this.Designer.Component);
10:}
11:}
12: if (componentActions == nulo)
13: {
14: componenteActions = new DesignerActionListCollection();
15:}
16: retornar ações de componente;
17:}
18:
19:
20:
vinte e um:
No código acima, você pode ver que o DesignerActionListCollection final é determinado por GetComponentActions da classe System.ComponentModel.Design.DesignerActionService no assembly System.Design e Microsoft.Web.Design.WebFormDesigner em Microsoft.Web.Design.Client. dll. +WebDesignerActionService herda esta classe e sua implementação é a seguinte:
1: substituição protegida void GetComponentDesignerActions (componente IComponent, DesignerActionListCollection actionLists)
2: {
3: Controle controle = componente como Controle;
4: ElementDesigner pai = nulo;
5: if (controle! = nulo)
6: {
7: pai = ElementDesigner.GetElementDesigner(controle);
8:}
9: if ((pai == nulo) || !parent.InTemplateMode)
10: {
11: base.GetComponentDesignerActions(componente, actionLists);
12: if ((pai! = null) && (parent.Designer! = null))
13: {
14: Designer ControlDesigner = parent.Designer como ControlDesigner;
15: if ((designer! = null) && (designer.AutoFormats.Count > 0))
16: {
17: actionLists.Insert(0, novo AutoFormatActionList(pai));
18:}
19:}
20: if ((pai! = null) && (parent.Element! = null))
vinte e um: {
22: IWebDataFormElementCallback dataFormElementCallback = parent.Element.GetDataFormElementCallback();
23: if (dataFormElementCallback! = Nulo)
vinte e quatro: {
25: Lista DataFormElementActionList = novo DataFormElementActionList(pai, pai.Control, dataFormElementCallback);
26: actionLists.Add(lista);
27: DataFormElementActionList.ModifyActionListsForListControl(actionLists, lista);
28:}
29:}
30: if (((pai! = null) && (parent.Designer! = null)) && parent.DocumentDesigner.ExtenderControlHelper.ProvidesActionLists)
31: {
32: pai.DocumentDesigner.ExtenderControlHelper.AddActionLists(pai, actionLists);
33:}
34:}
35: if ((pai! = null) && (parent.TemplateEditingUI! = null))
36: {
37: actionLists.Add(new TemplateEditingActionList(parent.TemplateEditingUI, parent.Element));
38:}
39:}
40:
41:
42:
43:
Neste método, existe este parágrafo:
1: if (((pai! = null) && (parent.Designer! = null)) && parent.DocumentDesigner.ExtenderControlHelper.ProvidesActionLists)
2: {
3: pai.DocumentDesigner.ExtenderControlHelper.AddActionLists(pai, actionLists);
4:}
Parece que a ação "Adicionar extensão" foi adicionada aqui. Continue observando a implementação de ExtenderControlHelper.AddActionLists:
1: public void AddActionLists (elemento ElementDesigner, listas DesignerActionListCollection)
2: {
3: listas.Add(novo ControlExtenderActionList(elemento));
4: Componente ExtenderControl = element.Designer.Component como ExtenderControl;
5: Controle control = element.Designer.Component as Control;
6: if ((componente == nulo) && (controle! = nulo))
7: {
8: serviço IExtenderInformationService = (IExtenderInformationService) control.Site.GetService(typeof(IExtenderInformationService));
9: if (serviço! = nulo)
10: {
11: foreach (Controle control3 em service.GetAppliedExtenders(control))
12: {
13: listas.Add(new HoistedExtenderActionList(element.Designer, control3));
14:}
15:}
16:}
17:}
18:
19:
20:
vinte e um:
A primeira frase neste método é lists.Add(new ControlExtenderActionList(element)).ControlExtenderActionList herda System.ComponentModel.Design.DesignerActionList Seu método GetSortedActionItems é definido da seguinte forma:
1: substituição pública DesignerActionItemCollection GetSortedActionItems()
2: {
3: Componente de controle = (Controle) this._htmlDesigner.Component;
4: Itens DesignerActionItemCollection = new DesignerActionItemCollection();
5: serviço IExtenderInformationService = (IExtenderInformationService) componente.Site.GetService (typeof (IExtenderInformationService));
6: categoria de string = SR.GetString(SR.Ids.SmartTasksLabelExtenderSection, CultureInfo.CurrentUICulture);
7: if (service.IsControlExtendible(componente))
8: {
9: string displayName = SR.GetString(SR.Ids.SmartTasksAddExtender, CultureInfo.CurrentUICulture);
10: items.Add(new DesignerActionMethodItem(this, "AddExtender", displayName, categoria, true));
11:}
12: if (service.IsControlExtended(componente))
13: {
14: string str3 = SR.GetString(SR.Ids.SmartTasksRemoveExtender, CultureInfo.CurrentUICulture);
15: items.Add(new DesignerActionMethodItem(this, "RemoveExtender", str3, categoria, true));
16:}
17: devolução de itens;
18:}
19:
Agora está claro que a ação "Adicionar extensão" está codificada no web form designer do Visual Studio. A estrutura .net não fornece uma interface correspondente para adicionarmos ações semelhantes. Mas o efeito que desejo é adicionar uma ação "adicionar ação", portanto não posso consultar o método AjaxControlToolkit para alcançá-lo, devo procurar outros métodos.
Volte e examine novamente o método GetComponentActions da classe Microsoft.Web.Design.WebFormDesigner+WebDesignerActionService e encontre a definição da classe base System.Web.UI.Design.WebFormsDesignerActionService (no assembly System.Design), como segue:
1: substituição protegida void GetComponentDesignerActions (componente IComponent, DesignerActionListCollection actionLists)
2: {
3: if (componente == nulo)
4: {
5: lançar new ArgumentNullException("componente");
6:}
7: if (actionLists == nulo)
8: {
9: lançar novo ArgumentNullException("actionLists");
10:}
11: site IServiceContainer = componente.Site como IServiceContainer;
12: if (site! = nulo)
13: {
14: Serviço DesignerCommandSet = (DesignerCommandSet) site.GetService(typeof(DesignerCommandSet));
15: if (serviço! = nulo)
16: {
17: listas DesignerActionListCollection = service.ActionLists;
18: if (listas! = Nulo)
19: {
20: actionLists.AddRange(listas);
vinte e um: }
vinte e dois: }
23: if ((actionLists.Count == 0) || ((actionLists.Count == 1) && (actionLists[0] é ControlDesigner.ControlDesignerActionList)))
vinte e quatro: {
25: DesignerVerbCollection verbos = service.Verbs;
26: if ((verbos! = null) && (verbos.Count! = 0))
27: {
28: DesignerVerb[] matriz = novo DesignerVerb[verbs.Count];
29: verbos.CopyTo(matriz, 0);
30: actionLists.Add(novo DesignerActionVerbList(array));
31:}
32:}
33:}
34:}
35:
36:
37:
38:
Ao estudar o código acima, podemos ver que DesignerActionListCollection é retornado pelo atributo ActionLists do serviço DesignerCommandSet, e este serviço é obtido do Site do componente, desde que eu escreva outro DesignerCommandSet e garanta que o DesignerCommandSet seja retirado do. O site é meu. Basta escrever este serviço. Finalmente encontrei o ponto de entrada, aqui está o método específico.
Primeiro, crie uma classe que herda DesignerCommandSet, da seguinte forma:
1: classe pública MyDesignerCommandSet: DesignerCommandSet
2: {
3: ComponentDesigner privado _componentDesigner;
4:
5: MyDesignerCommandSet público (ComponentDesigner componentDesigner)
6: {
7: _componentDesigner = componenteDesigner;
8:}
9:
10: substituição pública ICollection GetCommands (nome da string)
11: {
12: if (nome.Equals("Listas de Ações"))
13: {
14: retornar GetActionLists();
15:}
16: retornar base.GetCommands(nome);
17:}
18:
19: DesignerActionListCollection privado GetActionLists()
20: {
21: //Obtenha primeiro as DesignerActionLists originais do controle
22: Listas DesignerActionListCollection = _componentDesigner.ActionLists;
vinte e três:
24: //Adicionar a DesignerActionList "Adicionar ação"
25: listas.Add(new ActionList(_componentDesigner));
26: listas de devolução;
27:}
28:
29: classe interna ActionList: DesignerActionList
30: {
31: _ações privadas de DesignerActionItemCollection;
32:
33: ActionList pública (designer IDesigner)
34: :base(designer.Component)
35: {
36:}
37: substituição pública DesignerActionItemCollection GetSortedActionItems()
38: {
39: if (_actions == nulo)
40: {
41: const string actionCategory = "Ações";
42: _actions = new DesignerActionItemCollection();
43: _actions.Add(new DesignerActionMethodItem(this, "AddAction", "Adicionar ação...", actionCategory, true));
44:}
45: retornar _ações;
46:}
47:
48: public void AddAction()
49: {
50: //Adiciona lógica de ação, omitida
51:}
52:}
53:}
A próxima etapa é como fazer com que o Site ServiceProvider do componente retorne seu próprio serviço. O método é escrever você mesmo um Site e tornar o Site do Componente um objeto da classe SIte que você escreveu.
A definição da classe Site que escrevi é a seguinte:
1: classe pública SiteProxy: ISite, IServiceContainer
2: {
3: ISite_site privado;
4: ComponentDesigner _designer privado;
5:
6: SiteProxy público (site ISite, designer ComponentDesigner)
7: {
8: _site = site;
9: _designer = designer;
10:
11:}
12:
13: #region membros do ISite
14:
15: IComponentComponent público
16: {
17: obter {retornar _site.Component};
18:}
19:
20: Contêiner System.ComponentModel.IContainer público
vinte e um: {
22: obter {retornar _site.Container};
vinte e três: }
vinte e quatro:
25: bool públicoDesignMode
26: {
27: obter {retornar _site.DesignMode};
28:}
29:
30: nome da string pública
31: {
32: obtenha {retornar _site.Nome};
33: definir {_site.Nome = valor};
34:}
35:
36: #região final
37:
38: #region membro IServiceProvider
39:
40: objeto público GetService (tipo serviceType)
41: {
42: objeto serviço = _site.GetService(serviceType);
43:
44: if (serviceType == typeof(DesignerCommandSet) && !(_designer.Component é ExtenderControl))
45: {
46: if (serviço == nulo ||! (serviço é MyDesignerCommandSet))
47: {
48: if (serviço! = nulo)
49: {
50: RemoveService(typeof(DesignerCommandSet));
51:}
52: //Retorna o DesignerCommandSet escrito por você mesmo
53: serviço = novo MyDesignerCommandSet(_designer);
54: AddService(typeof(DesignerCommandSet), serviço);
55:}
56:}
57: serviço de retorno;
58:}
59:
60: #região final
61:
62: #region membro IServiceContainer
63:
64: public void AddService (tipo serviceType, retorno de chamada ServiceCreatorCallback, bool promote)
65: {
66: (_site como IServiceContainer).AddService(serviceType, retorno de chamada, promoção);
67:}
68:
69: public void AddService (tipo serviceType, retorno de chamada ServiceCreatorCallback)
70: {
71: (_site como IServiceContainer).AddService(serviceType, retorno de chamada);
72:}
73:
74: public void AddService (Tipo serviceType, objeto serviceInstance, bool promote)
75: {
76: (_site como IServiceContainer).AddService(serviceType, serviceInstance, promover);
77:}
78:
79: public void AddService (Tipo serviceType, objeto serviceInstance)
80: {
81: (_site como IServiceContainer).AddService(serviceType, serviceInstance);
82:}
83:
84: public void RemoveService(Tipo serviceType, bool promote)
85: {
86: (_site como IServiceContainer).RemoveService(serviceType, promover);
87:}
88:
89: public void RemoveService(Tipo serviceType)
90: {
91: (_site como IServiceContainer).RemoveService(serviceType);
92:}
93:
94: #região final
95:}
No método GetService deste Site, determine o tipo de serviço a ser obtido. Caso seja um DesignerCommandSet, retorne o MyDesignerCommandSet que você criou.
O próximo passo é como transformar o Site do componente em um SiteProxy escrito por você mesmo. Um método é adicionar um controle customizado e alterar o Site de outros controles no Container no método Initialize do ControlDesigner do controle. Você só precisa arrastar o controle para o WebForm para alterar o Site de outro método. O método é escrever um pacote vs e capturar os eventos correspondentes do designer de formulário da web no pacote. A primeira abordagem é apresentada abaixo:
Adicione um novo controle herdado de Control, chamado ActionManager. Este controle não precisa adicionar nenhuma função. Você só precisa criar um ControlDesigner para ele. O código principal de sua classe ControlDesigner é o seguinte:
1: classe pública ActionManagerDesigner: ControlDesigner
2: {
3: IDesignerHost privado _host;
4: private IDictionary<IComponent, ISite> _componentes;
5:
6: substituição pública void Initialize (componente IComponent)
7: {
8: base.Initialize(componente);
9:
10: _componentes = novo Dicionário<IComponent, ISite>();
11:
12: _host = GetService(typeof(IDesignerHost)) as IDesignerHost;
13: if (_host! = nulo)
14: {
15: //Substituir o Site pelos controles existentes
16: ProcessComponent();
17:
18: serviço IComponentChangeService =
19: _host.GetService(typeof(IComponentChangeService)) como IComponentChangeService;
20: if (serviço! = nulo)
vinte e um: {
22: serviço.ComponentAdded += ComponentAdded;
23: serviço.ComponentRemoving += ComponentRemoving;
vinte e quatro: }
25:}
26:}
27:
28: #regionProcessComponent
29:
30: ProcessComponent() nulo privado()
31: {
32: Componentes ComponentCollection = _host.Container.Components;
33: foreach (componente IComponent em componentes)
34: {
35: if (componente é ActionControl)
36: continuar;
37: ProcessComponentSite(componente);
38:}
39:}
40:
41: #região final
42:
43: #region substitui Site
44:
45: /// <resumo>
46: /// Substitua o Site original do Componente por SiteProxy
47: /// </summary>
48: privado vazio ProcessComponentSite (componente IComponent)
49: {
50: Designer ComponentDesigner = _host.GetDesigner(componente) como ComponentDesigner;
51: _componentes[componente] = componente.Site;
52: componente.Site = novo SiteProxy(component.Site, designer);
53:}
54:
55: /// <resumo>
56: /// Restaura o site original do Componente
57: /// </summary>
58: /// <param name="component"></param>
59: privado void RestoreComponentSite (componente IComponent)
60: {
61: if (_components.ContainsKey(componente))
62: {
63: Site ISite = _componentes[componente];
64: componente.Site = site;
65: _componentes.Remover(componente);
66:}
67:}
68:
69: #região final
70:
71: #region no componente Adicionar, remover, alterar
72:
73: private void ComponentRemoving (objeto remetente, ComponentEventArgs e)
74: {
75: if (e.Component é ActionControl)
76: {
77: retorno;
78:}
79: //Ao excluir um Componente, sua propriedade Site deve ser restaurada, caso contrário o Site original permanecerá no DesignerHost.
80: //Ao adicionar um componente com o mesmo nome desta forma, um erro de "nome de componente duplicado" será relatado.
81: RestoreComponentSite(e.Component);
82:}
83:
84:
85: private void ComponentAdded(objeto remetente, ComponentEventArgs e)
86: {
87: if (e.Component é ActionControl)
88: {
89: retorno;
90:}
91: ProcessComponentSite(e.Component);
92:}
93:
94: #região final
95:
96: #região descartar
97:
98: substituição protegida void Dispose(bool disposing)
99: {
100: if (_host! = nulo)
101: {
102: serviço IComponentChangeService =
103: _host.GetService(typeof(IComponentChangeService)) como IComponentChangeService;
104: if (serviço! = nulo)
105: {
106: serviço.ComponentAdded -= ComponentAdded;
107: serviço.ComponentRemoving -= ComponentRemoving;
108:}
109:}
110: base.Dispose(descartando);
111:}
112:
113: #região final
114:}
Neste ponto, contanto que você arraste um controle ActionManager para o web form designer, você poderá ver o link "Adicionar ação..." no painel de tarefas inteligente de outros controles. No entanto, este método requer a colocação de um controle adicional no webform designer. Esse controle só é útil em tempo de design e é inútil em tempo de execução. Parece estranho, então a melhor abordagem é a segunda abordagem, que é desenvolver um pacote vs. no método Initialize do pacote, registre o evento DesignerCreated de IDesignerEventService e, em seguida, atinja o objetivo de alterar o site de controle por meio de IDesignerHost e IComponentChangeService. A implementação específica é semelhante à acima, então não vou escrever mais.