![]() |
||||||||
|
|
||||||||


| com exemplos em VB |
| Componente para deixar forms em Vb semelhantes às telas do winnamp |
| Componente para colocar sua aplicação VB no Systray |
| Componente para transformar sua aplicação VB em serviço |
| Ferramentas úteis para quem usa Olap Server |
| |

|
||||||||||||||||
Pesquisa personalizada
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 :
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 :
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 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 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 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 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 |
||||||||||||||||
|
Veja abaixo os comentários já enviados :
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 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 • e-Mail: contato@bufaloinfo.com.br