Skip Navigation Links



Translate this page now :



»Programação
»Programação.NET
»Banco de Dados
»Webdesign
»Office
» Certificações Microsoft 4
»Treinamentos4
»Programação 4
»Webdesign«
»Office & User Tips«
»Grupos de UsuĆ”rios
»CĆ©lulas AcadĆŖmicas«
intcontpiada : 118
Hino do Internalta
Você já está cadastrado e participa do grupo de usuários de sua cidade ? Se não, comente o porque.
 
 
FaƧa um pequeno teste com 10 questƵes de VB
.:.
Teste seus conhecimentos em Visual Basic, SQL Server e ASP 3.0 com nossas provas on-line
.:.
Aprimore seus conhecimentos em programaĆ§Ć£o com nosso treinamento on-line de lĆ³gica de programaĆ§Ć£o
.:.
Veja nosso calendƔrio de treinamentos
Gostou da PƔgina?
Então

para um amigo!

Pesquisa personalizada
Pesquisar Dicas:

 







Transformando um WebUserControl em um CustomControl
(ou : Como usar composição na criação de CustomWebControls)

Antes de vermos os detalhes de como realizar este truque, que sem dúvida é muito interessante, vamos acertar algumas definições e ver o que ganharemos com isso.

WebUserControl : Criando dentro de uma aplicação web com um arquivo .ASCX e um codebehind.

CustomWebControl : Criando em uma WebControlLibrary e compilado para um assembly .DLL

O que ganharemos transformando um webUserControl em um CustomWebControl :

  • Poderemos inserir o controle na Toolbox
  • Podemos reaproveita-lo em outros projetos com facilidade, já que ele será um assembly .dll
  • Ele terá interface visual em design time
  • A instância no code-behind será inserida e retirada automaticamente
  • Com um pouco mais de codificação teremos as propriedades disponíveis na janela de propriedades, em design time

Todos os recursos acima não podem ser obtidos com webUserControls mas sim com CustomWebControls.

Por que converter e não criar um CustomWebControl diretamente ?

Os WebUserControls fornecem uma interface visual para a realização de composição, ao contrário dos CustomWebControls que são criados diretamente em código. Assim sendo fica fácil criar webUserControls que sejam formados por diversos outros, como textbox, combos e até mesmo outros webUserControls.

Assim sendo, convertendo um webUserControl para um CustomWebControl podemos desenvolver um controle composto com as facilidades de um WebUserControl e os recursos de um CustomWebControl

Perdemos algo ?

Perdemos simplicidade na manutenção, a cada vez que for necessário dar manutenção no componente o processo de conversão precisará ser refeito.

Quando aplicar ?

Quando o componente já não estiver sofrendo manutenções, estiver dentro da aplicação na forma de um componente reutilizável e até mesmo já houver o desejo de utiliza-lo de forma simples em outras aplicações.

Agora que acertamos esses detalhes iniciais, vamos ver como isso pode ser feito.

O conceito básico desta conversão está ligado ao processo de compilação utilizado pelo .NET. Inicialmente poderiamos imaginar a conversão como algo muito dificil ou impossível, já que os webUserControls possuem um arquivo de Tags enquanto que os customWebControls são construidos via código da linguagem. Então como converter um arquivo de tags em código ?

O segredo está no processo de compilação : Talvez vocês já saibam que o arquivo de tags é compilado sob demanda, ou seja, é compilado no momento em que é chamado pela primeira vez por um client. O que talvez não saibam é que essa compilação tem 2 passos : Primeiro o ASP.NET transforma as tags em um arquivo de código. Isso mesmo, código puro, na linguagem em que a aplicação foi desenvolvida, no nosso caso, VB.NET. Sem tags, só código. Depois é feita a compilação deste código e gerado um arquivo .dll, o assembly que contem o arquivo de tags - na verdade convertido para código.

Para Saber Mais : Para rever detalhes sobre o processo de compilação do ASP.NET veja o artigo em http://www.bufaloinfo.com.br/artigos/coluna19.asp

Assim sendo, as tags passam por um processo de conversão para código para serem executadas. O que faremos é nos aproveitar deste processo, pegar o assembly (.dll) gerado pelo ASP.NET após este processo e utiliza-lo para gerar nosso CustomWebControl.

Vamos fazer isso passo a passo.

Passo 1 : Gerar um webUserControl para ser transformado

  • Crie uma nova aplicação web
  • Insira um WebUserControl (vou chama-lo de wbCalendario.ascx)
  • Insira um calendário e uma textbox no webUserControl
  • Programe o calendário para preencher a textbox quando o usuário clicar no calendário.
  • Vamos expor no webUserControl uma propriedade e um evento do calendário. Com isso veremos a forma de trabalho com composição e como estes itens serão expostos no CustomWebcontrol

 

   50     Public Event SelectionChanged As EventHandler

   51 

   52     Private Sub Calendar1_SelectionChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Calendar1.SelectionChanged

   53 

   54 

   55         TextBox1.Text = Calendar1.SelectedDate.ToLongDateString

   56 

   57         RaiseEvent SelectionChanged(sender, e)

   58     End Sub

   59 

   60 

   61     Public Property SelectedDate() As Date

   62         Get

   63             Return (Calendar1.SelectedDate)

   64         End Get

   65         Set(ByVal Value As Date)

   66             Calendar1.SelectedDate = Value

   67         End Set

   68     End Property

Passo 2 : Compilar o WebUserControl e a aplicação

  • Elimine o global.asax - ele prejudica o código gerado, criando uma dependência desnecessária
  • Insira o wbCalendario.ascx dentro do formulário
  • Rode a aplicação web - CUIDADO - você faz isso depois de ter eliminado o global.asax, não antes. Não tenho certeza do resultado caso rode antes de eliminar o global.asax
  • Pare a aplicação
  • Elimine o webForm e o webconfig
  • Faça um Rebuild no projeto

 

Passo 3 : Localize o resultado da compilação

O que faremos : Ao rodar a aplicação o .ASCX foi compilado sob demanda, então é necessário localizar o resultado desta compilação sob demanda. Isso fica no diretório Temporary ASP.NET Files, localizado em Windows\Microsoft.NET\(versão)

  • Localize o diretório com o nome de sua aplicação web
    • Abra os subdiretórios, dois níveis até encontrar os arquivos de código fonte. Estarão logo acima de um subdiretório chamado "Assembly"
  • Localize o arquivo que contém o código de seu webUserControl
    • Você deverá ver 2 arquivos .vb com nomes codificados, um contendo seu webUserControl e o outro contendo seu WebForm. Descubra qual é qual abrindo os arquivos e observando o nome das classes que eles contém
      Observe que no mesmo diretório você encontra o assembly (.dll) e o .pdb referentes a compilação deste .vb

Para saber mais : Neste ponto temos uma bifurcação : Se você desejar utilizar diretamente o assembly, o arquivo .dll, você pode, inclusive transformando-o em custom web control. Porém da forma como está ele não irá gerar a sua aparencia em design time, exatamente como os WebUserControls, que não geram sua apresentação em design. Precisamos fazer uma pequena alteração no código para que isso aconteça.

Passo 4 : Entendendo e alterando o código do webUserControl

  • Crie um nova ClassLibrary. Vou chama-la simplesmente de Lib
  • Copie e cole o código do .VB que você encontrou no temporary asp.net files para dentro do class1.vb, substituindo a class1 vazia que está ali.
  • Faça os references necessários
    • Observe os erros nos imports. Isso indica os references que são necessários nesta class library, faça-os.
    • Vasculhando o código você encontrará mais um references necessário : System.Drawing
  • Copie os Assemblies necessários
    • Observe a herança na classe : Esta classe herda do codeBehind do WebUserControl. Pegue a dll do projeto web que criamos - e que tomamos cuidado de compilar sem o webform e web.config, para que estivesse o mais limpa possível - copie para o diretório bin da Lib e faça references
  • Adicione o seguinte código


  •    50     Dim binitialized As Boolean

       51 

       52 

       53     Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)

       54 

       55 

       56         EnsureChildControls()

       57         MyBase.Render(writer)

       58     End Sub

       59 

       60     Protected Overrides Sub EnsureChildControls()

       61         If Not binitialized Then

       62             FrameworkInitialize()

       63         End If

       64         MyBase.EnsureChildControls()

       65     End Sub

       66 

       67     Public Overrides ReadOnly Property Controls() As System.Web.UI.ControlCollection

       68 

       69 

       70         Get

       71             EnsureChildControls()

       72             Return (MyBase.Controls)

       73         End Get

       74     End Property



  • Compile essa classLibrary (Lib)

 

Para saber mais : Vamos entender melhor o código deste webUserControl

A tag #ExternalSource funciona como um comentário, não se preocupe com ela. Essa tag é usada pelo gerador tags/código do ASP.NET para indicar que tags geraram que trechos de código, mas durante uma compilação pelos compiladores da linguagem essa tag é ignorada. Você pode utiliza-la para estudar em mais detalhes o código gerado.

   50 #ExternalSource("http://localhost/WebApplication12/TesteControl.ascx",5)

   51 

   52     Private __control3 As System.Web.UI.WebControls.TableItemStyle

   53 

   54 #End ExternalSource

No código da classe você encontrará os seguintes elementos :

  • Definição de variáveis que conterão os webControls definidos originalmente em tags
  • Algumas definições e overrides de propriedades características de WebUserControls
  • Diversas Subs, uma para cada controle do webUserControl. Elas são utilizadas para inicializar cada controle do webUserControl
  • Uma Sub BuildControlTree que chama as subs de inicialização dos controles e os adiciona no webUserControl utilizando o AddParsedSubObject ao invés de utilizar a coleção controls
  • Uma sub FrameworkInitialize que chama a sub BuildControlTree

Com base nisso devemos fazer as seguintes constatações :

O código gerado para um WebUserControl não utiliza o Render, como é típico dos CustomWebControls. Assim sendo não usa a mesma sequencia de renderização de um CustomWebControl. Não que a sequencia não exista, ela existe e tem que existir, todo controle web a tem, mas os WebUserControls usam a sequencia de forma diferente, criando seu conteúdo em um momento diferente.

Observe que FrameworkInitialize é um overrides. Essa sub não só é conhecida pela classe base (UserControl - classe base do codeBehind), como é disparada por ela em algum dos eventos da sequencia de renderização de um componente web - esta é a constatação óbvia, já que FrameworkInitialize não faz parte da sequencia de renderização padrão de um componente web.

O resultado, descoberto por teste e erro, é que se deixarmos o código desta forma teremos o nosso novo componente se renderizando corretamente em run-time, mas não em design time. A sequencia de eventos não é organizada para gerar a renderização em design time e é isso que precisaremos alterar.

Normalmente, em um CustomWebControl, basta fazer overrides da coleção Controls e da sub Render e chamar o método EnsureChildControls para garantir que os componentes Child - contidos no Custom Control - serão renderizados. Porém, como você pode observar no código do WebUserControl, os controles não são Child, não estão contidos na coleção Controls, são sim vistos como ParsedSubObjects.

Então teremos que fazer com que essas duas rotinas (Render, Controls) chamem o FrameworkInitialize, mas apenas se ele não tiver sido utilizado antes, caso contrário teremos erro.

A forma mais elegante que encontrei para isso foi fazer um overrides no método EnsureChildControls, passando a controlar também a chamada do FrameworkInitialize e depois chamar a classe base. Com isso o Render e Controls ficam com a implementação padrão, chamando o ensurechildControls.


   50     Private Function __BuildControlTextBox1() As System.Web.UI.Control

   51         Dim __ctrl As System.Web.UI.WebControls.TextBox

   52 

   53             #ExternalSource("http://localhost/WebApplication12/TesteControl.ascx",3)

   54         __ctrl = New System.Web.UI.WebControls.TextBox

   55 

   56             #End ExternalSource

   57         Me.TextBox1 = __ctrl

   58 

   59             #ExternalSource("http://localhost/WebApplication12/TesteControl.ascx",3)

   60         __ctrl.ID = "TextBox1"

   61 

   62             #End ExternalSource

   63         Return __ctrl

   64     End Function

   65 

   66     Protected Overrides Sub FrameworkInitialize()

   67         binitialized = True

   68         Me.__BuildControlTree(Me)

   69     End Sub

Passo 5 : Criando o CustomWebControl

Por que ainda precisamos de um CustomWebControl, não podemos usar diretamente a LIB ?

A herança do controle contido em LIB vem de UserControl, o que não permite que seja inserido na toolbox. Criando um CustomWebControl resolvemos esse problema

E se ?

E se alterassemos a herança no codeBehind para passar a ser de webControl ao invés de userControl, isso não resolveria o problema ?

Provavelmente, mas também é muito provável que surja a necessidade de alterar uma quantidade maior de código. Seria um outro caminho para chegar ao mesmo lugar e uma experiência muito interessante a ser tentada.

  • Crie um novo projeto Web - utilizaremos para testar nosso controle
  • Adicione uma nova WebControlLibrary na solução
  • Limpe a classe de customControl criada (retire a propriedade Text, o Render e o atributo Default)
  • Altere o nome do CustomWebControl adequadamente - altere o nome da classe e altere no atributo ToolboxData
  • Copie o Assembly LIB e o assembly de nossa aplicação web anterior (onde criamos o WebUserControl) para o diretório BIN desta nova WebControlLibrary
  • Adicione references para os dois assemblies copiados
  • Definir o CustomControl como sendo uma tag SPAN

    Por que ?

    • Sendo definido como uma tag Span, essa tag irá conter todo o conteúdo renderizado pelo webUserControl. É isso que possibilitará um fácil posicionamento de todo o conteúdo, já que tudo obedecerá ao posicionamento da tag SPAN

       50     Protected Overrides ReadOnly Property TagKey() As System.Web.UI.HtmlTextWriterTag

       51 

       52 

       53         Get

       54             ensurechildcontrols()

       55             Return (System.Web.UI.HtmlTextWriterTag.Span)

       56 

       57 

       58         End Get

       59     End Property

       60 

       61     Protected Overrides ReadOnly Property TagName() As String

       62 

       63         Get

       64 

       65             ensurechildcontrols()

       66             Return ("span")

       67         End Get

       68     End Property



  • Vamos fazer overrides no CreateChildControls para podermos instanciar nosso WebUserControl, veja :

       50     Private uc As [Lib].ASP.TesteControl_ascx

       51 

       52     Protected Overrides Sub createchildcontrols()

       53         uc = New [Lib].ASP.TesteControl_ascx

       54         Me.Controls.Add(uc)

       55         MyBase.CreateChildControls()

       56     End Sub



  • Vamos fazer overrides da coleção Controls e do método OnInit para chamarmos o EnsureChildControls.
    • O normal seria fazermos overrides da Controls e do método OnRender, Mas ao invés disso faremos com o OnInit. Isso porque precisamos que nossos componentes mais internos (textbox, calendar) reconheçam o postBack, o que não acontecerá se fizermos o overrides do OnRender. Isso porque o Render é o último passo no processamento de WebControls e para que os webControls reconheçam suas sequencias de eventos corretamente eles precisam existir desde o 1o passo - onInit - e não apenas no último.


    •    50     Public Overrides ReadOnly Property Controls() As System.Web.UI.ControlCollection

         51 

         52 

         53         Get

         54             ensurechildcontrols()

         55             Return (MyBase.Controls)

         56         End Get

         57     End Property

         58 

         59     Protected Overrides Sub OnInit(ByVal e As System.EventArgs)

         60         MyBase.OnInit(e)

         61         ensurechildcontrols()

         62     End Sub



  • Vamos Implementar os recursos de composição
  • O que é isso ?

    Nosso CustomWebControl contém um WebUserControl com propriedades, eventos e poderiam até haver métodos. Para garantir que as funcionalidades sejam mantidas para o usuário final, precisamos reimplementar a assinatura, expondo a assinatura para o usuário final e disparando os métodos do WebUserControl que compõem nosso CustomWebControl. Sim, como você deve estar imaginando, isso é trabalhoso sim.

       51     Public Event SelectionChanges As EventHandler

       52 

       53     Protected Overrides Sub createchildcontrols()

       54         uc = New [Lib].ASP.TesteControl_ascx

       55         AddHandler uc.SelecionChanged, AddressOf DispararSelecao

       56         Me.Controls.Add(uc)

       57         MyBase.CreateChildControls()

       58     End Sub

       59 

       60     Public Property SelectedDate() As Date

       61         Get

       62             Return (uc.SelectedDate)

       63         End Get

       64         Set(ByVal Value As Date)

       65             uc.SelectedDate = Value

       66         End Set

       67     End Property

       68 

       69     Private Sub DispararSelecao(ByVal sender As Object, ByVal e As EventArgs)

       70         RaiseEvent SelectionChanges(sender, e)

       71     End Sub

Passo 6 : Testar o Custom Control

  • Compile a WebControlLibrary
  • Insira o custom web control na toolbox, clicando com o botão direito na toolbox e selecionado add/remove itens
  • A partir da toolbox insira o novo Custom Web Control no formulário. Você já verá o custom control renderizado mesmo em design time, podendo testa-lo em run-time em seguida

  • Confira a janela de propriedades com o Custom Control selecionado, você poderá ver a propriedade criada

  • Confira as combos na janela de código, você poderá ver o evento criado

 

Passo 7 : Manutenção

Como dar manutenção a esta arquitetura ? Como é tipico da web, temos 2 tipos de alterações possíveis :

Alterações a serem realizadas no arquivo de TAGs, ASPX

Estas sem dúvida são as mais difíceis, pois você terá que gerar novamente o código para este arquivo de tags, refazer as alterações e recompila-lo. É como repetir esse processo todo novamente. Depois de repetido o processo, bastará substituir a DLL do componente. Dependendo do seu cuidado em não recompilar a dll do codeBehind, ela não precisará ser substituida.

Alterações a serem realizadas no CodeBehind

Fiz testes destas alterações. Basta abrir o projeto web, praticamente vazio, alterar o codeBehind, recompilar e copiar a dll para a nova aplicação. Funciona perfeitamente e a dll mais antiga, do ASCX, consegue falar com ela.

O Visual Studio, porém, fica dando warnings de compilação avisando que a dependência é para uma dll mais antiga, acusa falha na copia da dependência.

Esse warning pode ser eliminado alterando a definição de version no assemblyInfo, fazendo com que a versão seja fixa. Não fiz os testes, mas tenho certeza que funcionará.

Com isso podemos guardar este projeto web, já quase vazio, já que ele servirá para darmos manutenção ao codeBehind de nosso customWebControl. Esse projeto ainda tem um .ASCX, mas existe mais uma experiência interessante a faze : Eliminar o .ASCX e manter o .VB do codeBehind no projeto. O projeto inicialmente criado como projeto web apenas gerará um assembly, .dll, atuando como uma classLibrary. Mais um recurso que nos faz conhecer mais a fundo a arquitetura do Visual Studio.

 

Dennes Torres
MCAD,MCSD,MCSE,MCDBA





Envie seus comentįrios sobre este artigo

Nome :

E-mail :

Comentários :


Avise-me quando houverem novos comentįrios nesta pįgina

Veja abaixo os comentários já enviados :

Nome : thaty E-Mail : thaty.araujo@gmail.com
O artigo é bom, mas acho que está faltando mais detalhes,pois faltam informações necessárias à criação do componente.
Tentei seguir o artigo para criar Custom Control mas não funcionou. Já na criação da lib aparecem vários erros que não tenho noção de como resolver.
Por exemplo:
Public Class ComponenteUsuarioSemChave_ascx
Inherits ComponenteUsuarioSemChave.ComponenteUsuarioSemChave
O código não aceita o trecho Inherits ComponenteUsuarioSemChave.ComponenteUsuarioSemChave

outra coisa o código não aceita o trecho Me.txchave (textbox criado na web user control)

Alguém poderia me ajudar a resolver este problema?
Nome : Dennes E-Mail : dennes@bufaloinfo.com.br
Oi, Thaty !

Esse truque foi implementado no framework 1.1. Não fiz novas tentativas de implementação no framework 2.0 em diante, até porque os user controls evoluiram bastante.

[]'s

Dennes

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Conheça mais sobre o nosso site :

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::



Quer saber mais?
Faça um curso na Búfalo Informática, Treinamento e Consultoria e
Prepare-se para o Mercado!
Veja o que a Búfalo tem para você.

ļæ½ BĆŗfalo InformĆ”tica, Treinamento e Consultoria - Rua Ɓlvaro Alvim, 37 Sala 920 - CinelĆ¢ndia - Rio de Janeiro / RJ
Tel.: (21)2262-1368 (21) 9240-5134 (21) 9240-7281 e-Mail:
contato@bufaloinfo.com.br