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
Os 3 Porquinhos
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:

 






Personalização da start page do VS

Baixe o código fonte do componente

Instale a personalização da start page no seu VS

A start page do VS funciona como um mini-browser personalizado. Através da aba Online Resources podemos ter acesso a diversos recursos on-line, até mesmo busca de conteúdo.

Esta start page pode ser personalizada. A personalização da start page ocorre através de um arquivo XML, que deve ser gravado em um diretório específico do disco. Quando o VS é iniciado, ele verifica a existência deste arquivo, valida o arquivo e o carrega para a start page.

Este arquivo pode ter conteúdo fixo ou pode buscar conteúdo na web. Mas é baseado em uma síntaxe específica utilizada para a start page e algumas outras finalidades (dynamic help).

Precisaremos então estar gerando este XML e gerando os dados deste XML. Uma montagem interessante seria que a start page buscasse informações na web, para poder ser atualizada dinamicamente.

A primeira idéia para gerar a área de dados deste XML foi fazer isso através de DataSets. Realmente fiz um exemplo assim e funcionou muito bem. Mas quando resolvi adicionair mais informações, cheguei a conclusão que estava complexo demais. Precisava de algum componente que pudesse fazer a conversão dos dados de um dataSet para o formato de dados necessário para a Start Page do VS.

A manipulação de arquivos de Schema do DataSet para montar os dados em determinado formato eu já mostrei no artigo sobre criação de documentos RSS http://www.bufaloinfo.com.br/artigos/artigo0701.asp , então nem irei entrar neste tópico. Vamos direto ao passo seguinte, a criação de um componente que permita a transformação de dados de dataSets na saida que desejamos para a Start Page. Os DataSets trabalham internamente na forma de XML, isso nos dá a liberdade de gerar um XSL para transforma-los, é isso que permite que criemos este componente de forma tão simples.

Precisamos então analisar o que este componente precisará conter :

  • Como os dados da startPage podem estar divididos em vários LinkGroups, isso significa que podem vir de vários dataSets/Tables. Consequentemente não bastará ter uma única propriedade DataSource, precisaremos de uma coleção de dataSources, sendo que cada dataSource irá gerar um diferente linkGroup
  • Para cada dataSource precisaremos guardar um mapeamento das conversões que serão feitas de cada campo para seus devidos elementos dentro da estrutura de dados utilizada pela start Page
  • O componente guardará em seu assembly uma estrutura de XSL (na forma de embbeded resource) que utilizaremos para converter, com um mínimo de código, o dataSet para o formato de dados da start page.

Cada um dos itens acima, porém, tem suas complexidades particulares. Vamos analisar um a um para no final juntarmos isso tudo, gerando o resultado final do componente.

Data Sources

Precisaremos montar uma coleção de origens de dados. Para montar uma coleção personalizada devemos criar uma nova classe que irá herdar as características de collectionBase.

Ao montar uma coleção personalizada precisamos saber de que será essa coleção, ou seja, será uma coleção de que tipo de dados ?

Se inserirmos um tipo de dados diretamente na coleção, tal como dataSet, então o editor de coleções padrão do VS (collection editor) irá, quando o usuário clicar no botão Add, criar um novo elemento, no exemplo um novo dataSet.

Mas não queremos isso. Queremos que o usuário possa escolher o dataSet/dataTable que deseja utilizar e não criar um novo dentro do editor de coleções.

A solução para isso é que a coleção seja uma coleção de uma classe personalizada, nossa, e nesta classe tenhamos uma propriedade que poderá conter um dataSet. Desta forma o collectionEditor, ao receber o Add, cria uma nova instância da nossa classe e mostra, na propertyGrid ao lado, as propriedades, no caso uma, que poderá conter um dataSet. A propriedade estará vazia e o propertyGrid permitirá selecionarmos o dataSet.

Outro problema que teremos serão as opções que o propertyGrid irá mostrar. Gostariamos que o propertyGrid listasse todos os dataSources possíveis, mas se definirmos a propriedade em questão como DataSet, apenas os dataSets serão listados, não as dataTables, por exemplo.

A solução para isso é que façamos a definição da propriedade não como um tipo especifico, mas como uma interface : IlistSource. Desta forma, quando o propertyGrid for nos exibir as opções disponíveis, buscará no form todos os componentes que suportam a interface IlistSource ou, em outras palavras, todas as possíveis origens de dados.

Então vamos ver como fica o código, levando em consideração o que vimos até agora :

Public Class clGStart
   Inherits System.ComponentModel.Component
   Dim cd As New ColecaoDataSets
   Public ReadOnly Property OrigensDados() As ColecaoDataSets
      Get
        Return (cd)
      End Get
   End Property
End Class
 
Public Class ColecaoDataSets
   Inherits CollectionBase
   Default Public Property Item(ByVal index As Integer) As UmaTabela
      Get
          Return List(index)
      End Get
      Set(ByVal Value As UmaTabela)
        List(index) = Value
      End Set
   End Property
       
   Public Function Add(ByVal value As UmaTabela) As Integer
      Return List.Add(value)
   End Function 'Add
   Public Function IndexOf(ByVal value As UmaTabela) As Integer
      Return List.IndexOf(value)
   End Function 'IndexOf
       
   Public Sub Insert(ByVal index As Integer, ByVal value As UmaTabela)
      List.Insert(index, value)
   End Sub 'Insert
       
   Public Sub Remove(ByVal value As UmaTabela)
     List.Remove(value)
   End Sub 'Remove
End Class
       
Public Class UmaTabela
   Dim tb As IListSource
       
   Public Property TabelaOrigem() As IListSource
      Get
         Return (tb)
      End Get
      Set(ByVal Value As IListSource)
        tb = Value
        Mapeamento.TabelaOrigem = tb
      End Set
   End Property
End Class
       

Podemos já iniciar os testes deste componente, adicionando um outro projeto na solução, desta vez um projeto windows Application. Adicionamos o componente na toolbox, clicando com o botão direito na toolbox, e por fim adicionamos o componente no form e testamos.

Porém encontraremos um problema : Apesar de tudo funcionar normalmente, se fecharmos o formulário windows e abrirmos novamente os objetos UmaTabela que tivermos inserido em nosso componente serão perdidos. Isso porque estas informações deveriam ser transformadas em código dentro da sub initializeComponent, através de um processo de serialização para código, porém pelo fato das classes estarem dentro de uma coleção isso não acontece diretamente.

Para garantir a serialização da classe UmaTabela para o código do InitializeComponente precisaremos fazer 2 coisas :

1) Aplicar o atributo designserializationvisibility na propriedade, para determinar a forma como deve ser codificada na initializecomponent

2) Criar um TypeConverter para a classe UmaTabela. Objetos TypeConverter permitem converter um tipo de dados em outro de forma personalizada, mas nesse caso a função é mais específica : O TypeConverter precisará informar para o VS qual constructor da classe UmaTabela deve ser utilizado para instanciar a classe dentro do initializeComponent

Vejamos então como fica a criação do TypeConverter :

Public Class conversor
   Inherits TypeConverter
   Public Overloads Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, _
        ByVal culture As System.Globalization.CultureInfo, _
        ByVal value As Object, ByVal destinationType As System.Type) As Object


      Dim ci As ConstructorInfo
      If destinationType Is GetType(InstanceDescriptor) Then
         ci = GetType(UmaTabela).GetConstructor(New Type() {})
         Return (New InstanceDescriptor(ci, Nothing, False))
      End If
      MyBase.ConvertTo(context, culture, value, destinationType)
   End Function
   Public Overloads Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, _
                   ByVal destinationType As System.Type) As Boolean
      If destinationType Is GetType(InstanceDescriptor) Then
         Return (True)
      End If
   End Function
End Class
       

Essa classe herda de typeConverter e fazemos o overloads em 2 métodos, canConvertTo e ConvertTo. O designer do VS irá utilizar esse typeConverter para solicitar uma conversão para instanceDescriptor. InstanceDescriptor é uma classe que descreve nossa classe, permitindo que o designer do VS faça a serialização da nossa classe em código.

Assim sendo, no canConvertTo identificamos se o pedido de conversão é para o instancedescriptor, se for retornamos true indicando que estamos prontos para fazer esta conversão.

Já no ConvertTo da mesma forma verificamos o tipo pedido. Sendo instancedescriptor, utilizamos reflections para obter o constructor de nossa classe (umatabela) e com esse constructor geramos uma nova instância da classe instanceDescriptor.

O true passado para o constructor do instanceDescriptor tem um papel importante neste ponto, pois informa que além de instanciar a classe o VS deverá também serializar o valor de suas propriedades.

O TypeConverter precisará ser ligado com a classe UmaTabela através de um atributo, veja :

<TypeConverter(GetType(conversor))> _
         Public Class UmaTabela
       
Veja também a aplicação do atributo que citei :
 <Description("Colecao de DataSets que serão utilizados como origem de dados"), _
   DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
   Public ReadOnly Property OrigensDados() As ColecaoDataSets
      Get
        Return (cd)
      End Get
   End Property
       

Definido como "Content", este atributo informa ao VS que deve serializar o conteúdo deste objeto, não o objeto em si.

Mapeamento

Para realizarmos o mapeamento entre a origem de dados e o formato da start page vamos criar uma nova classe, que chamaremos de mapeamento. Veja como fica a classe :

Public Class Mapeamento
 Dim LG As String
   Dim vID, vURL, vImage, vBlurb, vItem As String
   Dim tb As DataTable
 Public Property TabelaOrigem() As DataTable
   Get
      Return (tb)
   End Get
   Set(ByVal Value As DataTable)
      tb = Value
   End Set
 End Property
 Public Property LinkGroup() As String
   Get
      Return (LG)
   End Get
   Set(ByVal Value As String)
      LG = Value
   End Set
 End Property
 Public Property ID() As String
   Get
      Return (vID)
   End Get
   Set(ByVal Value As String)
      vID = Value
   End Set
 End Property
 Public Property Image() As String
   Get
      Return (vImage)
   End Get
   Set(ByVal Value As String)
      vImage = Value
   End Set
 End Property
 Public Property Blurb() As String
   Get
      Return (vBlurb)
   End Get
   Set(ByVal Value As String)
      vBlurb = Value
   End Set
 End Property
 Public Property Item() As String
   Get
      Return (vItem)
   End Get
   Set(ByVal Value As String)
      vItem = Value
   End Set
 End Property
 Public Property URL() As String
   Get
      Return (vURL)
   End Get
   Set(ByVal Value As String)
      vURL = Value
   End Set
 End Property
End Class
       

Como podemos observar, uma classe simples para guardar informações do mapeamento.

Como precisaremos de uma instância desta classe para cada origem de dados, podemos então criar uma propriedades dentro da classe UmaTabela para guardar uma instância da classe mapeamento. Veja como fica a classe UmaTabela :

<TypeConverter(GetType(conversor))> _
         Public Class UmaTabela
   'Inherits System.ComponentModel.Component
   Dim tb As IListSource
   Dim map As New Mapeamento
 <DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _
   Public Property TabelaOrigem() As IListSource
       Get
          Return (tb)
       End Get
       Set(ByVal Value As IListSource)
          tb = Value
          Mapeamento.TabelaOrigem = tb
       End Set
   End Property
 <Editor(GetType(EditorMapeamento), GetType(UITypeEditor)), _
  DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _
   Public Property Mapeamento() As Mapeamento
      Get
         Return (map)
      End Get
      Set(ByVal Value As Mapeamento)
         map = Value
      End Set
   End Property
End Class
       

Observe que na propriedade Mapeamento já apliquei um typeEditor. Esse Type Editor será necessário para criar uma interface visual para a edição do mapeamento, já que o VS não tem nenhuma interface que permita esta edição. Com a criação de um Type Editor, ao clicarmos nos 3 pontos "..." ao lado da propriedade nas janelas de edição visual e abriremos um form personalizado para editar este mapeamento.

Além do TypeEditor precisaremos também de um TypeConverter para a classe mapeamento, com o mesmo objetivo do anterior. Vejamos o código do TypeConverter :

 

Public Class conversorMapeamento
   Inherits TypeConverter
 Public Overloads Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, _
         ByVal culture As System.Globalization.CultureInfo, ByVal value As Object, _
         ByVal destinationType As System.Type) As Object
   Dim ci As ConstructorInfo
   Dim id As InstanceDescriptor
   If destinationType Is GetType(InstanceDescriptor) Then
       ci = GetType(Mapeamento).GetConstructor(New Type() {})
       id = New InstanceDescriptor(ci, Nothing, False)
       Return (id)
   End If
   MyBase.ConvertTo(context, culture, value, destinationType)
 End Function
 Public Overloads Overrides Function CanConvertTo(ByVal context As _
        System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
   If destinationType Is GetType(InstanceDescriptor) Then
      Return (True)
   End If
 End Function
End Class
       

Não tem muita novidade neste typeConverter, é equivalente ao que fizemos anteriormente para a classe UmaTabela.

Precisaremos criar um formulário que permita a edição visual do mapeamento. Vamos chamar esse formulário de frmMapear. A estrutura do XML da start Page tem 5 elementos que precisam ser definidos : Item,ID,Blurb, URL e Image. Esses itens deverão ser vinculados a campos da tabela de origem. Já o LinkGroup, também necessário para o XML da start page, é fixo.

Então precisaremos ter neste form 5 combos e uma textbox para podermos fazer a definição destes itens. Nas combos iremos exibir os nomes dos campos da origem de dados, para que o usuário escolha qual campo irá para qual posição no XML da start page.

Este form será disparado a partir da interface visual do VS. O objetivo será editar uma instância da classe mapeamento, portanto o form deverá possuir uma variável pública deste tipo, para receber a instância a ser editada e depois passa-la de volta para a interface visual do VS.

No form_load, portanto, deveremos programar o preenchimento das combos de acordo com a instância da classe mapeamento que o formulário já receberá preenchida. Veja como fica o form_load :

 

 Public objMap As Mapeamento
 Private Sub frmMapear_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
   Dim c As DataColumn
     For Each c In objMap.TabelaOrigem.Columns
         cmbID.Items.Add(c.ColumnName())
         cmbURL.Items.Add(c.ColumnName)
         cmbBlurb.Items.Add(c.ColumnName)
         cmbImage.Items.Add(c.ColumnName)
         cmbItem.Items.Add(c.ColumnName)
     Next
     cmbID.Items.Add("-- Selecione --")
     cmbURL.Items.Add("-- Selecione --")
     cmbBlurb.Items.Add("-- Selecione --")
     cmbImage.Items.Add("-- Selecione --")
     cmbItem.Items.Add("-- Selecione --")
     If objMap.ID <> "" Then
        cmbID.SelectedIndex = cmbID.Items.IndexOf(objMap.ID)
     Else
        cmbID.SelectedIndex = cmbID.Items.Count - 1
     End If
     If objMap.URL <> "" Then
        cmbURL.SelectedIndex = cmbURL.Items.IndexOf(objMap.URL)
     Else
        cmbURL.SelectedIndex = cmbURL.Items.Count - 1
     End If
     If objMap.Blurb <> "" Then
        cmbBlurb.SelectedIndex = cmbBlurb.Items.IndexOf(objMap.Blurb)
     Else
        cmbBlurb.SelectedIndex = cmbBlurb.Items.Count - 1
     End If
     If objMap.Image <> "" Then
        cmbImage.SelectedIndex = cmbImage.Items.IndexOf(objMap.Image)
     Else
        cmbImage.SelectedIndex = cmbImage.Items.Count - 1
     End If
     If objMap.Item <> "" Then
        cmbItem.SelectedIndex = cmbImage.Items.IndexOf(objMap.Item)
     Else
        cmbItem.SelectedIndex = cmbItem.Items.Count - 1
     End If
     txtLinkGroup.Text = objMap.LinkGroup
 End Sub

 

No código acima temos :

- Preenchimento das combos com os campos da tabela de origem
- Seleção dos valores atuais da classe mapeamento nas combox ou
- Seleção dos itens "-- Selecione --" na combo

Vamos criar algum código a mais para controlar a interface visual. O botão Ok deverá estar desabilitado por default e só ser habilitado quando todos os itens forem preenchidos. O único item opcional é o item Image. Então quando todos os itens estiverem preenchidos o botão Ok deve ser novamente habilitado. Veja como fica :

 Private Sub cmbID_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles cmbID.SelectedIndexChanged, cmbBlurb.SelectedIndexChanged, cmbItem.SelectedIndexChanged, _
          cmbURL.SelectedIndexChanged, txtLinkGroup.TextChanged
   Dim b As Boolean
     b = (cmbID.SelectedIndex <> cmbID.Items.Count - 1 And cmbURL.SelectedIndex <> cmbURL.Items.Count - 1)
     b = b And cmbBlurb.SelectedIndex <> cmbBlurb.Items.Count - 1 _
                      And cmbItem.SelectedIndex <> cmbItem.Items.Count - 1
     b = b And Trim(txtLinkGroup.Text) <> ""
     cmdOk.Enabled = b
 End Sub
       

Oberve o uso do handles na sub acima para tratar todos os eventos de change importantes deste form. Assim a cada mudança verificamos se já podemos ou não habilitar o botão Ok.

Por fim, devemos montar os botões Ok e cancelar. Veja como fica :

 Private Sub cmdOk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdOk.Click
   objMap.URL = cmbURL.SelectedItem
   objMap.Blurb = cmbBlurb.SelectedItem
   objMap.Item = cmbItem.SelectedItem
   objMap.ID = cmbID.SelectedItem
   If cmbImage.SelectedIndex <> cmbImage.Items.Count - 1 Then
      objMap.Image = cmbImage.SelectedItem
   End If
   objMap.LinkGroup = txtLinkGroup.Text
   Me.DialogResult = Windows.Forms.DialogResult.OK
End Sub
       
 Private Sub cmdCancelar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdCancelar.Click
   Me.DialogResult = Windows.Forms.DialogResult.Cancel
 End Sub
       

No botão Ok preenchemos a instância do objMap com os novos valores, selecionados na combo. Já no cancelar apenas fechamos o form. Tanto no Ok como no cancelar utilizamos a propriedade DialogResult para fechar o form, determinando ao mesmo tempo resultado da operação, se foi um Ok com sucesso ou um cancelamento.

Montado o form, devemos agora criar o TypeEditor que será o responsável pelo disparo do form quando a propriedade for editada. Veja como fica o TypeEditor :

Public Class EditorMapeamento
   Inherits UITypeEditor
 
   Public Overloads Overrides Function GetEditStyle(ByVal context As _
           System.ComponentModel.ITypeDescriptorContext) As System.Drawing.Design.UITypeEditorEditStyle
       Return (System.Drawing.Design.UITypeEditorEditStyle.Modal)
   End Function
       
   Public Overloads Overrides Function EditValue(ByVal context As _
                       System.ComponentModel.ITypeDescriptorContext, _
                       ByVal provider As System.IServiceProvider, ByVal value As Object) As Object
      Dim f As New frmMapear
      Dim ch As IComponentChangeService
      ch = context.GetService(GetType(IComponentChangeService))
      ch.OnComponentChanging(context.Instance, context.PropertyDescriptor)
      f.objMap = value
      f.ShowDialog()
      If f.DialogResult = Windows.Forms.DialogResult.OK Then
         ch.OnComponentChanged(context.Instance, context.PropertyDescriptor, value, f.objMap)
         Return (f.objMap)
      End If
  End Function
End Class
       

Temos na criação deste TypeEditor 2 overrides : GetEditStyle para determinar o estilo de edição, que no nosso exemplo será um formulário modal. E o EditValue para disparar o formulário modal e realizar a edição em si.

Uma questão importante com relação ao overloads do editValue é a necessidade de disparar os métodos OnComponentChanging e OnComponentChanged . Isso porque o VS precisa identificar quando realmente ocorrer uma mudança na propriedade. Identificando isso o VS marca o arquivo como tendo sido alterado e necessitando ser salvo e serializa o conteúdo das propriedades.

Observe a forma como obtemos o objeto IComponentChangeService, a partir do context.getservice. O restante do código é mais simples : Passamos o objeto Mapeamento para o formulário, exibimos o formulário e, tendo Ok como resultado, chamamos o onComponentChanged e retornamos o objeto.

XSL para transformação dos dados

XSL é uma linguagem derivada do XML que permite a realização de transformações de dados de um formato de XML para outro formato qualquer.

Portanto usando XSL poderemos transformar rapidamente os dados de uma origem de dados para o formato necessário para a start page.

A origem de dados, porém, irá variar. Portanto o XSL que será usado para a transformação também. Como as origens de dados serão dataTables, o formato geral do XSL será o mesmo, mas irão variar os nomes de campo que serão gerados.

Então a idéia é termos um arquivo XSL padrão e marcarmos dentro deste arquivo as posições em que deverão entrar os nomes de campo. Podemos guardar este arquivo XSL como embedded resource, ou seja, fazer com que seja guardado dentro do assembly do componente, ao invés de em um arquivo a parte.

 

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
     <xsl:apply-templates select="//(idRegistro)" />
</xsl:template>
       
<xsl:template match="(idRegistro)">
 <LItemEx>
    <LItem>
        <xsl:attribute name="ID">
            X<xsl:value-of select="(CampoID)" />
        </xsl:attribute>
        <xsl:attribute name="URL">
           <xsl:value-of select="(CampoURL)" />
        </xsl:attribute>
        <xsl:attribute name="Image">
           (CampoIMG)
        </xsl:attribute>
   
        <xsl:attribute name="LinkGroup">(Link)</xsl:attribute>
        <xsl:value-of select="(CampoTitulo)" />
   </LItem>
   <Blurb> 
     <xsl:value-of select="(CampoDesc)" />
   </Blurb>
 </LItemEx>
</xsl:template>
</xsl:stylesheet>
       

Para que o XSL se torne um embedded resource basta alterarmos a propriedade Build Action, na janela de propriedades sobre o arquivo.


Processando os dados

Vamos então começar a gerar o processamento que será realizado pelo componente. Vamos por partes pequenas, das menores tarefas até as maiores tarefas. Veja 3 das tarefas que precisarão ser feitas :

RecuperarRecurso : Recuperar os embedded resources do assembly. Teremos pelo menos 2, o style e a estrutura do XML de resultado

MontarEstilo : A partir do estilo que estará como embedded resource, deveremos montar o estilo que usaremos para a transformação dos dados, substituindo as "variáveis" pelos nomes de campo

AplicarEstilo : Fazer a aplicação do estilo XSL, transformando os dados

RecuperarRecurso

Como teremos mais de um recurso a ser recuperado, esta função será parametrizada, recebendo o nome do recurso a ser recuperado. Irá devolver um stringBuilder com o texto recuperado.

Publicamos uma dica sobre a recuperação de embedded resources no link http://www.bufaloinfo.com.br/dicas.asp?cod=627

Veja com fica o código desta função :

 Private Function RecuperarRecurso(ByVal NomeRecurso As String) As StringBuilder
   Dim arq As [Assembly]
   Dim sr As StreamReader
   Dim sb As StringBuilder
   arq = [Assembly].GetExecutingAssembly()
 'Observe a escrita do nome do resource embedded, um formato padrão 
   sr = New StreamReader(arq.GetManifestResourceStream("clGeradorStart." & NomeRecurso & ".xml"))
   sb.Append(sr.ReadToEnd)
   Return (sb)
 End Function
       

MontarEstilo

O que precisaremos fazer nesta função são basicamente replaces : substituir as "variáveis" guardadas dentro do estilo pelos nomes de campo definidos no mapeamento dos dados. Esta montagem precisará ser feita várias vezes, de acordo com o conjunto de DataSources que houver sido definido. Então vamos fazer essa sub de forma parametrizável : Ela deverá receber uma instancia da classe montagem e um stringBuilder com o estilo.

O código fica da seguinte forma :

 Private Function MontarEstilo(ByVal map As Mapeamento, ByVal sb As StringBuilder) As StringBuilder
   sb = sb.Replace("(idRegistro)", map.TabelaOrigem.TableName)
   sb = sb.Replace("(CampoID)", map.ID)
   sb = sb.Replace("(CampoURL)", map.URL)
   If map.Image <> "" Then
      sb = sb.Replace("(CampoIMG)", "<xsl:value-of select=""" & map.Image & """ />")
   Else
      sb = sb.Replace("(CampoIMG)", "")
   End If
   sb = sb.Replace("(CampoTitulo)", map.Item)
   sb = sb.Replace("(CampoDesc)", map.Blurb)
   Return (sb)
 End Function
       

A imagem é opcional, dai o IF que vemos acima no código.

AplicarEstilo

A aplicação do estilo também ocorrerá diversas vezes, de acordo com o número de dataSources definidas. Portanto essa é mais uma função que precisará estar trabalhando com parâmetros : receberá dois xmlDocuments, o documento a ser transformado e o documento contendo o estilo.

Na função utilizaremos a classe xslTransform para fazer a conversão do XML. Veja como fica a função :

 Private Function AplicarEstilo(ByVal doc As XmlDocument, ByVal estilo As XmlDocument) As StringBuilder
   Dim obj As New Xml.Xsl.XslTransform
   Dim xrE As New XmlNodeReader(estilo)
   Dim resultado As New StringBuilder
   Dim s As New MemoryStream
   Dim xrw As New XmlTextWriter(s, Encoding.ASCII)
   obj.Load(xrE, Nothing, Nothing)
   obj.Transform(doc, args:=Nothing, resolver:=Nothing, output:=xrw)
   s.Position = 0
   Dim sr As New StreamReader(s)
   resultado.Append(sr.ReadToEnd)
   Return (resultado)
 End Function

Uma questão bem interessante nesta subs é a necessidade de passagem de argumentos nomeados na chamada do método Transform.

O problema é que o método em questão tem vários overloads. Com nothing nos 2 últimos parametros o VS não consegue identificar qual dos overloads estamos chamando.

Claro que com nothing não seria possível fazer directCast. Então a única solução é fazer a passagem nomeada, para que desta forma o VS saiba qual overload estamos chamando.

Vamos agora iniciar a montagem das subs para fazer o processamento dos dados. Da mesma forma que fizemos até agora, vamos dividir isso por partes :

ProcessarItem : Sub para processar uma origem de dados

ProcessarTodos : Sub para processar todas as origens de dados

ProcessarItem

Mantendo-se parametrizada, esta sub receberá uma origem de dados (classe UmaTabela) para iniciar o trabalho de transformação. Precisará então fazer o seguinte :

- Obter o XML com os dados de origem
- Montar o estilo para a transformação
- Transformar os dados
- Guardar o resultado da transformação

Essa sub será chamada várias vezes, uma vez para cada origem de dados que tenha sido informada. Então a cada transformação será necessário guardar o resultado da transformação, podemos fazer isso em uma collection no nível da classe.

Veja como fica o código :

 Private Sub ProcessarItem(ByVal ut As UmaTabela)
   Dim sb As StringBuilder
   Dim doc As New XmlDocument
   Dim docEstilo As New XmlDocument
   Dim s As String
    s = retirarAcentos(DirectCast(ut.TabelaOrigem, DataTable).DataSet.GetXml)
    s = Regex.Replace(s, "xmlns=""[^""]*""", "")
    doc.LoadXml(s)
 
    sb = MontarEstilo(ut.Mapeamento, sbEstilo)
    docEstilo.LoadXml(sb.ToString)
   
    COLresultados.Add(AplicarEstilo(doc, docEstilo))
 End Sub

 

Observe que esta sub conta com a existência de uma variável sbEstilo, um string builder definido a nível de classe que estará guardando o estilo embedded, depois de carregado. Além disso esta sub faz duas outras tarefas :

- Retira acentos dos dados. No formato em que está sendo montada a startpage não aceitará acentuação. É possível fazer com que aceite, mas não trabalhei nisso.

- Retirar o XMLNS. O XMLNS, namespace do XML, dificulta a aplicação do estilo XSL. A forma mais fácil de resolver o problema é retira-lo. Observe o interessante uso de expressões regulares para, em uma única linha, retirar todas as aparições do xmlns dentro do documento XML de dados. Em um artigo futuro falaremos sobre expressões regulares.

Veja como fica a função retirarAcentos :

 Private Function retirarAcentos(ByVal texto As String) As String
   Dim sb As New StringBuilder
   sb.Append(texto)
   sb = sb.Replace("é", "e")
   sb = sb.Replace("ó", "o")
   sb = sb.Replace("ê", "e")
   sb = sb.Replace("ã", "a")
   sb = sb.Replace("õ", "o")
   sb = sb.Replace("ç", "c")
   sb = sb.Replace("í", "i")
   sb = sb.Replace("ô", "o")
   sb = sb.Replace("á", "a")
   sb = sb.Replace("â", "a")
   sb = sb.Replace("ú", "u")
   sb = sb.Replace("É", "E")
   sb = sb.Replace("Ó", "O")
   sb = sb.Replace("Ê", "E")
   sb = sb.Replace("Ã", "A")
   sb = sb.Replace("Õ", "O")
   sb = sb.Replace("Ç", "C")
   sb = sb.Replace("Í", "I")
   sb = sb.Replace("Ô", "O")
   sb = sb.Replace("Á", "A")
   sb = sb.Replace("Â", "A")
   sb = sb.Replace("Ú", "U")
   Return (sb.ToString)
End Function
       

 

ProcessarTodos

O que esta sub precisará fazer é carregar o estilo que encontra-se embedded e fazer um loop no conjunto de origens de dados informadas. Veja como fica o código :

 Private Sub ProcessarTodos()
   Dim sb As StringBuilder
   Dim ut As UmaTabela
   For Each ut In OrigensDados
      sbEstilo = RecuperarRecurso("Estilo")
      ProcessarItem(ut)
   Next
 End Sub
       

Nesta sub é definida a variável sbEstilo, que conforme vimos um pouco acima é utilizada pela sub ProcessarItem.

Por fim, vamos montar a função pública, que irá realizar todo o processamento. Vamos chama-la simplesmente de Processar.

Esta função precisará disparar todo o processamento, o que a principio é simples, apenas chamar o processarTodos. Mas precisará também juntar todo o resultado em um XML único e utilizar para isso a estrutura do XML que encontra-se embedded.

Então esta função terá os seguintes passos :

- Recuperar a estrutura do XML
- Disparar o processamento
- Usar um string builder para concatenar todo o resultado dos processamentos
- Inserir o resultado do processamento dentro da estrutura do XML
- Carregar o resultado em um XMLDocument e devolver

Veja como fica o código :

 Public Function Processar() As XmlDocument
   Dim sb As StringBuilder
   Dim sbresultado As New StringBuilder
   Dim doc As StringBuilder
   Dim docresultado As New XmlDocument
   sb = RecuperarRecurso("Modelo")
     ProcessarTodos()
     For Each doc In COLresultados
        sbresultado.Append(doc.ToString)
     Next
       
     b.Replace("(dados)", sbresultado.ToString)
     docresultado.LoadXml(sb.ToString)
     Return (docresultado)
 End Function
       

O modelo para a Start Page

Este modelo nada mais é do que a estrutura de XML da parte de dados da página de start, com a posição dos dados marcada. Esse XML ficará embedded dentro do componente e será usado para posicionar os dados em meio a tag Data e assim devolver o resultado final.

Veja como fica, bem simples :

<Data>
 <Context>
    <Links>
       (dados)
     </Links>
 </Context>
</Data>

 

Utilizando o componente

Vamos agora começar a utilização do componente.

O principal truque na utilização do componente é o fato de que não iremos utiliza-lo dentro de um webService, mas sim dentro de uma página aspx.

A saida do componente e da página em si será XML e não o trabalho tradicional de um webForm. Porém se fizessemos no formato de webService este adicionaria uma instrução de processamento, <?xml , no inicio do resultado e isso não é suportado pela start page do Visual Studio. Produzindo o resultado em uma página ASPX podemos ter o controle preciso do resultado.

Vamos então criar um arquivo XML e mante-lo como embedded. Este arquivo irá conter a parte fixa do XML que será gerado para a start page

Gerando a parte fixa do XML

Os novos itens que criarmos na start page aparecerão na aba "Online Resources", no lado esquerdo, junto a lista de itens que já existe ali. Cada item que criarmos pode ser uma simples página ou pode ser dividir em várias abas, como já acontece com alguns dos itens incluidos ali.

Vamos então analisar a hierarquia do XML que montaremos :

<TabDefinition> : Raiz do XML . Dentro deste elemento iremos inserir a definição de um ou mais Tabs. Cada tab é um item no lado esquerdo da aba "Online Resources"

<Tab> : Identifica uma Tab, um item que será exibido no lado esquerdo da janela online resources

<Application> : Identifica uma aplicação. Agrega os Panels da aplicação e os dados que irão preencher os Panels

<Pane> : Identifica um painel. Se houver apenas um, é o conteúdo da página em si, se houver mais de um a página será dividida em abas superiores e cada Pane representa uma aba.

<PaneSet> : Utilizado para agregar os Panes quando existem mais de um

<Data> : Contém dados que serão utilizados para preencher dinâmicamente os Panes

Preenchimento de dados

A ligação entre os Panes e a área de dados é feita através de uma tag LinkGroup. Essa tag, aplicada dentro de um PANE, irá buscar os dados dentro da tag Data que estiverem marcados com o mesmo linkgroup.

Veja como fica o modelo do XML para a start page :

<TabDefinition>
   <Tab ID="gus_tab" Name="Grupos de Usuarios" Filterable="false">
      <Application ID="gus_app">
         <PaneSet ID="psgus">
             <Pane ID="eventos_Pane" Title="Proximos Eventos">
                <LinkGroup ID="grpEventos" />
             </Pane>
             <Pane ID="grupos_Pane" Title="Grupos Ativos">
                <LinkGroup ID="grpGrupos" />
             </Pane>
             <Pane ID="noticias_Pane" Title="Ultimas Noticias">
                <LinkGroup ID="lastNews" />
             </Pane>
             <Pane ID="artigos_Pane" Title="Ultimos Artigos">
                <LinkGroup ID="grpArtigos" />
             </Pane>
        </PaneSet>
         (dados)
      </Application>
   </Tab>
 </TabDefinition>
       

Obtendo os dados dos webServices

Para montar os dados na start Page iremos acessar 4 webServices :

Artigos : http://www.bufaloinfo.com.br/servicosweb/artigosdva/artigos.asmx
Notícias : http://www.devaspnet.com.br/noticias.asmx
Eventos : http://www.bufaloinfo.com.br/servicosweb/artigosdva/eventos.asmx
Grupos : http://www.bufaloinfo.com.br/servicosweb/artigosdva/grupos.asmx

Vamos recuperar os dados destes webServices e utilizar o componente para transformar esses dados na saida que desejamos.

Destes serviços, o mais complicado é o serviço de notícias, pois a devolução dele não é um dataSet, mas sim um XML em formato de RSS. Então precisaremos carregar o resultado deste serviço de notícias em um dataSet e adicionar uma coluna a mais em uma das tableas, para servir como ID para os objetos na start page.

Também por causa disso o mapeamento da origem de dados para a start page precisará ser feito via código, e não visualmente (apenas para as notícias, claro)

Veja como fica o código :

 Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
   'Put user code to initialize the page here
   Dim srv1 As New srvGrupos.Grupos
   Dim srv2 As New srvEventos.eventos
   Dim srv3 As New srvArtigos.Artigos
   Dim srv4 As New srvNoticias.Noticias1
   Dim ds As New DataSet
   Dim ut As New clGeradorStart.UmaTabela
   Dim i As Integer
   Dim dr As DataRow
   Dim doc As New XmlDocument
   Dim xrr As XmlNodeReader
       
    doc.LoadXml(srv4.ListaNoticias.OuterXml)
    xrr = New XmlNodeReader(doc)
    ds.ReadXml(xrr)
    ds.Tables(2).Columns.Add(New DataColumn("ID", GetType(String)))
    For i = 0 To ds.Tables(2).Rows.Count - 1
        dr = ds.Tables(2).Rows(i)
        dr.BeginEdit()
        dr("ID") = "X" & (i + 300)
        dr.EndEdit()
    Next
    ut.TabelaOrigem = ds.Tables(2)
    ut.Mapeamento.TabelaOrigem = ds.Tables(2)
    ut.Mapeamento.LinkGroup = "lastNews"
    ut.Mapeamento.Item = "title"
    ut.Mapeamento.Blurb = "description"
    ut.Mapeamento.URL = "link"
    ut.Mapeamento.ID = "ID"
       
    ClGStart1.OrigensDados.Add(ut)
    DsGrupos1.Merge(srv1.ListarGrupos)
    DsArtigos1.Merge(srv3.UltimosArtigos)
    DsEventos1.Merge(srv2.FiltrarEventos(Nothing, Nothing, Nothing))
    ClGStart1.OrigensDados(2).Mapeamento.Blurb = "descricao"
    Response.ContentType = "text/xml"
    Dim sb As StringBuilder
    sb = RecuperarRecurso("arquivo")
    sb = sb.Replace("(dados)", ClGStart1.Processar().OuterXml)
    Response.Write(sb.ToString)
 End Sub
       

A instalação nas máquinas locais

Para que esta personalização da start page possa ser instalada nas máquinas client, precisamos de um XML que faça a requisição de dados ao endereço web onde estiver a aplicação que montamos acima.

Veja como fica este XML :

<TabDefinition>
    <Tab ID="gus_tab" Name="Grupos de Usuarios" Filterable="false">
        <Application ID="gus_app">
           <PaneSet ID="psgus">
              <Pane ID="eventos_Pane" Title="Proximos Eventos">
                 <LinkGroup ID="grpEventos" />
              </Pane>
              <Pane ID="grupos_Pane" Title="Grupos Ativos">
                 <LinkGroup ID="grpGrupos" />
              </Pane>
              <Pane ID="noticias_Pane" Title="Ultimas Noticias">
                 <LinkGroup ID="lastNews" />
              </Pane>
              <Pane ID="artigos_Pane" Title="Ultimos Artigos">
                 <LinkGroup ID="grpArtigos" />
              </Pane>
          </PaneSet>
        </Application>
        <Feeds>
           <Feed>
               <Source LCID="1033" URL="http://www.devaspnet.com.br/paginastart/webform1.aspx" />
           </Feed>
        </Feeds>
     </Tab>
 </TabDefinition>
       

Este arquivo deve ser salvo no seguinte caminho : Program Files\Microsoft Visual Studio 2003\Common7\IDE\HTML\Custom

Se a pasta Custom não existir, pode cria-la.

A partir dai já temos a start page personalizada e você estará recebendo novidades diretamente no seu Visual Studio !

Dennes Torres
MCAD,MCSD,MCSE,MCDBA
http://br.thespoke.net/MyBlog/Dennes/MyBlog.aspx





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 : RODRIGO JENDICK DE CAMARGO E-Mail : rodrigojendick@yahoo.com.br
BOM DIA ,

ESTOU COM UMA DUVIDA SOBRE O PROPERTYGRID NO VISUAL STUDIO 2005 POIS PRECISO CARREGAR DADOS DE OUTRAS TABELAS EM UMA PROPRIEDADE , SOMENTE CONSEGUI FAZER ISSO CARREGANDO UM DROPDOWNLIST COM STRINGS E NÃO OBJETOS ....

QUERIA SABER SE TEM COMO CARREGAR COM OBJETOS TIPO A COMBOBOX POR EXEMPLO COM O DISPLAY MEMBER E O VALUE MEMBER ???



DESDE JÁ OBRIGADO....

RODRIGO JENDICK DE CAMARGO
Nome : Papayss E-Mail : Papayhf@mail.com
Thank you.
Nome : Papaykh E-Mail : Papayxr@mail.com
Thank you!
Nome : Papaymt E-Mail : Papayfs@mail.com
Thank you!
Nome : Jada E-Mail : friend35@hotmail.com
I'm not working at the moment https://www.leemshop.nl/tadelakt/ order diflucan online no prescription General Instructions for Completing Paper Claims
Nome : Seth E-Mail : infest@msn.com
Could I have a statement, please? https://www.freshspring.co.uk/accessibility/ zyban cst code with a valid quantity which
Nome : Jordan E-Mail : lightsoul@gmail.com
Could I have , please? https://www.accountcontrol.com/leadership.php tablet cefixime transmission in the United States. Rockville [MD]: U.S. Department of Health and
Nome : Brody E-Mail : lightsoul@gmail.com
Some First Class stamps https://www.wesearchtogether.org/about.php where can you buy diflucan over the counter 8.0, Table 6 on page 8.0.4 for
Nome : freelife E-Mail : cooler111@yahoo.com
Could you transfer $1000 from my current account to my deposit account? https://www.wesearchtogether.org/about.php diflucan no prescription canada Allen, who won Oscars for best director and best original screenplay for the 1977 film "Annie Hall" and for best screenplay for 1986's "Hannah and Her Sisters" and 2011's "Midnight in Paris," married Soon-Yi in 1997.
Nome : freelove E-Mail : lifestile@msn.com
What do you like doing in your spare time? https://www.wesearchtogether.org/about.php diflucan no prescription Public cloud computing, which AWS pioneered in 2006, lets companies rent computing power, storage and other services from data centers shared with other customers. The services typically are cheaper and more flexible for companies than maintaining their own hardware.
Nome : Jamar E-Mail : henryj68@gmail.com
Have you got any ? https://www.freshspring.co.uk/accessibility/ can yu buy zyban nline Data from the U.S. Census Bureau and public benefit programs show 52 percent of fast-food cooks, cashiers and other "front-line" staff had relied on at least one form of public assistance, such as Medicaid, food stamps and the Earned Income Tax Credit program, between 2007 and 2011, researchers at the University of California-Berkeley and the University of Illinois said.
Nome : Valeria E-Mail : raymon9e@usa.net
I'm doing an internship https://www.freshspring.co.uk/accessibility/ zyban buprpin The China Securities Regulatory Commission (CSRC) is now ready to transfer audit papers to the U.S. Securities and Exchange Commission (SEC) and the Public Company Accounting Oversight Board (PCAOB), a CSRC spokesman said, confirming local media reports. He did not identify the company whose audit documents the CSRC is turning over, or say when the handover will take place.
Nome : -1' E-Mail : 1
1
Nome : 1 E-Mail : -1'
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : Flyman E-Mail : terrence9i@usa.net
Directory enquiries https://rarcc.org/contact-us bimatoprost cheapest â??Itâ??s turned out to be the ultimate road trip vehicle,â? said Butitta, whoâ??s in the midst of a 5,000-mile cross-country journey to â??testâ? the bus, which was his final project at the University of Minnesotaâ??s School of Architecture.
Nome : Gaylord E-Mail : alexanderqva@aol.com
An accountancy practice https://rarcc.org/contact-us bimatoprost price Both sides are trying to dampen expectations of any rapid breakthrough at the two-day meeting, the first to be held since President Hassan Rouhani took office, promising conciliation over confrontation in Iran's relations with the world.
Nome : Vaselir E-Mail : apollo2@gmail.com
5031f06cb9aaf10295a17fca86ac0644
Nome : ??????? ?????????? ????? E-Mail : dsfgadfg@okoijhy.com
I believe that it is not so,
Nome : ??????? ?????????? ????? E-Mail : dsfgadfg@okoijhy.com
I believe that it is not so,
Nome : ?????????? ????? ??? nod32 ????????? E-Mail : fhytjeja@ijuinuybyt.co.uk
And what do you write?,
Nome : ??????? ?????????? ????? E-Mail : cijnsfiudia@kjuib.net
Maybe enough news on this topic?,
Nome : ????????? ??? ?????? ?????? nod32 E-Mail : oijmnuinib@modisnmfdf.co.uk
Maybe enough news on this topic?,
Nome : ????????? ??? ?????? ?????? nod32 E-Mail : oijmnuinib@modisnmfdf.co.uk
Maybe enough news on this topic?,
Nome : ??????? ????? ??????? ????????? E-Mail : gthtjhgja@okpuijnuyb.eu
original news,
Nome : ????? ?? ??????? ??????? E-Mail : safgfd@jnmu.biz
?Excellent site! Thank U!,
Nome : JCSymvArmXIiqJiP E-Mail : vNsXlbuBPwgoaFT
tJ9BW2 6.txt;2;8
Nome : ????? ?????????? nod32 v5 E-Mail : dfauiodngfui1@jnuybu.biz
original news,
Nome : ??????? ????? ?? ?????????? ????? E-Mail : dafgfghswt@ijkoijio.com
?Excellent site! Thank U!,
Nome : -1' E-Mail : 1
1
Nome : 1 E-Mail : -1'
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : -1'
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : -1' E-Mail : 1
1
Nome : 1 E-Mail : -1'
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : -1'
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 53J36AHJ E-Mail : 5ty1b4cbf@outlook.com
Times are chnginag for the better if I can get this online!
Nome : -1' E-Mail : 1
1
Nome : 1 E-Mail : -1'
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : fizWEUSrcm E-Mail : wopuwg582@yahoo.com
Hey there, DJ! Instieetrng story. Too bad school was an uphill battle for you. Good to hear you've "grown out of it" as some might say. Any chance you want to "guest author" that story on my ADHD blog? Let me know.Liz aka Busy Lizzy :-)
Nome : michael kors handbags E-Mail : uxqgujefo@gmail.com
document_srl=

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
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