Archive for fevereiro 2011

CascadingDropDown Passo a Passo

Este artigo tem como objetivo criar um passo a passo, informando o que é, e como funciona o CascadingDropDown.
Deve-se entender que, o CascadingDropDown é um webControl que faz parte do Atlas toolkit, onde permite a montagem de DropDowns em cascata, ou seja, o usuário seleciona uma informação na primeira dropDown para filtrar a 2a e assim por diante, em um número indeterminado de vezes.
Para entender melhor, será mostrado um exemplo onde se realiza uma busca de um veículo, através do fabricante, modelo, ano e versão. Para isso, essas informações devem estar disponíveis em dropDowns, para que o usuário realize a filtragem, veja na Figura 1.

Figura 1- Busca de veículo
Antes de partir para o desenvolvimento da aplicação, primeiramente será criado o banco, para que assim, interaja-se posteriormente o mesmo com os dropDowns.
Criando o banco

Foi utilizado o banco SQL Server 2005 para criação da base “Mercado” e das tabelas “tbFabricante”, “tbModelo”, “tbAno” e “tbVersao”.
Segue abaixo o script de criação:

/*Cria Base Mercado */

create database Mercado


/*Cria tabela tbFabricante */

CREATE TABLE [dbo].[tbFabricante](
      [IdFabricante] [int] IDENTITY(1,1) NOT NULL,
      [Nome] [nvarchar](50),
 CONSTRAINT [PK_tb_Fabricante] PRIMARY KEY CLUSTERED
(
      [IdFabricante] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

/*Cria tabela tbModelo que possui uma referência do fabricante */

CREATE TABLE [dbo].[tbModelo](
      [IdModelo] [int] IDENTITY(1,1) NOT NULL,
      [Nome] [nvarchar](50) ,
      [IdFabricante] [int] NOT NULL,
 CONSTRAINT [PK_tbModelo] PRIMARY KEY CLUSTERED
(
      [IdModelo] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

GO
ALTER TABLE [dbo].[tbModelo]  WITH CHECK ADD  CONSTRAINT [FK_Modelo_Fabricante] FOREIGN KEY([IdFabricante])
REFERENCES [dbo].[tbFabricante] ([IdFabricante])
GO
ALTER TABLE [dbo].[tbModelo] CHECK CONSTRAINT [FK_Modelo_Fabricante]



/*Cria tabela tbAno que possui uma referÊncia do modelo */

CREATE TABLE [dbo].[tbAno](
      [IdAno] [int] IDENTITY(1,1) NOT NULL,
      [Nome] [nvarchar](50) ,
      [IdModelo] [int] NOT NULL,
 CONSTRAINT [PK_tbAno] PRIMARY KEY CLUSTERED
(
      [IdAno] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

GO
ALTER TABLE [dbo].[tbAno]  WITH CHECK ADD  CONSTRAINT [FK_Ano_Modelo] FOREIGN KEY([IdModelo])
REFERENCES [dbo].[tbModelo] ([IdModelo])
GO
ALTER TABLE [dbo].[tbAno] CHECK CONSTRAINT [FK_Ano_Modelo]



/*Cria tabela tbVersao que possui uma referência do ano */

CREATE TABLE [dbo].[tbVersao](
      [IdVersao] [int] IDENTITY(1,1) NOT NULL,
      [Nome] [nvarchar](50) ,
      [IdAno] [int] NOT NULL,
 CONSTRAINT [PK_tbVersao] PRIMARY KEY CLUSTERED
(
      [IdVersao] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

GO
ALTER TABLE [dbo].[tbVersao]  WITH CHECK ADD  CONSTRAINT [FK_Versao_Ano] FOREIGN KEY([IdAno])
REFERENCES [dbo].[tbAno] ([IdAno])
GO
ALTER TABLE [dbo].[tbVersao] CHECK CONSTRAINT [FK_Versao_Ano]
Observa-se na Figura 2 que a tabela criada possui o seguinte relacionamento:

Figura 2 – Diagrama da base Mercado
Agora, serão inseridos alguns registros no banco recém criado, com objetivo futuro de testes. Segue script abaixo:
/*Inserir na tbFabricante*/

INSERT [tbFabricante] ([Nome]) VALUES ('FIAT')
INSERT [tbFabricante] ([Nome]) VALUES ('FORD')


/*Inserir na tbModelo com referencia ao fabricante*/

INSERT [tbModelo] ([Nome], [IdFabricante]) VALUES ('PALIO', 1)
INSERT [tbModelo] ([Nome], [IdFabricante]) VALUES ('UNO', 1)
INSERT [tbModelo] ([Nome], [IdFabricante]) VALUES ('FIESTA', 2)


/*Inserir na tbAno com referência ao modelo */


INSERT [tbAno] ([Nome], [IdModelo]) VALUES ('2011', 1)
INSERT [tbAno] ([Nome], [IdModelo]) VALUES ('2010', 1)
INSERT [tbAno] ([Nome], [IdModelo]) VALUES ('2010', 2)
INSERT [tbAno] ([Nome], [IdModelo]) VALUES ('2009', 2)
INSERT [tbAno] ([Nome], [IdModelo]) VALUES ('2008', 3)
INSERT [tbAno] ([Nome], [IdModelo]) VALUES ('2009', 3)


/*Inserir na tbVersao com referência ao ano*/


INSERT [tbVersao] ([Nome], [IdAno]) VALUES ('1.8 MPI WEEKEND 8V', 1)
INSERT [tbVersao] ([Nome], [IdAno]) VALUES ('1.0 MPI ELX 8V', 1)
INSERT [tbVersao] ([Nome], [IdAno]) VALUES ('1.8 MPI R 8V', 3)
INSERT [tbVersao] ([Nome], [IdAno]) VALUES ('1.4 MPI TREKKING', 3)
INSERT [tbVersao] ([Nome], [IdAno]) VALUES ('1.6 MPI TRAIL', 8)
Após a criação do banco, incia-se o desenvolvimento da aplicação. Utilizando o Visual Studio 2008, crie um novo projeto denominado Mercado, em seguida adicione quatro DropDownList e quatro Label no formulário do projeto (Figura 3), renomeie-os  para “ddlFabricante”, “ddlModelo”, “ddlAno”, “ddlVersao”, conseqüentemente. Posicione-os em tela para ficar esteticamente viável (observe a Figura 4). Compile o projeto e confirme a opção Add a new Web.config.

Figura 3 – Toolbox do Visual Studio 2005

Figura 4 – Página em construção
É claro que, como a aplicação está utilizando uma extensão do Ajax, deve-se ter na nossa aplicação, mais especificamente na pasta Bin, as ddls do AjaxControlToolKit, caso você não possua, clique aqui (http://ajaxcontroltoolkit.codeplex.com/releases/view/43475) para baixar.
Agora, deve-se entender o seguinte, é claro que, pode-se popular estes dropDowns sem utilizar o CascadingDropDown, porém o foco deste artigo é ver as vantagens que este webControl proporciona.
Toda a lógica sobre como preencher os DropDownList através de buscas no banco, ficará armazenada em um WebService. O WebService de exemplo usado neste exemplo, lê os dados a partir de uma ‘select’ SQL simples e exibe-os nos combos em questão.
Deve-se então, criar este WebService, para isso, vá até a Solution Explorer do Visual Studio, em seguida clique com o botão direito sobre o diretório do projeto, escolha Add New Item (Figura 5). Nesta nova janela, selecione o item Web Service e renomeie-o para wsVeiculo.asmx, em seguida clique em Add ( veja na Figura 6).
Deve-se observar que, para trabalhar com CascadingDropDown, o WebService precisa estar localizado no mesmo site que a página criada, devido a segurança contra Cross-Site Scripting existente no browser, que impedirá um script de fazer a chamada para um webService em outro domínio.

Figura 5 – Add New Item

Figura 6 – Add novo Web Service
Após adicionar o WebService no projeto, vá ao Solution Explorer, em seguida acesse App_Code -> wsVeiculo.cs. Nesta classe serão configurados os métodos que irão realizar as consultas que forem necessárias à base de dados.
Dentro do wsVeiculo.cs, apague o método HelloWorld, pois este é apenas demonstrativo.em seguida, deve-se adicionar a seguinte linha antes de publicicar a classe, pois esta, tem o objetivo de indicar que o WebService poderá ser invocado a partir de um script.
[ScriptService]
Póximo passo é instanciar uma string onde indicará qual conexão de banco de dados será utilizada, porém, para isso, deve-se informar para aplicação, qual servidor deseja acessar, bem como a base de dados desejada, portanto, acesse o web.config que foi criado, e configure sua connectionstring da seguinte maneira.
<connectionStrings>
   <add name="mercadoCS" connectionString="Data Source=NOMEDOCOMPUTADOR\SQLEXPRESS;Initial Catalog=NOMEDABASE(Mercado);Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
Agora volte ao WebService, e instancie uma string, com o nome conString, que irá através do ConfigurationManager, receber a string de conexão informada no web.config.
string conString = ConfigurationManager.ConnectionStrings["mercadoCS"].ToString();
Importe a classe Configuration:
using System.Configuration;
Próximo passo será criar o método que será utilizado para retornar os fabricantes para o DropDownList ddlFabricante.
    [WebMethod]
    public AjaxControlToolkit.CascadingDropDownNameValue[] GetDropDownFabricante(string knownCategoryValues, string category)
    {
        SqlConnection sqlConn = new SqlConnection(conString);
        sqlConn.Open();
        SqlCommand sqlSelect = new SqlCommand("select * from tbFabricante ORDER BY Nome", sqlConn);
        sqlSelect.CommandType = System.Data.CommandType.Text;
        SqlDataAdapter sqlAdapter = new SqlDataAdapter(sqlSelect);
        DataSet myDataset = new DataSet();
        sqlAdapter.Fill(myDataset);
        sqlConn.Close();

        List<AjaxControlToolkit.CascadingDropDownNameValue> cascadingValues = new List<AjaxControlToolkit.CascadingDropDownNameValue>();

        foreach (DataRow dRow in myDataset.Tables[0].Rows)
        {
            string categoryID = dRow["IdFabricante"].ToString();
            string categoryName = dRow["Nome"].ToString();
            cascadingValues.Add(new AjaxControlToolkit.CascadingDropDownNameValue(categoryName, categoryID));
        }

        return cascadingValues.ToArray();
    }
Este WebService criado, recebe 2 parâmetros string e retorna um parâmetro de tipo especial. É valido frisar que, AtlasControlToolkit.CascadingDropDownNameValue, é um array deste tipo . O parâmetro Category contém a informação que está sendo requisitada. Cada combo recebe um nome, uma identificação para a informação que ela representa. Pode-se utilizar somente este método para retornar para todos droDowns que for desejado, porém, será apresentado com métodos independentes para melhor entendimento.
Para verificar se este método está funcionando corretamente em conjunto à aplicação, basta voltar para pagina Default.aspx. Trabalhando em modo Source, adicione o código abaixo antes de começar a tag HTML, pois este irá “informar” para o AjaxControlToolKit que esta página irá utilizá-lo.
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc1" %>
Em seguida será adicionado o ScriptManager na página, pois este é necessário quando se trabalha com Ajax, observe na Figura 7.

Figura 7 – Add ScriptManager
Neste momento, será configurado o webControl cascadingdropdown, adicione o seguinte código na aplicação:
<cc1:CascadingDropDown ID="cddFabricante" runat="server" Category="IdFabricante" TargetControlID="ddlFabricante" PromptText="Selecione um Fabricante" LoadingText="carregando fabricantes…" ServicePath="wsVeiculo.asmx"                ServiceMethod="GetDropDownFabricante"></cc1:CascadingDropDown>
Deve-se entender que, o CascadingDropDown aparece como um webControl não visual e atua como um extender. Renomei o ID para cddFabricante, veja abaixo suas propriedades:
·         Category - Nome da categoria representada pela DropdDownList. Em nosso exemplo, "Id" pois este é o nosso identificador (da tabela tbFabricante);
·         TargetControlID - Nome da DropDownList a ser identificada. Definimos o ddlFabricante;
·         PromptText - Texto de um item nulo que é inserido na DropDownList, por exemplo, "Selecione um Fabricante";
·         LoadingText  - O texto que será exibido no momento em que os dados estão sendo carregados;
·         ServicePath - O diretório de onde está localizado seu WebService.asmx;
·         ServiceMethod - Nome do método no WebService que será executado.
Após configurar o CascadingDropDown, compile e veja se o ddlFabricante exibiu os fabricantes cadastrados, repare na Figura 8.

Figura 8 – Seleção de um Fabricante
Sendo exibido corretamente todos fabricantes, agora será criado o WebService do Modelo, Ano e Verão, segue abaixo, o código exemplo do método do Modelo:
    [WebMethod]
    public AjaxControlToolkit.CascadingDropDownNameValue[] GetDropDownModelo(string knownCategoryValues, string category)
    {
        int categoryID;

        StringDictionary categoryValues = AjaxControlToolkit.CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);

        categoryID = Convert.ToInt32(categoryValues["IdFabricante"]);


        SqlConnection sqlConn = new SqlConnection(conString);
        sqlConn.Open();
        SqlCommand sqlSelect = new SqlCommand("select * from tbModelo WHERE IdFabricante = @IdFabricante order by Nome", sqlConn);
        sqlSelect.CommandType = System.Data.CommandType.Text;
        sqlSelect.Parameters.Add("IdFabricante", SqlDbType.Int).Value = categoryID;
        SqlDataAdapter sqlAdapter = new SqlDataAdapter(sqlSelect);
        DataSet myDataset = new DataSet();
        sqlAdapter.Fill(myDataset);
        sqlConn.Close();

        List<AjaxControlToolkit.CascadingDropDownNameValue> cascadingValues = new List<AjaxControlToolkit.CascadingDropDownNameValue>();

        foreach (DataRow dRow in myDataset.Tables[0].Rows)
        {
            string productID = dRow["IdModelo"].ToString();
            string productName = dRow["Nome"].ToString();
            cascadingValues.Add(new AjaxControlToolkit.CascadingDropDownNameValue(productName, productID));
        }

        return cascadingValues.ToArray();
    }
Observando este método, percebem-se poucas diferenças para o GetDropDownFabricante, veja o código abaixo :
    categoryID = Convert.ToInt32(categoryValues["IdFabricante"]);
O categoryID vai receber o valor identificador do DropDownList (no nosso caso ddlFabricante), percebam que, em categoryValues["IdFabricante"], o identificador entre as aspas é exatamente o mesmo que está na tabela tbFabricante.
Será feita uma busca na tabela do modelo de acordo com o fabricante que foi selecionado, observe :
    sqlSelect.Parameters.Add("IdFabricante", SqlDbType.Int).Value = categoryID;
Deve-se entender que , o retorno productId, neste método, é o identificador do modelo, vejam:
    string productID = dRow["IdModelo"].ToString();
Segue abaixo os metodos do Ano e Versão, respectivamente:

    [WebMethod]
    public AjaxControlToolkit.CascadingDropDownNameValue[] GetDropDownAno(string knownCategoryValues, string category)
    {
        int categoryID;

        StringDictionary categoryValues = AjaxControlToolkit.CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);

        categoryID = Convert.ToInt32(categoryValues["IdModelo"]);


        SqlConnection sqlConn = new SqlConnection(conString);
        sqlConn.Open();
        SqlCommand sqlSelect = new SqlCommand("select * from tbAno WHERE IdModelo = @IdModelo order by Nome Desc", sqlConn);
        sqlSelect.CommandType = System.Data.CommandType.Text;
        sqlSelect.Parameters.Add("IdModelo", SqlDbType.Int).Value = categoryID;
        SqlDataAdapter sqlAdapter = new SqlDataAdapter(sqlSelect);
        DataSet myDataset = new DataSet();
        sqlAdapter.Fill(myDataset);
        sqlConn.Close();

        List<AjaxControlToolkit.CascadingDropDownNameValue> cascadingValues = new List<AjaxControlToolkit.CascadingDropDownNameValue>();

        foreach (DataRow dRow in myDataset.Tables[0].Rows)
        {
            string productID = dRow["IdAno"].ToString();
            string productName = dRow["Nome"].ToString();
            cascadingValues.Add(new AjaxControlToolkit.CascadingDropDownNameValue(productName, productID));
        }

        return cascadingValues.ToArray();
    }



    [WebMethod]
    public AjaxControlToolkit.CascadingDropDownNameValue[] GetDropDownVersao(string knownCategoryValues, string category)
    {
        int categoryID;

        StringDictionary categoryValues = AjaxControlToolkit.CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);

        categoryID = Convert.ToInt32(categoryValues["IdAno"]);


        SqlConnection sqlConn = new SqlConnection(conString);
        sqlConn.Open();
        SqlCommand sqlSelect = new SqlCommand("select * from tbVersao WHERE IdAno = @IdAno order by Nome", sqlConn);
        sqlSelect.CommandType = System.Data.CommandType.Text;
        sqlSelect.Parameters.Add("IdAno", SqlDbType.Int).Value = categoryID;
        SqlDataAdapter sqlAdapter = new SqlDataAdapter(sqlSelect);
        DataSet myDataset = new DataSet();
        sqlAdapter.Fill(myDataset);
        sqlConn.Close();

        List<AjaxControlToolkit.CascadingDropDownNameValue> cascadingValues = new List<AjaxControlToolkit.CascadingDropDownNameValue>();

        foreach (DataRow dRow in myDataset.Tables[0].Rows)
        {
            string productID = dRow["IdVersao"].ToString();
            string productName = dRow["Nome"].ToString();
            cascadingValues.Add(new AjaxControlToolkit.CascadingDropDownNameValue(productName, productID));
        }

        return cascadingValues.ToArray();
    }
Agora que foram criados os métodos, será ativado os CascadingDropDown para o drops ddlModelo, ddlAno e ddlVersao.
Observe que, a única propriedade diferente neste cddModelo, é a ParentControlID, que refere ao DropDownList “pai”, neste caso é o ddlFabricante.
Modelo :
            <cc1:CascadingDropDown ID="cddModelo" runat="server" Category="IdModelo" TargetControlID="ddlModelo" PromptText="Selecione um Modelo" ParentControlID="ddlFabricante" LoadingText="carregando modelos…" ServicePath="wsVeiculo.asmx" ServiceMethod="GetDropDownModelo">
            </cc1:CascadingDropDown>
Ano:
            <cc1:CascadingDropDown ID="cddAno" runat="server" Category="IdAno" TargetControlID="ddlAno" PromptText="Selecione um Ano" ParentControlID="ddlModelo" LoadingText="carregando anos…" ServicePath="wsVeiculo.asmx" ServiceMethod="GetDropDownAno">
            </cc1:CascadingDropDown>
Versão:
            <cc1:CascadingDropDown ID="cddVersao" runat="server" Category="IdVersao"   TargetControlID="ddlVersao" PromptText="Selecione um Versão" ParentControlID="ddlAno" LoadingText="carregando versões…" ServicePath="wsVeiculo.asmx" ServiceMethod="GetDropDownVersao">      
            </cc1:CascadingDropDown>
Como o estudo realizado, percebe-se que, utilizando o CascadingDropDown para realizar este tipo de filtragem, facilita a implementação do código, e gera agilidade no carregamento de informações, devido essas consultas serem feitas através de WebServices.