Depois de entender a arquitetura de classes exposta pelo Framework de WebParts, chega o momento de vermos na prática essas funcionalidades. Essa seção está dividida em cinco partes: configuração, uso de verbos, manipulação (drag-and-drop), catálogos e edição das WebParts. Na primeira dessas quatro partes, veremos a configuração básica para que as páginas ASPX comportem as WebParts; já quando falamos de manipulação, veremos como habilitar a possibilidade de alterar os locais (zonas) onde as WebParts se encontram. Já em catálogos, veremos como catalogamos as nossas WebParts e, finalmente, a edição das zonas e, conseqüentemente, das WebParts contidas dentro delas.
Como já discutimos anteriormente, o principal controle que gerencia todas as manipulação feitas sob as WebParts e as zonas é o WebPartManager. Esse controle não tem nenhuma aparência visual e deve sempre existir um – e somente um – para cada página ASPX que fizer o uso das WebParts. Além disso, é importante dizer que as WebParts só trabalham com usuários devidamente autenticados, ou seja, usuários anônimos não terão permissão para alterar entre zonas (drag-and-drop), editar as propriedades, aparência e comportamento das WebParts. Entre as principais funcionalidades deste controle podemos destacar algumas delas:
-
Relação das WebParts e suas zonas: internamente o controle WebPartManager gerencia todas as WebParts presentes na página. Essas WebParts estão adicionadas dentro de uma coleção do tipo WebPartCollection e exposta através de uma propriedade denominada WebParts. Além desta coleção, ainda existe uma outra não menos importante chamada Zones. Esta coleção, do tipo WebPartZoneCollection, mantém as zonas que a página contém. Sendo assim, internamente ele conseguirá manter a relação de uma determinada WebPart com a WebZone, sabendo em qual zona a WebPart encontra-se e também qual a posição dela dentro desta zona.
-
Gerencia o estado de visualização das páginas: quando trabalhamos com WebParts é comum ouvirmos falar sobre o estado de visualização da página. Esse estado de visualização é que vai permitir e habilitar certas funcionalidades disponibilizadas por elas. Esses estados são representados por classes do tipo WebPartDisplayMode, onde para cada estado é criado um membro público de somente leitura (read-only), que deverá ser definido na propriedade DisplayMode do WebPartManager para que ele se encarregue de alterar o layout de acordo com a visualização escolhida pelo usuário. Veremos mais abaixo os modos de visualização disponíveis.
-
Armazena as conexões entre as WebParts: este controle também é responsável por estabelecer e monitorar a comunicação entre uma ou mais WebParts existentes em uma página. Veremos mais sobre conexões mais tarde, ainda neste artigo.
-
Personalização e persistência de estado através de um provider: possibilita o usuário mover as WebParts para a zona que desejar; permite ainda alterar a aparência, propriedades e comportamentos de cada uma das WebParts. Com as modificações efetuadas é necessário armazenar isso em algum lugar para mais tarde, quando o usuário voltar, não precisar fazer todas as alterações novamente. Nesta altura é que fazemos o uso da arquitetura de providers para podermos persistir tais alterações em algum repositório. A personalização será abordada em uma futura seção, ainda neste artigo.
-
Encarregado de tratar a autorização para cada WebPart: apesar de possuir um grande número de eventos, o controle WebPartManager fornece um evento importante chamado AuthorizeWebPart. Este evento ocorre quando o método IsAuthorized é chamado para determinar se o usuário tem ou não permissão para visualizar uma determinada WebPart.
Antes de efetivamente vermos o código responsável pela adição e configuração do controle WebPartManager na página ASPX, temos ainda que esclarecer uma questão que ficou pendente: os estados de visualização de uma página que contém WebParts. Quando fazemos o uso de WebParts, podemos ter vários estados de visualização da página corrente, ou seja, para cada um desses estados é necessário informarmos ao WebPartManager qual o estado que queremos e, para isso, utilizamos a propriedade DisplayMode.
Essa propriedade aceita um objeto do tipo WebPartDisplayMode que, por sua vez, definirá o comportamento que a página terá quando esse modo estiver ativo. Atualmente temos 5 modos/estados de visualização, que são explicados através da tabela abaixo:
|
E como esses modos de visualização são representados? Esses objetos são declarados como estáticos e de somente leitura dentro da classe WebPartManager. Como já falamos acima, esses objetos são do tipo WebPartDisplayMode, que encontra-se também declarado dentro do namespace System.Web.UI.WebControls.WebParts. Cada um dos estados tem uma classe que herda da classe base WebPartDisplayMode. Essas classes são declaradas no interior da classe WebPartManager e customiza as propriedades e métodos para os diferentes tipos de visualização. Como essas são privadas, não há informações sobre elas na ajuda do Visual Studio .NET, mas podemos visualizar isso através do Reflector, assim como é mostrado na imagem abaixo:
|
Figura 1 – Classes internas que representam os estados de visualização. |
|
Figura 2 – Membros da classe abstrata WebPartDisplayMode. |
Agora que já conhecemos a arquitetura superficial do WebPartManager vamos começar a trabalhar efetivamente em cima de um projeto para vermos as WebParts em ação. Como já sabemos, o primeiro controle a termos na página é o WebPartManager. Como ele não tem nenhuma aparência em runtime, então poderá colocá-lo em qualquer lugar da sua página. Depois deste controle, é o momento de declararmos as zonas que desejamos ter em nossa página. Também não há a necessidade de amarrar as zonas especificando o ID do controle WebPartManager, já que esse vínculo será feito em runtime. Abaixo é exibido a configuração padrão para uma página que fará o uso das WebParts, tendo 4 zonas posicionadas de duas em duas em cada linha de uma tabela:
<html> <body> <form id="form1" runat="server"> <asp:WebPartManager ID="WebPartManager1" runat="server"> </asp:WebPartManager> <table width="100%" align="center" cellspacing="0"> <tr> <td width="75%" colspan="2" valign="top"> <asp:WebPartZone ID="WebPartZone1" runat="server"> <ZoneTemplate> </ZoneTemplate> </asp:WebPartZone> </td> <td width="25%" valign="top"> <asp:WebPartZone ID="WebPartZone3" runat="server"> <ZoneTemplate> </ZoneTemplate> </asp:WebPartZone> </td> </tr> <tr> <td width="75%" colspan="2" valign="top"> <asp:WebPartZone ID="WebPartZone2" runat="server"> <ZoneTemplate> </ZoneTemplate> </asp:WebPartZone> </td> <td width="25%" valign="top"> <asp:WebPartZone ID="WebPartZone4" runat="server"> <ZoneTemplate> </ZoneTemplate> </asp:WebPartZone> </td> </tr> </table> </form> </body> </html> |
Reparem que cada uma das zonas possui um elemento ZoneTemplate, onde dentro dele será armazenado os controles que desejamos que apareça. É também para dentro desta seção que os controles são colocados quando utilizamos o recurso de drag-and-drop. No nosso caso, essa página deverá “nascer” com quatro User Controls (arquivos ASCX), onde cada uma das seções armazenará um deles.
A declaração dos arquivos ASCX dentro destes templates não muda nada com relação ao que já fazemos atualmente quando queremos colocá-lo no interior de uma página ASPX. O único detalhe aqui é que podemos, na declaração do controle ASCX que está contido dentro da ZoneTemplate, já especificar as propriedades que são expostas pela classe GenericWebPart. Mesmo que o editor de HTML “reclamar”, dizendo que a propriedade não faz parte do controle, quando a aplicação é executada, o erro não acontecerá, pois como vimos anteriormente, os controles colocados dentro deste template são encapsulados por essa classe genérica.
Finalmente temos a página sendo exibida com as WebParts em suas posições iniciais. Lembre-se que neste momento o usuário ainda não está logado na aplicação e, conseqüentemente, não poderá modificar o layout (drag-and-drop). Quando a página é carrega inicialmente ela estará com o seu modo de visualização definido como BrowseDisplayMode e o drag-and-drop não resultará, porém o uso dos verbos (veremos a seguir) poderá ser utilizado sem problemas.
Cada WebPart pode conter diversos verbos. Verbos são ações que o usuário pode executar em cima de uma determinada WebPart e eles podem aparecer como links, botões ou imagens (isso é definido através da propriedade VerbButtonType do controle WebPartZone). Por padrão temos esses verbos em um dropdown menu, que automaticamente aparecem no canto superior direito das WebParts. A imagem abaixo ilustra esses verbos:
|
Figura 3 – Menu com os verbos padrões. |
A configuração destes verbos é definida exclusivamente para cada uma das zonas contidas na página. Essas zonas expõem propriedades do tipo WebPartVerb para cada um dos verbos existentes dentro do controle WebPartZone. Esse controle por sua vez, já possui vários verbos (propriedades), que podemos utilizar dentro das aplicações. Essas propriedades, por convenção, adotam a seguinte nomenclatura: NomeVerboVerb. A listagem abaixo mostra os verbos fornecidos atualmente pela classe/controle WebPartZone:
|
Caso nenhum dos verbos atender a necessidade, ainda há a possibilidade de criar o seu próprio verbo. Se não quiser suporte visual, basta você criar uma instância da classe WebPartVerb ou até mesmo criar uma classe customizada, onde terá essa classe (WebPartZone) como base, configurá-la de acordo com a sua necessidade e, em seguida, adicionar na coleção de verbos da(s) zona(s) ou de uma determinada WebPart. Se desejar ter um suporte visual para alteração das propriedades, então terá que criar uma zona customizada (herdando de WebPartZone) e adicionando uma nova propriedade expondo esse novo verbo (esse cenário é válido quando você quiser que todas as WebParts contidas dentro desta zona tenha esse verbo customizado, caso contrário, deverá adicionar esse verbo customizado na coleção de verbos da sua WebPart). Há duas propriedades interessantes que vale a pena chamar a atenção: ClientClickHandler e ServerClickHandler. A primeira delas nós podemos especificar uma string contendo um nome de método em código cliente (Javascript) que será executado quando o usuário clicar. Já a segunda, especificamos um delegate do tipo WebPartEventHandler, que referenciará um método do lado do servidor a ser executado.
O controle WebPartZone fornece um evento chamado CreateVerbs, que é disparado quando os verbos são criados para uma determinada zona, logo, podemos interceptar a criação a partir deste evento e, neste momento, adicionarmos verbos customizados. O código abaixo mostra um exemplo de como criar um verbo customizado e adicionar a coleção de verbos de uma zona através do evento CreateVerbs:
protected void WebPartZone4_CreateVerbs(object sender, WebPartVerbsEventArgs e) { WebPartVerb c = new WebPartVerb( "Anuncio", new WebPartEventHandler(ProcessoServidor), "return confirm('Deseja mesmo continuar?');"); c.Text = "Comprovar"; c.Description = "Uma descrição qualquer."; List newVerbs = new List(); newVerbs.Add(c); e.Verbs = new WebPartVerbCollection(e.Verbs, newVerbs); } private void ProcessoServidor(object sender, WebPartEventArgs e) { Response.Write(e.WebPart.Title); } |
Como podemos ver no código acima, optamos por criar um objeto do tipo WebPartVerb e customizá-lo. Reparem que o construtor desta classe fornece diversos overloads, onde um deles permite passarmos uma string que representa o código cliente que será executado quando o usuário clicar. Esse valor é guardado dentro da propriedade ClientClickHandler e, como era de se esperar, há também o overload que recebe o delegate que aponta para a função do lado do servidor que será executada se o usuário clicar neste verbo. Logo em seguida, criamos uma nova lista que armazenará os novos verbos e adicionamos o verbo recém criado dentro desta coleção. Finalmente, criamos uma nova instância da classe WebPartVerbCollection, que é responsável por armazenar em seu interior uma coleção de verbos e, em seu construtor, informamos a lista atual de verbos (exposta através da propriedade Verbs do argumento WebPartVerbsEventArgs) e a lista contendo os novos verbos. A imagem abaixo mostra a WebParts de DVDs incluindo este novo verbo:
|
Figura 4 – Novo verbo adicionado. |
Importante: Quando você opta por herdar da classe WebPart, é necessário que você sobrescreva a propriedade Verbs. Essa propriedade é definida através de uma interface chamada IWebActionable, que retorna uma coleção read-only (WebPartVerbCollection) contendo todos os verbos da WebPart em questão.
Quando queremos habilitar ao usuário a possibilidade de alterar o layout, ou seja, permitir o drag-and-drop das WebParts para as zonas que temos na página, além do usuário precisar estar devidamente logado, ainda é necessário que a propriedade DisplayMode, ou melhor, o modo de visualização do controle WebPartManager esteja definido como DesignDisplayMode. Com este estado de visualização definido, agora é permitido efetuar o drag-and-drop das WebParts e assim customizar o layout da página.
De acordo com o layout da nossa aplicação, há um controle DropDownList no canto superior direito da página que contém os possíveis estados da página. Para exemplificar, defini o mesmo como Editar (valor que eu mesmo customizei) e no evento SelectedIndexChanged faço a verificação e, dependendo do valor selecionado, a rotina define o respectivo estado ao controle WebPartManager. Logo abaixo é mostrado o código que faz essa verificação (ele será modificado nas próximas seções) e logo em seguida a página em seu estado de Design, onde posso, via drag-and-drop, modificar as WebParts.
protected void ddlOpcoes_SelectedIndexChanged(object sender, EventArgs e) { string selectedValue = ((DropDownList)sender).SelectedValue; if (!string.IsNullOrEmpty(selectedValue)) { switch (selectedValue) { case "Design": this.WebPartManager1.DisplayMode = WebPartManager.DesignDisplayMode; break; case "Catalog": this.WebPartManager1.DisplayMode = WebPartManager.CatalogDisplayMode; break; case "Edit": this.WebPartManager1.DisplayMode = WebPartManager.EditDisplayMode; break; case "Browse": this.WebPartManager1.DisplayMode = WebPartManager.BrowseDisplayMode; break; case "Connect": this.WebPartManager1.DisplayMode = WebPartManager.ConnectDisplayMode; break; } } } |
Observação: O motivo de estar fazendo o cast é porque o DropDownList está dentro de um controle LoginView.
|
Figura 5 – Drag-and-Drop em ação. |
Como já vimos anteriormente, os catálogos servem para armazenar as WebParts quando elas não estão em uma determinada zona da página. Imagine a seguinte situação: você remove temporariamente uma determinada WebPart da página e, mais tarde, quer trazê-la de volta. Precisamos ter um repostório para que possamos selecionar/escolher as WebParts e, em um clique, podemos adicioná-la(s) em uma zona qualquer da página corrente.
Para isso a Microsoft adicionou três controles/catálogos: DeclarativeCatalogPart, PageCatalogPart e ImportCatalogPart. O primeiro delas, DeclarativeCatalogPart, é responsável por armazenar WebParts que inicialmente não aparecem na página, ou seja, ficam ali de stand-by para, a qualquer momento, adicioná-las na página e sua configuração ainda é realizada em design-time. Já o segundo, PageCatalogPart, irá armazenar as WebParts que são removidas da página pelo usuário final. O última deles veremos mais detalhadamente na seção de Exportação e Importação de WebParts. Abaixo veremos a configuração de uma zona de catálogo (que é a zona que armazena os catálogos descritos anteriormente):
<asp:CatalogZone ID="CatalogZone1" runat="server"> <ZoneTemplate> <asp:PageCatalogPart ID="PageCatalogPart1" runat="server" /> <asp:DeclarativeCatalogPart ID="DeclarativeCatalogPart1" runat="server"> <WebPartsTemplate> <asp:Calendar ID="Calendar1" runat="server" Title="Calendário" /> </WebPartsTemplate> </asp:DeclarativeCatalogPart> </ZoneTemplate> <!-- Propriedades ocultadas para poupar espaço --> </asp:CatalogZone> |
É importante reparar que, nesta zona, a seção ZoneTemplate serve para adicionar os catálogos que desejamos ter na página. Para o exemplo decidi utilizar dois dos três catálogos mencionados acima, e aproveito para apresentar a seção WebPartsTemplate do catálogo DeclarativeCatalogPart que recebe as WebParts que inicialmente não estarão visíveis na página, ou seja, poderão ser adicionadas pelo usuário final. É importante dizer que, para que os catálogos estejam disponíveis, é necessário definirmos a propriedade DisplayMode do controle WebPartManager como CatalogDisplayMode (consultar o código do evento SelectedIndexChanged do DropDownList um pouco mais acima).
Através do comparativo que a imagem abaixo mostra, vemos que a zona que contém os catálogos cria links para os catálogos que temos em seu interior, ou seja, como temos o DeclarativeCatalogPart e PageCatalogPart há dois links em que, quando clicados, listam as respectivas WebParts. No rodapé da zona de catálogos temos um controle do tipo DropDownList que contém todas as zonas da página corrente em que podemos adicionar as WebParts que estão nestes catálogos. Depois de selecionada a zona e a(s) WebPart(s) (através do CheckBox) que desejamos adicionar, basta clicar no botão Adicionar.
|
Figura 6 – Catálogos de WebParts. |
Como já mencionamos anteriormente, as WebParts ainda contém alguns controles que permitem a sua edição de aparência, comportamento e layout. Para que isso seja possível, é disponibilizado um controle chamado de EditorZone onde, em seu interior (dentro da seção ZoneTemplate), são colocados os seguintes controles de edição: AppearanceEditorPart, BehaviorEditorPart, LayoutEditorPart e o PropertyGridEditorPart. É Importante lembrar que os controles responsáveis pela edição das WebParts somente estarão disponíveis se o estado da propriedade DisplayMode do controle WebPartManager da página estiver definido como EditDisplayMode (para ver como definir isso, consulte o evento SelectedIndexChanged do DropDownList). Quando este estado é definido, um verbo chamado Edit é adicionado às WebParts para que, quando o usuário clicar, a mesma entre em modo de edição.
O primeiro deles, AppearanceEditorPart, fornece propriedades para alterarmos a aparência de cada WebPart, como por exemplo, alterar o título apresentado pela WebPart. A imagem abaixo ilustra o uso dele:
|
Figura 7 – O editor AppearanceEditorPart sendo utilizado. |
O segundo editor, BehaviorEditorPart, permite-nos configurar a utilização dos verbos expostos pelas WebParts, como por exemplo, AllowClose, AllowEdit, etc. Além disso, ainda é possível alterar a propriedade AuthorizationFilter, porém você deve analisar até que ponto isso é viável. A imagem abaixo ilustra o controle em funcionamento:
|
Figura 8 – O editor BehaviorEditorPart sendo utilizado. |
Em terceiro lugar, o editor LayoutEditorPart permite a configuração do estado e da localização da WebPart. Por estado, significa que se ele deve ser minimizado, fechado, etc. Já a localização diz respeito a zona e a posição dentro dela que a WebPart terá. Esse editor permitirá a alteração das seguintes propriedades das WebParts: ChromeState, Zone e ZoneIndex. A imagem abaixo ilustra o controle em funcionamento:
|
Figura 9 – O editor LayoutEditorPart sendo utilizado. |
Finalmente o quarto e último editor, o PropertyGridEditorPart, que permite a configuração de propriedades customizadas das WebParts, contrariando os outros editores, que editam apenas as propriedades relacionadas com a UI (User Interface). Este controle irá exibir todas as propriedades da WebPart denotadas com o atributo WebBrowsable. Para cada tipo dessas propriedades, um controle ASP.NET específico é criado para satisfazer as alterações. Na listagem abaixo é mostrado a relação entre tipo e controle:
|
Para exemplificarmos o uso deste editor, vamos adicionar uma propriedade no UserControl (ASCX) que recebe/retorna uma string. Como já vimos, o atributo WebBrowsable é obrigatório. Além dele, ainda há alguns outros atributos que você pode utilizar para customizar ainda mais, como é o caso dos atributos WebDisplayName, WebDescription e Personalizable.
O atributo WebDisplayName definirá o nome (label) da propriedade que será exibido que, por padrão, é o próprio nome da propriedade. Já o WebDescription serve para definirmos uma pequena mensagem (tooltip) quando o usuário posiciona o mouse em cima do controle responsável por alterar a propriedade. Finalmente o atributo Personalizable, que garantirá que o valor atribuído a propriedade será persistido no repositório de dados especificado através do provider. É importante dizer que somente a propriedade de escrita e leitura são permitidas, e qualquer outro tipo de propriedade, incluindo as parametrizadas, resultarão em uma excessão do tipo HttpException. A imagem abaixo ilustra o controle em funcionamento:
|
Figura 10 – O editor PropertyGridEditorPart sendo utilizado. |
Depois de visualizado o resultado, é necessário vermos como ficou a implementação da propriedade dentro do UserControl que é editado pelo controle PropertyGridEditorPart:
[ WebBrowsable, WebDisplayName("Cor da Barra de Navegação"), WebDescription("Cor que aparecerá como Background da Barra de Navgegação"), Personalizable ] public string CorBarraNavegacao { get { return this.BarraNavegacao.BgColor; } set { this.BarraNavegacao.BgColor = value; } } |
Olá Israel!
Parabéns pelo post!
Precisava de um help. Tenho na página, 9 Web Zones com diferentes gadgets… o que acontece é que precisava que as opções de cada Verb de cada Web Zone, correspondesse ao gadget. Por exemplo, tenho um WZ com um gadget calendário, e precisava que as opções fossem referentes ao calendário, porém quando movesse para um outro web zone, permanecesse o verb do calendário…Não sei se deu para entender..rs..mais resumindo, preciso sempre ter o menu do gadget acompanhando ele por cada Web Zone, independente se ele entrar no Web Zone de outro gadget…
Tem alguma maneira de criar um conjunto de verbs para cada Web Part ou só da para criar os Verbs para uma zona?
Boas André,
Faz tempo que não trabalho com as WebParts, mas pelo que lembro, eu acho que os verbos estão mesmos ligados à zona e não a WebPart.
Quando você opta por herdar da classe WebPart, é necessário que você sobrescreva a propriedade Verbs. Essa propriedade é definida através de uma interface chamada IWebActionable, que retorna uma coleção read-only (WebPartVerbCollection) contendo todos os verbos da WebPart em questão.