Desenvolvedores.Net - TechBlog

Tag Archives: ASPX

Criando componentes no lado Servidor

1
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (Sem votação.)
Loading...
20 de junho de 2014

CriandoComponentes

Para um melhor entendimento deste Artigo veja o Índice (Rich Internet Applications com Ext.Net)

Criando componentes no lado Servidor

Ao desenvolvermos um componente utilizando como base um componente já criado no Ext.NET, ganhamos de brinde toda a reutilização deste, por exemplo: – Nosso componente já pode ser utilizado em WebForm,  em ASP MVC, com a engine Razor, no código markup, etc.

Outra vantagem desta abordagem é que quando estudamos o código do Ext.NET, você poderá entender como eles criam seus componentes com base nos componentes Ext JS,e não necessariamente você precisa ser um conhecedor profundo do Ext JS.

Como vantagem também, podemos citar que você pode desenvolver seu componente em qualquer linguagem .NET que você tenha familiaridade e não precisa aprender a ser um ninja em javascript, pois todo o javascript será gerado para o lado cliente.

Neste tópico, iremos focar na criação do componente do lado servidor, na utilização do lado cliente, e como criar seus próprios métodos que podem ser acessados do lado cliente e lado servidor.

Antes de começarmos…

Vamos definir nossas abstrações e nossos objetos de validação do componente. Como neste artigo eu irei criar dois componentes para inserção de texto. Iremos criar uma abstração para tipos texto e uma classe de resultado da validação.

Classe ValidateResult

/// <summary>
/// Classe que determina o resultado da validação feita e retorna.
/// </summary>
public sealed class ValidateResult
{
	#region locais
	bool valid = true;
	string invalidMessage = "";
	#endregion

	#region propriedades
	/// <summary>
	/// Se true, o valor inserido no componente é valido.
	/// </summary>
	public bool Valid
	{
		get { return valid; }
		private set { valid = value; }
	}

	/// <summary>
	/// Se não for válido é necessário informar uma mensagem de erro
	/// </summary>
	public string InvalidMessage
	{
		get { return invalidMessage; }
		private set { invalidMessage = value; }
	}
	#endregion

	#region Construtores
	/// <summary>
	/// Inicia o resultado da validação
	/// </summary>
	/// <param name="valid">true/false para válido ou não.</param>
	/// <param name="invalidMessage">Se "valid" for verdadeiro, é obrigatório informar a mensagem.</param>
	/// <exception cref="ArgumentException">Se "valid" for verdadeiro, e o parâmetro "invalidMessage"
	/// não for informado. Esta exceção será lançada.</exception>
	public ValidateResult(bool valid, string invalidMessage = "")
	{
		this.valid = valid;

		if(!valid && String.IsNullOrEmpty(invalidMessage))
			throw new ArgumentException("Se \"valid\" for definido como false. É obrigatório informar uma mensagem de erro em \"invalidMessage\".", "invalidMessage");

		this.invalidMessage = invalidMessage;
	}
	#endregion
}

Abstração para campos do tipo texto (TextFieldBase)

public abstract class TextFieldBase: Ext.Net.TextField, IValidator
{
	public TextFieldBase()
		: base()
	{

	}

	#region IValidator Members
	public string ErrorMessage { get; set; }
	public bool IsValid { get; set; }

	public override void Validate()
	{
		ValidateResult result = Validate(Text);
		IsValid = result.Valid;
		ErrorMessage = result.InvalidMessage;

		if(IsValid)
			MarkAsValid();
		else
			MarkInvalid(ErrorMessage);
	}

	/// <summary>
	/// Faz a validação do componente e retorna true se o texto do componente for válido, caso contrário false
	/// </summary>
	/// <param name="value">valor que deverá ser validado</param>
	/// <returns></returns>
	protected abstract ValidateResult Validate(string value);
	#endregion
}
note-taking No código em destaque, vejam que eu fiz uma herança da classe “Ext.Net.TextField” e da interface “IValidator”.
Desta forma eu já herdo todas as funcionalidades de um componente do tipo “TextField” e apenas ajusto para a minha necessidade. a herança da interface “IValidator” é apenas para a implementação dos métodos de validação.

Ok! Definimos a nossa abstração. Vamos agora criar o nosso componente.

Meu primeiro componente

Vamos criar um componente bem simples. Um campo de CEP, que irá fazer uma validação e será utilizado em uma página.

CEPField

public class CEPField: TextFieldBase
{
	#region Contrutores
	public CEPField()
		: base()
	{
		DirectEvents.Blur.Event += new ComponentDirectEvent.DirectEventHandler(Blur_Event);
		AllowBlank = false;
		this.IsRemoteValidation = true;
		this.RemoteValidation.Validation += new RemoteValidationDirectEvent.RemoteValidationEventHandler(RemoteValidation_Validation);
		FieldLabel = "CEP";
	}
	#endregion

	#region Tratadores de eventos
	void RemoteValidation_Validation(object sender, RemoteValidationEventArgs e)
	{
		Validate();
		e.Success = IsValid;
		e.ErrorMessage = ErrorMessage;
	}

	void Blur_Event(object sender, DirectEventArgs e)
	{
		this.SetValue(Format.CEP(Text));
	}
	#endregion

	#region overrides

	public override void SetValue(object value)
	{
		Validate();

		base.SetValue(value);
		if(!string.IsNullOrEmpty(ClientID))
			Ext.Net.X.AddScript(ClientID +
				".isValid = function(){return " + IsValid.ToString().ToLower() + ";};");
	}

	public override int MaxLength
	{
		get { return 9; }
		set { base.MaxLength = 9; }
	}
	#endregion

	#region IValidator Members
	protected override ValidateResult Validate(string value)
	{
		if(AllowBlank && String.IsNullOrEmpty(value))
			return new ValidateResult(true);

		value = Utilities.OnlyNumbers(value);

		if(String.IsNullOrEmpty(value))
			return new ValidateResult(false, "O CEP não pode ser vazio ou não foi digitado corretamente.");

		//permite somente números
		if(value.Count(w => !Char.IsNumber(w)) > 0)
			return new ValidateResult(false, "O CEP não é válido.");

		//para ser válido tem que ter 8 caracteres
		if(value.Length != 8)
			return new ValidateResult(false, "O CEP não é válido.");

		return new ValidateResult(true);
	}
	#endregion
}
question Hm! OK! Eu criei meu componente. Mas como eu faço para chamá-lo em uma página asp? É possível utilizar a mesma marcação que temos para o Ext.NET, utilizando a tag “<ext>“?

R: Sim, é possível, com um pequeno ajuste no nosso arquivo “web.config”.
Dentro da tag “” devemos inserir a linha em destaque, logo abaixo

<pages>
  <controls>
	<!--Indica que toda página criada irá ter a diretiva "ext" como padrão para ser usada como marcação na criação.-->
	<add assembly="Ext.Net" namespace="Ext.Net" tagPrefix="ext"/>
	<add assembly="Ext.Net.Tutorial" namespace="Ext.Net.Tutorial.ComponentModel" tagPrefix="tut"/>
  </controls>
</pages>
  • Esta linha indica que um assembly deverá ser adicionado; “add assembly“; para todas as páginas criadas;
  • Os controles que estão no “namespace” …
  • Deverão ser associados com o prefixo;”tagPrefix“; “tut“;

Pronto. Agora podemos utilizar o nosso componente apenas escrevendo a tag

<tut:CEPField ID="CEPField1" runat="server" FieldLabel="Informe seu CEP">
 </tut:CEPField>

O próximo componente que será criado é um “CurrencyField“, um campo para inserir valores monetários. Vamos ver o código do mesmo, e explicar logo em seguida.

public class CurrencyField: TextFieldBase
{
	#region Construtores
	public CurrencyField()
		: base()
	{
		//-------------------------------------------------------------------------
		// Define que só podemos digitar números de 0-9 e a vírgula.
		//-------------------------------------------------------------------------
		MaskRe = "/[0-9\\,]/";

		//-------------------------------------------------------------------------
		// Define o método javascript que irá ser chamado ao receber o foco
		//-------------------------------------------------------------------------
		Listeners.Focus.Handler = "this.setRawValue(this.getNumber());this.selectText();";

		//-------------------------------------------------------------------------
		// Define o método que será chamado ao perder o foco
		//-------------------------------------------------------------------------
		Listeners.Blur.Handler = "this.setValue(this._getValue(this.getValue()));";

		//-------------------------------------------------------------------------
		// Define que o valor deverá ser alinhado a direta do componente
		//-------------------------------------------------------------------------
		FieldStyle = "text-align:right;";
	}

	#endregion

	#region Overrides
	public override ConfigItemCollection CustomConfig
	{
		get
		{
			ConfigItemCollection result = base.CustomConfig;
			string curSbl = "R$";

			#region _getValue
			//-------------------------------------------------------------------------
			// Criando o método _getValue para o lado cliente.
			//-------------------------------------------------------------------------
			ConfigItem it = new ConfigItem("_getValue", @"function(v){
				return MyApp.Util.Format.currency(this.cleanValue(v), 2,'" + curSbl + @"');
				}
			");

			result.Add(it);
			#endregion

			#region getNumber
			//-------------------------------------------------------------------------
			// Criando o método getNumber para o lado cliente.
			//-------------------------------------------------------------------------
			it = new ConfigItem("getNumber", @"function(){
				if(this.number === undefined || this.number == 0)
					this.number = MyApp.Convert.toNumber(this.getValue());

				return this.number;
				}
			");

			result.Add(it);
			#endregion

			#region setValue
			//-------------------------------------------------------------------------
			// Substituindo o método setValue, com a nossa definição
			//-------------------------------------------------------------------------
			it = new ConfigItem("setValue", @"function(v){
				v = this._getValue(v);
				this.setRawValue(v);
				this.number = MyApp.Convert.toNumber(this.getValue());
				this.text = v;
				}
			");

			result.Add(it);
			#endregion

			#region cleanValue
			//-------------------------------------------------------------------------
			// Criando o método cleanValue para o lado cliente.
			//-------------------------------------------------------------------------
			it = new ConfigItem("cleanValue", @"function(v){
					if(v){
						return MyApp.Util.Format.number(v, 2);
					}else
						return 0;
				}
			");

			result.Add(it);
			#endregion

			return result;
		}
	}

	public override void SetValue(object value)
	{
		this.SetValue(value.ToString());
		base.SetValue(Number);
	}
	protected void SetValue(string text)
	{
		Text = text;
		Number = Convert.ToDouble(text);
	}
	#endregion

	#region Propriedades
	public virtual double Number
	{
		get { return Utilities.Convert.ToDouble(base.Text); }
		set { base.Text = value.ToString(); }
	}
	#endregion

	#region IValidator members
	protected override ValidateResult Validate(string value)
	{
		double d = Utilities.Convert.ToDouble(value);

		if(!AllowBlank && d == 0)
			return new ValidateResult(false, "É obrigatório informar o valor monetário.");

		return new ValidateResult(true);
	}
	#endregion
}

Neste código foi apresentado um meio de você criar seus próprios métodos javascript e associá-los ao seus componentes.

Para isto, utilizamos o que chamamos de “CustomConfig“, veja nas linhas em destaque.

note-taking O objeto “Ext.Net.ConfigItem“, permite que você defina métodos e propriedades para seus controles. Feito isso, você pode acessar em um código javascript.

var v = App.MyComponent._getValue();

Entendendo o objeto Ext.Net.ConfigItem

Este objeto tem a finalidade de vincular seus métodos javascript aos componentes Ext.NET, isso se torna útil quando estamos criando nossos próprios componentes.

Possui 3 propriedades importantes:

  • Name: Define o nome do método ou propriedade que será exposto;
  • Value:  Define o valor que será representado pelo sue método ou propriedade;
  • Mode: O mode pode ser:
    • Auto: O Ext.NET tentará detectar o tipo e se não for capaz, A propriedade “Value” será encapsulada como uma string, ou como um número, de acordo com seu tipo;
    • Raw: A propriedade “Value” será exposta como está;
    • Value: Valor da propriedade ou código do método;
note-taking Por convenção, nomeie seus métodos utilizando “camelCase“. Se você escrever o nome do método em “UpperCase” o Ext.NET irá converter para “camelCase“, mantendo a convenção de nomes definida pelo javascript.

 


É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Trabalhando com dados, Combobox e GridPanel

0
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (1 votos, média: 4,00 de 5)
Loading...
19 de junho de 2014

Dados_Combobox_GridPanel

Para um melhor entendimento deste Artigo veja o Índice (Rich Internet Applications com Ext.Net)

Trabalhando com dados, Combobox e GridPanel

À essa altura do tópico, já temos conhecimento sobre os componentes, “DirectMethod“, “DirectEvents“, “Listeners“, entre outras coisas. Então é hora de entendermos como podemos exibir os dados aos nossos usuários. Neste tópico eu vou apresentar à vocês a classe “Store“. Então, vamos lá.

Store

O “Store” é um componente que trata os dados entre o cliente e o servidor, normalmente utilizado com componentes do tipo “Grids” e “Comboboxes“. Um “Store” tem a capacidade de filtrar e ordenar os dados, seja local ou remotamente.

Este por sua vez, tem dois pontos importantes:

  • Um “Model“, onde definimos os tipos de dados, nomes de campos, validações, entre outros;
  • Um “Proxy“, responsável pelas requisições entre cliente e servidor, e manter os dados atualizados no lado cliente;
note-taking O componente “Store” tem vários tipos de subclasses, como “JsonStore“, “ArrayStore“, “XMLStore” entre outros. Neste tópico iremos ver os dois mais utilizados “JsonStore” e “ArrayStore“.Mas não se preocupe, quando Ext.NET gera as classes proxyes, automaticamente já é gerado todo o código Ext Js que irá tratar cada tipo de “Store“.

Models

Um “Model” define os dados que o “Store” irá tratar. Em versões anteriores do Ext.NET, este era conhecido como “Record“. No “Model” podemos definir os dados, as validações, nomes para cada campo e formato.

Proxy

Um “Proxy” é utilizado para manter os dados, é uma abstração para o mecanismo de tratamento dos dados e se divide em dois tipos:

  • Client-Side Proxies;
    • MemoryProxy: É um simples “proxy” que mantem os dados na memória do navegador;
    • LocalStorageProxySessionStorageProxy: Usam a nova definição de armazenamento do HTML5. Não funcionam em navegadores mais antigos, pois estes não aceitam HTML5
    • (Eu ainda não estou muito familiarizado com o HTML5 e as novas definições de proxy para o mesmo. Para evitar informação errada, vou parar por aqui 🙂 )

  • Server-Side Proxies;
    • AjaxProxy: Este é um “proxy” de propósito geral, que aceita objetos do tipo array, cada índice é um nome de coluna definido no “model“;
    • JsonPProxy: Permite requisições AJAX “cross-domain“;
    • PageProxy: É um “proxy” que trabalha na página e permite a ligação com eventos no lado servidor;

Entendido o processo de acesso utilizando “Stores”, vamos botar em prática o que aprendemos.

Não se preocupe com os detalhes do código, vamos nos ater apenas ao uso do “Store” e suas peculiaridades.

Antes de começar

Para os dois exemplos, temos que preparar o nosso ambiente antes de começar, para isso, crie dois arquivos de estilo.

combobox.css

.list-item
{
    font: normal 11px tahoma, arial, helvetica, sans-serif;
    padding: 3px 10px 3px 10px;
    border: 1px solid #fff;
    border-bottom: 1px solid #eeeeee;
    white-space: normal;
    color: #555;
}

.list-item h3
{
    display: block;
    font: inherit;
    font-weight: bold;
    margin: 0px;
    color: #222;
}

gridpanel.css

.x-grid-cell-fullName .x-grid-cell-inner
{
    font-family: tahoma, verdana;
    display: block;
    font-weight: normal;
    font-style: normal;
    color: #385F95;
    white-space: normal;
}

.x-grid-rowbody div
{
    margin: 2px 5px 20px 5px !important;
    width: 99%;
    color: Blue;
}

.x-grid-row-expanded td.x-grid-cell
{
    border-bottom-width: 0px;
}

Estes dois arquivos serão responsáveis pela apresentação dos dados no lado cliente.

Continuando …

Exibindo os dados…

Vamos utilizar uma lista de objetos para exibir e tratar os dados do lado cliente. Nos dois exemplos iremos configurar um “Store” para uma “Combbox” e para  uma “GridPanel“.

Veja os exemplos:

Atente apenas ao uso do “Store”. O Resto do código será explicado posteriormente.

GridPanel

ASPX Code

<head id="Head1" runat="server">
    <title>Tutorial Ext.NET</title>
    <link href="../css/gridpanel.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="../Scripts/GridPanel.js"></script>
</head>
<body>
    <form id="Form1" runat="server">
    <ext:ResourceManager ID="ResourceManager1" runat="server" />
    <ext:Window ID="Window1" runat="server" Collapsible="true" Maximizable="true" Icon="Money"
        Title="Orders List" Width="1300" Height="600" X="50" Y="50" Layout="Fit" Closable="false">
        <Items>
            <ext:GridPanel ID="GridPanel1" runat="server" Title="Employees" Frame="true" Header="false"
                Border="false">
                <Store>
                    <ext:Store ID="Store1" runat="server" OnReadData="Store1_ReadData">
                        <Model>
                            <ext:Model ID="Model1" runat="server">
                                <Fields>
                                    <ext:ModelField Name="CustomerID" />
                                    <ext:ModelField Name="EmployeeID" />
                                    <ext:ModelField Name="OrderDate" />
                                    <ext:ModelField Name="RequiredDate" />
                                    <ext:ModelField Name="ShippedDate" />
                                    <ext:ModelField Name="ShipVia" />
                                    <ext:ModelField Name="Freight" />
                                    <ext:ModelField Name="ShipName" />
                                    <ext:ModelField Name="ShipAddress" />
                                    <ext:ModelField Name="ShipCity" />
                                    <ext:ModelField Name="ShipRegion" />
                                    <ext:ModelField Name="ShipPostalCode" />
                                    <ext:ModelField Name="ShipCountry" />
                                </Fields>
                            </ext:Model>
                        </Model>
                    </ext:Store>
                </Store>
                <ColumnModel ID="ColumnModel1" runat="server">
                    <Columns>
                        <ext:Column ID="Column0" runat="server" Text="CustomerID" Width="150" DataIndex="CustomerID">
                        </ext:Column>
                        <ext:Column ID="Column1" runat="server" DataIndex="EmployeeID" Text="EmployeeID"
                            Width="150" />
                        <ext:DateColumn ID="Column2" runat="server" DataIndex="OrderDate" Text="Order Date"
                            Width="150" Format="dd/MM/yyyy" />
                        <ext:DateColumn ID="DateColumn1" runat="server" DataIndex="RequiredDate" Text="Required Date"
                            Width="110" Format="dd/MM/yyyy" />
                        <ext:DateColumn ID="DateColumn2" runat="server" DataIndex="ShippedDate" Text="Shipped Date"
                            Width="110" Format="dd/MM/yyyy" />
                        <ext:Column ID="Column3" runat="server" DataIndex="ShipVia" Text="Ship Via" Width="150" />
                        <ext:Column ID="Column4" runat="server" DataIndex="Freight" Text="Freight" Width="100" />
                    </Columns>
                </ColumnModel>
                <View>
                    <ext:GridView ID="GridView1" runat="server">
                        <GetRowClass Handler="return 'x-grid-row-expanded';" />
                    </ext:GridView>
                </View>
                <SelectionModel>
                    <ext:RowSelectionModel ID="RowSelectionModel1" runat="server" Mode="Single" />
                </SelectionModel>
                <Features>
                    <ext:RowBody ID="RowBody1" runat="server">
                        <GetAdditionalData Handler="orig.rowBody = '<div><span style=\'color: red\'>Ship Information</span>' + '</br/>' + data.ShipName + '</br/>' + data.ShipAddress + '</br/>' + data.ShipCity + (data.ShipRegion == null ? '' : ' - ' + data.ShipRegion) + '</br/>' + (data.ShipPostalCode == null ? '' : data.ShipPostalCode + '</br/>') + data.ShipCountry + '</div>'; orig.rowBodyColspan = record.fields.getCount();" />
                    </ext:RowBody>
                </Features>
                <BottomBar>
                    <ext:PagingToolbar ID="PagingToolbar1" runat="server">
                        <Items>
                            <ext:Label ID="Label1" runat="server" Text="Page size:" />
                            <ext:ToolbarSpacer ID="ToolbarSpacer1" runat="server" Width="10" />
                            <ext:ComboBox ID="ComboBox1" runat="server" Width="80">
                                <Items>
                                    <ext:ListItem Text="1" />
                                    <ext:ListItem Text="2" />
                                    <ext:ListItem Text="10" />
                                    <ext:ListItem Text="20" />
                                </Items>
                                <SelectedItems>
                                    <ext:ListItem Value="10" />
                                </SelectedItems>
                                <Listeners>
                                    <Select Handler="#{GridPanel1}.store.pageSize = parseInt(this.getValue(), 10); #{GridPanel1}.store.reload();" />
                                </Listeners>
                            </ext:ComboBox>
                        </Items>
                        <Plugins>
                            <ext:ProgressBarPager ID="ProgressBarPager1" runat="server" />
                        </Plugins>
                    </ext:PagingToolbar>
                </BottomBar>
            </ext:GridPanel>
        </Items>
    </ext:Window>
    </form>
</body>
note-taking Atenção às linhas em destaque, nestas linhas, estão as definições do “Store”, do “Model” e do “Proxy”.
As definições do modelo estão na tag “<Model>” e dentro de cada tag “<Model>” temos para cada campo a definição “<ModelField>”, onde definimos o nome da propriedade, ou campo, do objeto que será usado no componente “<ColumnModel>” que é definido logo abaixo.
Dentro do “<ColumnModel>” utilizamos a propriedade “DataIndex”, esta tem que ser o mesmo nome definido em “<Model>”, e será este nome que deverá ser usado em seus código javascript.

 

question Mas onde está a definição do <Proxy> dentro do <Store>?

R: Como não definimos um <Proxy> e estamos utilizando uma requisição de página, método “OnReadData=’Store1_ReadData'”, automaticamente será definido como proxy um “PageProxy”.

Code Behind

public partial class GridPanelArray: System.Web.UI.Page
{
	protected void Page_Load(object sender, EventArgs e)
	{
		if(!X.IsAjaxRequest)
		{
			this.BindData();
		}
	}

	protected void Store1_ReadData(object sender, StoreReadDataEventArgs e)
	{
		BindData();
	}

	private void BindData()
	{
		Store store = this.GridPanel1.GetStore();

		store.DataSource = this.Data;
		store.DataBind();
	}

	List<Order> Data
	{
		get
		{
			return Order.Find<Order>();
		}
	}
}

No código acima, temos o método que será chamado quando o “Store” requisitar os dados para o servidor, “Store1_ReadData“. Este método por sua vez chama o método “BindData()“, dentro do método “BindData()”, é carregado uma lista de pedidos (“Order”) e retornados para a propriedade “DataSource” do “Store“, e logo em seguida fazemos a ligação dos dados chamando o método “Store.DataBind()“.

ComboBox

<ext:ComboBox ID="ComboBox4" runat="server" FieldLabel="Selecione vários colaboradores"
	DisplayField="FirstName" Width="320" LabelWidth="130" QueryMode="Local" TypeAhead="true"
	MultiSelect="true">
	<Store>
		<ext:Store ID="Store4" runat="server" Data="<%# ArrayOfEmployees %>" AutoDataBind="true">
			<Model>
				<ext:Model ID="Model4" runat="server">
					<Fields>
						<ext:ModelField Name="EmployeeID" />
						<ext:ModelField Name="FirstName" />
						<ext:ModelField Name="LastName" />
					</Fields>
				</ext:Model>
			</Model>
			<Reader>
				<ext:ArrayReader />
			</Reader>
		</ext:Store>
	</Store>
	<ListConfig>
		<ItemTpl ID="ItemTpl1" runat="server">
			<Html>
				<div class="list-item">
							<h3>ID: {EmployeeID}</h3>
							{FirstName} {LastName}
					</div>
			</Html>
		</ItemTpl>
	</ListConfig>
</ext:ComboBox>

Code Behind

public object ArrayOfEmployees
{
	get
	{
		object result = (from e in Employee.Find<Employee>()
						 select new object[]
						 {
							 e.EmployeeID,
							 e.FirstName,
							 e.LastName,
						 }).ToArray();

		return result;

	}
}

No exemplo do combobox não mudou muita coisa, definimos um “Model” com o mapeamento dos campos, mas  … Temos uma diferença, definimos que o nosso “Reader” irá utilizar uma “ArrayReader”, logo temos que retornar um “Array” de objetos, isto ficou a cargo da propriedade  “ArrayOfEmployees“. A chamada também foi modificada, não usamos o evento “OnDataRead“, e sim a propriedade “Data” com um diretiva ASPX <%# ArrayOfEmployees %>, em ASPX esta diretiva diz ao interpretador para retornar um objeto “datasource“.

Utilizando um DataSource.

O Ext.NET aceita diversos tipos de “DataSources”:

  • LinqDataSource: Aceita comando do tipo “LINQ”;
  • ObjectDataSource: Aceita classes definidas dentro da nossa aplicação. Eu gosto deste 🙂
  • SqlDataSource: Aceita comando do tipo SQL;
  • XmlDataSource: Aceita arquivos do tipo XML. Este datasource requer que seja criado um “Transform” para que possa compreender os dados;
note-taking Você pode usar diversos “datasources” em uma mesma página, para isso basta definir o ID de cada um, e ao utilizar em um “Store“, define o ID do “datasource” na propriedade “Store.DataSourceID“. Cada Store só pode ter um “datasource” definido.
Para saber mais: http://examples.ext.net/#/search/datasource

No exemplo abaixo iremos ver o “ObjectDataSource” em ação.

ASPX Code

<body>
<form id="Form1" runat="server">
<ext:ResourceManager ID="ResourceManager1" runat="server" />
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="Find" TypeName="Ext.Net.Tutorial.Data.Order" />
<ext:Window ID="Window1" runat="server" Collapsible="true" Maximizable="true" Icon="Money"
	Title="Orders List" Width="1300" Height="600" X="50" Y="50" Layout="Fit" Closable="false">
	<Items>
		<ext:GridPanel ID="GridPanel1" runat="server" Title="Employees" Frame="true" Header="false"
			Border="false">
			<Store>
				<ext:Store ID="Store1" runat="server" DataSourceID="ObjectDataSource1">
					<Model>
						<ext:Model ID="Model1" runat="server">
							<Fields>
								<ext:ModelField Name="CustomerID" />
								<ext:ModelField Name="EmployeeID" />
								<ext:ModelField Name="OrderDate" />
								<ext:ModelField Name="RequiredDate" />
								<ext:ModelField Name="ShippedDate" />
								<ext:ModelField Name="ShipVia" />
								<ext:ModelField Name="Freight" />
								<ext:ModelField Name="ShipName" />
								<ext:ModelField Name="ShipAddress" />
								<ext:ModelField Name="ShipCity" />
								<ext:ModelField Name="ShipRegion" />
								<ext:ModelField Name="ShipPostalCode" />
								<ext:ModelField Name="ShipCountry" />
							</Fields>
						</ext:Model>
					</Model>
				</ext:Store>
			</Store>
			<ColumnModel ID="ColumnModel1" runat="server">
				<Columns>
					<ext:Column ID="Column0" runat="server" Text="CustomerID" Width="150" DataIndex="CustomerID">
					</ext:Column>
					<ext:Column ID="Column1" runat="server" DataIndex="EmployeeID" Text="EmployeeID"
						Width="150" />
					<ext:DateColumn ID="Column2" runat="server" DataIndex="OrderDate" Text="Order Date"
						Width="150" Format="dd/MM/yyyy" />
					<ext:DateColumn ID="DateColumn1" runat="server" DataIndex="RequiredDate" Text="Required Date"
						Width="110" Format="dd/MM/yyyy" />
					<ext:DateColumn ID="DateColumn2" runat="server" DataIndex="ShippedDate" Text="Shipped Date"
						Width="110" Format="dd/MM/yyyy" />
					<ext:Column ID="Column3" runat="server" DataIndex="ShipVia" Text="Ship Via" Width="150" />
					<ext:Column ID="Column4" runat="server" DataIndex="Freight" Text="Freight" Width="100" />
				</Columns>
			</ColumnModel>
			<View>
				<ext:GridView ID="GridView1" runat="server">
					<GetRowClass Handler="return 'x-grid-row-expanded';" />
				</ext:GridView>
			</View>
			<SelectionModel>
				<ext:RowSelectionModel ID="RowSelectionModel1" runat="server" Mode="Single" />
			</SelectionModel>
			<Features>
				<ext:RowBody ID="RowBody1" runat="server">
					<GetAdditionalData Handler="orig.rowBody = '<div><span style=\'color: red\'>Ship Information</span>' + '</br/>' + data.ShipName + '</br/>' + data.ShipAddress + '</br/>' + data.ShipCity + (data.ShipRegion == null ? '' : ' - ' + data.ShipRegion) + '</br/>' + (data.ShipPostalCode == null ? '' : data.ShipPostalCode + '</br/>') + data.ShipCountry + '</div>'; orig.rowBodyColspan = record.fields.getCount();" />
				</ext:RowBody>
			</Features>
		</ext:GridPanel>
	</Items>
</ext:Window>
</form>
</body>

No código em destaque, acima, definimos o “ObjectDataSource” para o nosso “Store“, vamos explicar algumas propriedades:

  • ID: “ObjectDataSource1”: Define o nome do objeto “DataSource” que deverá ser utilizado no “Store“;
  • TypeName: “Ext.Net.Tutorial.Data.Order”: Define o nome do objeto que será instanciado pelo “DataSource”;
  • SelectMethod: “Find”: Nome do método que será chamado para popular o “DataSource”;

Nas outras linhas em destaque, como sempre, definimos o nosso “Model” e já estamos familiarizados com ele.

Utilizando um Proxy

Até que enfim… Falamos tanto do acesso aos dados, de diversas formas, que já estava esquecendo deste tal “proxy“. Vamos ver como trabalhar com o proxy. Para isso, iremos criar um “ASPX Hanlder“.

question Handler? O que é isso?

R: Os Handlers, são manipuladores responsáveis por tratar as requisições HTTP, não têm interface com usuário e são executados de forma síncrona. Devem implementar a interface “System.Web.IHttpHandler“. Um exemplo de requisição que implementa a interface “System.Web.IHttpHandler” é a “System.Web.UI.Page“, que deve ser herdada para criar os formulários em ASPX. (Web Forms). Esta é quem define toda a interatividade com o usuário. OS “Handlers” são úteis quando precisamos de acessar dados do servidor, sem a intervenção do usuário.

Preparando o ambiente.

Antes de começarmos, iremos criar 3 “ASPX Handlers”:

  1. HandlerBase<T>: Abstração para o tratamento dos dados, pois o código de tratamento é semelhante para todos os outros “handlers” que tratam o acesso aos dados.
  2. EmployeeHandler: É o “handler” responsável por exibir os dados dos colaboradores;
  3. OrderHandler: É o “handler” responsável por exibir os dados dos pedidos;

Abstração HandlerBase

public abstract class HandlerBase<T>: System.Web.IHttpHandler
	where T: IModelBase, new()
{
	public void ProcessRequest(HttpContext context)
	{
		//-------------------------------------------------------------------------
		// Definir o tipo de retorno da resposta da requisição.
		// Iremos retornar um objeto do tipo JSON
		//-------------------------------------------------------------------------
		context.Response.ContentType = "application/json";

		//-------------------------------------------------------------------------
		// Recuperar os parâmetros que foram passados pela requisição
		//-------------------------------------------------------------------------
		StoreRequestParameters storeParams = new StoreRequestParameters(context);

		//-------------------------------------------------------------------------
		// Paginar
		//-------------------------------------------------------------------------
		Paging<T> data = DataPaging(storeParams.Start, storeParams.Limit,
			storeParams.Sort.Count() > 0 ? storeParams.Sort[0].Property : "",
			storeParams.Sort.Count() > 0 ? storeParams.Sort[0].Direction.ToString() : "",
			storeParams.GridFilters);
		context.Response.Write(JSON.Serialize(data));
	}

	public bool IsReusable
	{
		get
		{
			return false;
		}
	}

	public static Paging<T> DataPaging(int start, int limit, string sort, string dir, FilterConditions fc)
	{
		List<T> data = DbContext.Find<T>(new T());

		#region Filtrar
		//-------------------------------------------------------------------------
		// Se foi definido alguma condição, temos que filtrar os registros
		//-------------------------------------------------------------------------
		if(fc != null && fc.Conditions.Count > 0)
		{
			foreach(FilterCondition condition in fc.Conditions)
			{
				Comparison comparison = condition.Comparison;
				string field = condition.Field;
				FilterType type = condition.Type;

				object value;
				switch(condition.Type)
				{
					case FilterType.Boolean:
						value = condition.Value<bool>();
						break;
					case FilterType.Date:
						value = condition.Value<DateTime>();
						break;
					case FilterType.List:
						value = condition.List;
						break;
					case FilterType.Numeric:
						if(data.Count > 0 && data[0].GetType().GetProperty(field).PropertyType == typeof(int))
						{
							value = condition.Value<int>();
						}
						else
						{
							value = condition.Value<double>();
						}

						break;
					case FilterType.String:
						value = condition.Value<string>();
						break;
					default:
						throw new ArgumentOutOfRangeException();
				}

				//-------------------------------------------------------------------------
				// Aqui removemos todos os registros que não satisfazem a condição
				//-------------------------------------------------------------------------
				data.RemoveAll(
					item =>
					{
						object oValue = item.GetType().GetProperty(field).GetValue(item, null);
						IComparable cItem = oValue as IComparable;

						switch(comparison)
						{
							case Comparison.Eq:

								switch(type)
								{
									case FilterType.List:
										return !(value as List<string>).Contains(oValue.ToString());
									case FilterType.String:
										return !oValue.ToString().ToLower().Contains(value.ToString().ToLower());
									default:
										return !cItem.Equals(value);
								}

							case Comparison.Gt:
								return cItem.CompareTo(value) < 1;
							case Comparison.Lt:
								return cItem.CompareTo(value) > -1;
							default:
								throw new ArgumentOutOfRangeException();
						}
					}
				);
			}

		}
		#endregion

		#region Ordenar
		//-------------------------------------------------------------------------
		// Ordena os registros de acordo com o que foi passado pelo usuário
		//-------------------------------------------------------------------------
		if(!string.IsNullOrEmpty(sort))
		{
			data.Sort(delegate(T x, T y)
			{
				object a;
				object b;

				int direction = dir == "DESC" ? -1 : 1;

				a = x.GetType().GetProperty(sort).GetValue(x, null);
				b = y.GetType().GetProperty(sort).GetValue(y, null);

				return CaseInsensitiveComparer.Default.Compare(a, b) * direction;
			});
		}
		#endregion

		#region Paginar
		//-------------------------------------------------------------------------
		// Faz a paginação do registro.
		//-------------------------------------------------------------------------
		if((start + limit) > data.Count)
		{
			limit = data.Count - start;
		}

		List<T> rangeData = (start < 0 || limit < 0) ? data : data.GetRange(start, limit);
		#endregion

		//retornar
		return new Paging<T>(rangeData, data.Count);
	}
}

Exemplos de acesso

Classe concreta EmployeeHandler

/// <summary>
/// Summary description for EmployeeHandler
/// </summary>
public class EmployeeHandler: HandlerBase<Employee>
{
}

Classe concreta OrderHandler

/// <summary>
/// Summary description for GridPanelHandler
/// </summary>
public class OrderHandler: HandlerBase<Order>
{
}

Estamos quase lá… Vamos ver agora dois exemplos, uma “Combobox” e uma “GridPanel” acessando os dados por um “proxy” e utilizando um “handler” para tratar os dados.

ASPX Code

<ext:ComboBox ID="ComboBox5" runat="server" FieldLabel="Selecione vários colaboradores"
	DisplayField="FirstName" Width="320" LabelWidth="130" QueryMode="Local" TypeAhead="true"
	MultiSelect="true">
	<Store>
		<ext:Store ID="Store5" runat="server" AutoDataBind="true">
			<Proxy>
				<ext:AjaxProxy Url="../DataHandler/EmployeeHandler.ashx">
					<ActionMethods Read="GET" />
					<Reader>
						<ext:JsonReader Root="data" TotalProperty="total" />
					</Reader>
				</ext:AjaxProxy>
			</Proxy>
			<Model>
				<ext:Model ID="Model5" runat="server">
					<Fields>
						<ext:ModelField Name="EmployeeID" />
						<ext:ModelField Name="FirstName" />
						<ext:ModelField Name="LastName" />
					</Fields>
				</ext:Model>
			</Model>
		</ext:Store>
	</Store>
	<ListConfig>
		<ItemTpl ID="ItemTpl2" runat="server">
			<Html>
				<div class="list-item">
							<h3>ID: {EmployeeID}</h3>
							{FirstName} {LastName}
					</div>
			</Html>
		</ItemTpl>
	</ListConfig>
</ext:ComboBox>

GridPanel

ASPX Code

<head id="Head1" runat="server">
    <title>Tutorial Ext.NET</title>
    <link href="../css/gridpanel.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="../Scripts/GridPanel.js"></script>
</head>
<body>
    <form id="Form1" runat="server">
    <ext:ResourceManager ID="ResourceManager1" runat="server" />
    <ext:Window ID="Window1" runat="server" Collapsible="true" Maximizable="true" Icon="Money"
        Title="Orders List" Width="1300" Height="600" X="50" Y="50" Layout="Fit" Closable="false">
        <Items>
            <ext:GridPanel ID="GridPanel1" runat="server" Title="Employees" Frame="true" Header="false"
                Border="false">
                <Store>
                    <ext:Store ID="Store1" runat="server" PageSize="10">
                        <Proxy>
                            <ext:AjaxProxy Url="../DataHandler/OrderHandler.ashx">
                                <ActionMethods Read="GET" />
                                <Reader>
                                    <ext:JsonReader Root="data" TotalProperty="total" />
                                </Reader>
                            </ext:AjaxProxy>
                        </Proxy>
                        <Model>
                            <ext:Model ID="Model1" runat="server">
                                <Fields>
                                    <ext:ModelField Name="CustomerID" />
                                    <ext:ModelField Name="EmployeeID" />
                                    <ext:ModelField Name="OrderDate" />
                                    <ext:ModelField Name="RequiredDate" />
                                    <ext:ModelField Name="ShippedDate" />
                                    <ext:ModelField Name="ShipVia" />
                                    <ext:ModelField Name="Freight" />
                                    <ext:ModelField Name="ShipName" />
                                    <ext:ModelField Name="ShipAddress" />
                                    <ext:ModelField Name="ShipCity" />
                                    <ext:ModelField Name="ShipRegion" />
                                    <ext:ModelField Name="ShipPostalCode" />
                                    <ext:ModelField Name="ShipCountry" />
                                </Fields>
                            </ext:Model>
                        </Model>
                        <Sorters>
                            <ext:DataSorter Property="CustomerID" Direction="ASC" />
                        </Sorters>
                    </ext:Store>
                </Store>
                <ColumnModel ID="ColumnModel1" runat="server">
                    <Columns>
                        <ext:Column ID="Column0" runat="server" Text="CustomerID" Width="150" DataIndex="CustomerID">
                        </ext:Column>
                        <ext:Column ID="Column1" runat="server" DataIndex="EmployeeID" Text="EmployeeID"
                            Width="150" />
                        <ext:DateColumn ID="Column2" runat="server" DataIndex="OrderDate" Text="Order Date"
                            Width="150" Format="dd/MM/yyyy" />
                        <ext:DateColumn ID="DateColumn1" runat="server" DataIndex="RequiredDate" Text="Required Date"
                            Width="110" Format="dd/MM/yyyy" />
                        <ext:DateColumn ID="DateColumn2" runat="server" DataIndex="ShippedDate" Text="Shipped Date"
                            Width="110" Format="dd/MM/yyyy" />
                        <ext:Column ID="Column3" runat="server" DataIndex="ShipVia" Text="Ship Via" Width="150" />
                        <ext:Column ID="Column4" runat="server" DataIndex="Freight" Text="Freight" Width="100" />
                    </Columns>
                </ColumnModel>
                <View>
                    <ext:GridView ID="GridView1" runat="server">
                        <GetRowClass Handler="return 'x-grid-row-expanded';" />
                    </ext:GridView>
                </View>
                <SelectionModel>
                    <ext:RowSelectionModel ID="RowSelectionModel1" runat="server" Mode="Single" />
                </SelectionModel>
                <Features>
                    <ext:RowBody ID="RowBody1" runat="server">
                        <GetAdditionalData Handler="orig.rowBody = '<div><span style=\'color: red\'>Ship Information</span>' + '</br/>' + data.ShipName + '</br/>' + data.ShipAddress + '</br/>' + data.ShipCity + (data.ShipRegion == null ? '' : ' - ' + data.ShipRegion) + '</br/>' + (data.ShipPostalCode == null ? '' : data.ShipPostalCode + '</br/>') + data.ShipCountry + '</div>'; orig.rowBodyColspan = record.fields.getCount();" />
                    </ext:RowBody>
                </Features>
                <BottomBar>
                    <ext:PagingToolbar ID="PagingToolbar1" runat="server">
                        <Items>
                            <ext:Label ID="Label1" runat="server" Text="Page size:" />
                            <ext:ToolbarSpacer ID="ToolbarSpacer1" runat="server" Width="10" />
                            <ext:ComboBox ID="ComboBox1" runat="server" Width="80">
                                <Items>
                                    <ext:ListItem Text="1" />
                                    <ext:ListItem Text="2" />
                                    <ext:ListItem Text="10" />
                                    <ext:ListItem Text="20" />
                                    <ext:ListItem Text="40" />
                                    <ext:ListItem Text="100" />
                                </Items>
                                <SelectedItems>
                                    <ext:ListItem Value="10" />
                                </SelectedItems>
                                <Listeners>
                                    <Select Handler="#{GridPanel1}.store.pageSize = parseInt(this.getValue(), 10); #{GridPanel1}.store.reload();" />
                                </Listeners>
                            </ext:ComboBox>
                        </Items>
                        <Plugins>
                            <ext:ProgressBarPager ID="ProgressBarPager1" runat="server" />
                        </Plugins>
                    </ext:PagingToolbar>
                </BottomBar>
                <Features>
                    <ext:GridFilters ID="GridFilters1" runat="server">
                        <Filters>
                            <ext:StringFilter DataIndex="CustomerID" />
                            <ext:NumericFilter DataIndex="EmployeeID" />
                            <ext:DateFilter DataIndex="OrderDate">
                                <DatePickerOptions runat="server" TodayText="Now" />
                            </ext:DateFilter>
                            <ext:DateFilter DataIndex="RequiredDate">
                                <DatePickerOptions runat="server" TodayText="Now" />
                            </ext:DateFilter>
                            <ext:DateFilter DataIndex="ShippedDate">
                                <DatePickerOptions runat="server" TodayText="Now" />
                            </ext:DateFilter>
                            <ext:NumericFilter DataIndex="ShipVia" />
                            <ext:NumericFilter DataIndex="Freight" />
                        </Filters>
                    </ext:GridFilters>
                </Features>
            </ext:GridPanel>
        </Items>
    </ext:Window>
    </form>
</body>
</html>

Vejam nos códigos em destaque a definição do proxy, vamos agora explicar o que é cada propriedade que foi utilizada.

  • <Proxy>“: Usamos esta marcação para definir que neste “Store” será usado um “proxy“.
  • <ext:AjaxProxy Url=”../DataHandler/OrderHandler.ashx”>“: Esta marcação define o tipo de “proxy” que iremos usar. Eu utilizo, na maioria das vezes, o “AjaxProxy“, sempre me atendeu. Na definição desta marcação temos a propriedade  “Url” e como podem ver, ela define o caminho do nosso “handler” em relação à página;
  • <ActionMethods Read=”GET” />“: Aqui definimos a ação da requisição como um “GET“, pois vamos solicitar uma informação;
  • <ext:JsonReader Root=”data” TotalProperty=”total” />“: Nesta marcação, definimos o nosso “Reader“, nosso leitor da informação, como eu defini que meu retorno no “handler” será um “JSON” .
    • Lembram da definição do “handler“? context.Response.ContentType = “application/json;

    • Esta marcação tem duas propriedades importantes:
      • Root“, que define o nome da marcação pai de todas, no objeto “JSON” retornado e;
      • TotalProperty“, que define o nome da propriedade que representa o total de registros retornados pela aplicação. Veja abaixo um retorno para facilitar o entendimento destas duas propriedades
        {
         "data": [{
         "ID": 1,
         "EmployeeID": 1,
         "LastName": "Davolio",
         "FirstName": "Nancy",
         "Title": "Sales Representative",
         "TitleOfCourtesy": "Ms.",
         "BirthDate": "1948-12-08T00:00:00",
         "HireDate": "1992-05-01T00:00:00",
         "Address": "507 - 20th Ave. E.Apt. 2A",
         "City": "Seattle",
         "Region": "WA",
         "PostalCode": "98122",
         "Country": "USA",
         "HomePhone": "(206) 555-9857",
         "Extension": "5467",
         "Photo": "System.Byte[]",
         "Notes": "Education includes a BA in psychology from Colorado State University in 1970. 
                   She also completed \"The Art of the Cold Call.\" 
                   Nancy is a member of Toastmasters International.",
         "ReportsTo": 2,
         "PhotoPath": "http://accweb/emmployees/davolio.bmp",
         "New": false
         }],
         "total": 1
        }
whew Ufa! Este tópico foi demorado, mas valeu a pena. Aprendemos nele como acessar dados utilizando “Store“, ‘Models“… Definimos “DataSources“, “Proxies” entre outras funcionalidades.E ainda de quebra conhecemos dois componentes, “ComboBox” e “GridPanel“.

É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Listeners_Eventos

Para um melhor entendimento deste Artigo veja o Índice (Rich Internet Applications com Ext.Net)

Listeners – Eventos lado cliente

Para facilitar o entendimento do artigo, vamos primeiro definir o que são listeners e eventos.

Listeners

Listeners são objetos ouvintes que notificam outros objetos quando acontece alguma mudança no comportamento do objeto. Os listeners utilizam o Design Pattern Observer.

Eventos

Os eventos são os tipos mais comuns que utilizam um listener, pois são neles que definimos quais métodos  serão chamados (Alguém disse delegates?) quando uma ação ocorrer.

Eventos são lançados sempre que uma ação acontece no nosso objeto, seja motivada pelo usuário, pelo próprio sistema ou por mensagens do sistema operacional.

Eventos no Ext.NET

O Ext.NET suporta eventos do lado cliente, AJAX e as formas tradicionais de tratamento de eventos.
Cada componente tem seus eventos, lado cliente, definidos dentro da tag “listeners“.

Como exemplo, vamos pegar um simples clique de botão e exibir uma mensagem.

<body>
    <ext:ResourceManager ID="ResourceManager1" runat="server">
    </ext:ResourceManager>
    <form id="form1" runat="server">
    <ext:Button ID="Button1" runat="server" Icon="Accept" Text="Clica em mim.">
        <Listeners>
            <Click Handler="Ext.Msg.show({
                                                   title: 'Oi!',
                                                   msg: 'Oba! Fui clicado.',
                                                   buttons: Ext.Msg.OK,
                                                   icon: Ext.Msg.WARNING
                                               });">
            </Click>
        </Listeners>
    </ext:Button>
    </form>
</body>

Neste pequeno trecho de código podemos dizer que tratamos o evento clique de um botão qualquer.

Uma outra forma de tratarmos o evento, seria criando uma função em javascript.

note-taking Eu, particularmente, prefiro esta segunda abordagem, pois assim deixo todos os meus fontes separados entre as classes (.cs), o código html (.aspx) e o código de script (.js).

Para este exemplo, crie um arquivo com o nome de “MyApp.js” no seu projeto e cole o código abaixo:

/**
 Arquivo de definição dos objetos em javascript
 */

// definir o namespace de base para a aplicação
var MyApp = {};

/**
 Define o método listener para o clique do botão
 */
MyApp.Button1Click = function() {
    Ext.Msg.show({
        title : 'Oi!',
        msg : 'Oba! Fui clicado em uma função.',
        buttons : Ext.Msg.OK,
        icon : Ext.Msg.WARNING
    });
};

Agora para a chamada do método clique, iremos definir um ponteiro para uma função em javascript.

<ext:Button ID="Button2" runat="server" Icon="Accept" Text="Clica em mim.">
        <Listeners>
            <Click Fn="MyApp.Button1Click">
            </Click>
        </Listeners>
</ext:Button>

Neste ponto podemos perceber que temos dois meios de chamar um método para o evento “Click“, no primeiro utilizamos um manipulador (handler) diretamente na definição do evento. E no segundo método, definimos um ponteiro (pointer) para uma função definida em nosso arquivo de script.

Esta é a diferença entre usar “Handler” e “Fn“.

Se o evento só deve ser executado uma única vez, podemos definir a propriedade “Single” para “true” e o evento só será executado uma vez, até que a página seja recarregada.

Se for necessário definir o escopo em que o evento deverá ser executado, devemos utilizar a propriedade “scope” e passar o nome do objeto contêiner onde o evento será executado. Se nada for informado, será utilizado o escopo global.

Determinando os argumentos do evento

Usando a documentação do Ext JS você poderá encontrar os argumentos que são passados para cada evento. Tenha em mente que diferentes controles têm diferentes eventos e assinaturas de evento.

Para uma documentação completa do lado cliente veja o link http://docs-origin.sencha.com/extjs/4.1.3.

DirectEvents (Eventos do Lado Servidor)

Os “DirectEvents” são eventos que são lançados pelo lado cliente e que podem ser capturados pelo lado servidor.

Tenha em mente que quando um “DirectEvent” é lançado, todo o controle deverá ser reconstruído do lado servidor, como é feito em um post tradicional. Se o seu form tem o atributo “runat=’server’” todo o formulário será reenviado por padrão.

Isto pode nos causar alguns problemas e para validar se devemos recriar e evitar vários códigos de inicialização da nossa página, podemos usar a propriedade “X.IsAjaxRequest”,  se true, a requisição veio de um evento AJAX.  Esta propriedade funciona como a propriedade do ASP.NET “IsPostBack“.

Vamos ver 3 exemplos de chamadas “DirectEvents” que são usados com muito frequência.

Sem parâmetros

ASPX Code

<ext:Button ID="Button1" runat="server" Icon="Clock" Text="Que horas são?"><DirectEvents>
            <Click OnEvent="Button1Click">
            </Click>
        </DirectEvents>
    </ext:Button>

Code Behind

protected void Button1Click(object sender, DirectEventArgs e)
        {
            X.Msg.Show(new MessageBoxConfig
            {
                Buttons = MessageBox.Button.OK,
                Title = "Olá!",
                Message = String.Format("São {0}.", DateTime.Now.ToLongTimeString())
            });
        }

Com parâmetros

Para definir parâmetros, temos que utilizar a marcação “ExtraParams” que possuem 3 propriedades importantes:

  1. Mode:
    1. Pode ser “Raw“: Define que o conteúdo da propriedade  “Value” deverá ser executado antes de ser enviado ao servidor;
    2. Poder ser “Value“: Define que o conteúdo da propriedade “Value” deverá ser retornado como especificado;
  2. Name: Nome do parâmetro que será enviado ao servidor, pode ser passado mais de um item de parâmetro, este nome será o índice que iremos recuperar o valor do parâmetro no servidor;
  3. Value:  Valor que deverá ser passado ao servidor, atente para o item 1. Mode, que define como este valor deverá ser interpretado;

ASPX Code

<ext:TextField ID="TextField1" runat="server" FieldLabel="Informe seu nome.">
    </ext:TextField>
    <ext:Button ID="Button2" runat="server" Icon="Clock" Text="Que horas são?">
        <DirectEvents>
            <Click OnEvent="Button2Click">
                <ExtraParams>
                    <ext:Parameter Mode="Raw" Value="#{TextField1}.value" Name="nome">
                    </ext:Parameter>
                </ExtraParams>
            </Click>
        </DirectEvents>
    </ext:Button>

Code Behind

protected void Button2Click(object sender, DirectEventArgs e)
        {
            //Perceba que pegamos aqui pelo nome do extra params que informamos no ASPX Code
            string nome = e.ExtraParams["nome"];

            X.Msg.Show(new MessageBoxConfig
            {
                Buttons = MessageBox.Button.OK,
                Title = String.Format("Olá {0}!", nome),
                Message = String.Format("São {0}.", DateTime.Now.ToLongTimeString())
            });
        }

Com parâmetros de um objeto definido

Com esta opção, podemos criar um objeto do lado cliente e passar este objeto para o lado servidor.
Do lado servidor o mesmo deverá ser recriado para um objeto compreendido pelo C#.

Para este exemplo, iremos criar um arquivo de javascript com o nome de  DirectEvent.js com o seguinte código:

/**
 Arquivo de definição dos objetos em javascript
 */

// definir o namespace de base para a aplicação
var MyApp = {};

/**
 Define o método listener para o clique do botão
 */
MyApp.Button3Click = function() {
    //Aqui iremos criar um objeto do tipo 'MyInfo', esperado pelo lado servidor
    var result = {
        Nome : Ext.getCmp('TextField2').value,
        Email : Ext.getCmp('TextField3').value
    };

    return result;
};

ASPX Code

<script type="text/javascript" src="../Scripts/DirectEvent.js"> </script>
    <ext:TextField ID="TextField2" runat="server" FieldLabel="Informe seu nome">
    </ext:TextField>
    <ext:TextField ID="TextField3" runat="server" FieldLabel="Informe seu email">
    </ext:TextField>
    <ext:Button ID="Button3" runat="server" Icon="Clock" Text="Enviar">
        <DirectEvents>
            <Click OnEvent="Button3Click">
                <ExtraParams>
                    <ext:Parameter Mode="Raw" Value="MyApp.Button3Click()" Name="dados">
                    </ext:Parameter>
                </ExtraParams>
            </Click>
        </DirectEvents>
    </ext:Button>

Code Behind

Definição do objeto que o lado cliente deverá retornar

/// <summary>
/// Objeto retornado pelo cliente
/// </summary>
struct MeusDados
{
    public string Nome { get; set; }
    public string Email { get; set; }
}

Método que irá receber a informação do lado cliente e converter para o objeto esperado

protected void Button3Click(object sender, DirectEventArgs e)
{
    // Aqui iremos converter o objeto que recebemos do lado cliente
   // em um objeto do tipo "MyInfo"
    MyInfo dados = JSON.Deserialize<MyInfo>(e.ExtraParams["dados"]);
    X.Msg.Show(new MessageBoxConfig
    {
        Buttons = MessageBox.Button.OK,
        Title = String.Format("Olá {0}!", dados.Nome),
        Message = String.Format("Seu e-mail é  {0}.", dados.Email)
    });
}

Neste tópico não iremos abordar como criar  o nosso próprio manipulador de eventos. Este será abordado no tópico “Criando componentes no lado Servidor


É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Apresentando o Ext.Net

0
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (1 votos, média: 5,00 de 5)
Loading...
5 de maio de 2014

Apresentando_Ext.Net

Para um melhor entendimento deste Artigo veja o Índice (Rich Internet Applications com Ext.Net)

O que é Ext.NET?

Ext.NET é um framework de código fonte aberto, de componentes  ASP.NET  (WebForm + MVC) integrado ao framework Ext JS.

O site oficial Ext.NET é:

Site oficial do Sencha:

Ext.NET  (pronuncia-se -eee-ecks-T dot NET), eu pronuncio “EXT.NET” mesmo 🙂 .

Em outras palavras, é um conjunto de controles e classes, que geram JavaScript , HTML e CSS sempre que necessário, e, que este JavaScript tem como base a estrutura do Ext JS.

O Ext.NET também inclui componentes que não são encontrados em Ext JS, estende várias classes, sempre que necessário, proporcionando assim sua própria camada JavaScript.

O Ext.NET é uma abstração do Ext JS. Esta abstração não é uma camada fraca ou
restritiva, se assim desejar, você pode escrever diretamente na camada Ext JS baseada em JavaScript.

note-taking Eu particularmente escrevo todos os componentes do lado servidor quando preciso de um novo.
Tenho esta visão devido à facilidade que é oferecida pelo Ext.Net no lado servidor.

Uma outra forma de ver, é pensar no Ext.NET como uma ponte entre Ext JS no
lado cliente e ASP.NET no lado servidor.

A relação com ASP.NET

note-taking A escolha da linha de desenvolvimento, WebForms ou MVC não é importante para este artigo como um todo. Mas toda a apresentação será feita em WebForms.

Se você está acostumado com a criação de aplicações ASP.NET usando controles que seguem o padrão ASP.NET, que mantêm seu estado no ViewState, o Ext.NET pode parecer familiar, mas com muitas melhorias:

  • O “ViewState” não é requerido pelo Ext.NET, podemos desligar e economizaremos uma quantidade de bytes para a aplicação cliente.
    • A não ser que você esteja usando “FormAuthentication”;
  • Uma aplicação ASP.NET Web Forms requer um único “<runat = “server” >” e já adicionará todos os eventos, métodos e propriedades e assim por diante ao componente. Com Ext.NET isto é opcional como será demonstrado;
  • Ao desenvolver aplicações ASP.NET, os controles são adicionados na coleção “Controls” do componente recipiente. Com Ext.NET, você normalmente adiciona componente filhos ao componente pai, usando a coleção “Items”. Tenha isso em mente, irá nos ajudar nos próximos capítulos;

Vamos nos concentrar um pouco no ViewState.

Em um desenvolvimento ASP tradicional, WebForms, o ViewState e o modelo PostBack são pontos chaves para recriar os controles complexos no estado em que o desenvolvedor e usuário espera que eles existam.

São feitos vários posts e esperado vários retornos; “response”; para as páginas que trafegam este “ViewState”.

Com Ext.NET, criamos uma interface rica, onde o estado dos controles não precisam ser mantidos no lado servidor.

As aplicações desenvolvidas utilizando Ext.Net utilizam-se da tecnologia AJAX, o que significa que podemos recarregar apenas o ponto em questão da página, ou fazer uma consulta sem modificar nada na página.

Exemplo:

Para validar algo no banco de dados e exibir uma mensagem ao usuário.

Para o lado cliente, o Ext.NET framework lida com toda a complexidade da manutenção dos controles, como a instanciação, destruição, layout e redesenho.

Mas, se mesmo assim você ainda precisar de mais, a API do Ext.NET é bastante flexível para que você possa colocar a mão na massa e desenvolver seu próprio componente, como será visto no artigo “Criando componentes no lado Servidor“.


É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Embedded Javascript

0
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (2 votos, média: 5,00 de 5)
Loading...
5 de novembro de 2011

embedded-javascript

Introdução

Neste artigo irei ensinar uma técnica em asp.net para embutir javascript em uma página HTML como um recurso embutido (embedded resource), utilizando nossos próprios componentes.

Muitas vezes precisamos escrever nossos controles em asp, e quando vamos disponibilizar nossa biblioteca de controles nos deparamos com a necessidade de enviar todo o código que foi feito em javascript e nossos controles compilados  não são instalados corretamente, algum diretório da nossa estrutura é modificado pelo usuário, algum diretório não é criado, logo, nossos controles deixam de funcionar como gostaríamos por causa de uma instalação errada ou uma permissão de arquivo.

Um recurso interessante que vejo nos controles asp é a possibilidade de embutirmos todo o nosso código javascript dentro da DLL do controle, isso mesmo, compilar e embutir o nosso javascript e depois utilizar ele com um recurso não mais precisando fazer a criação de diretórios, ou distribuir nossos arquivos .js junto com nosso controle.

O que é Recurso Embutido (Embedded Resource)?

Recurso embutido é quando inserimos algum arquivo dentro de nossa aplicação e o mesmo é compilado e enviado junto com a aplicação dentro do mesmo arquivo, seja um .exe ou .DLL.

Exemplos práticos de arquivos embutidos são quando criamos aplicações Windows no Visual Studio, se repararmos quando colocamos uma imagem em um “form” é criado um arquivo com a extensão .resx, este arquivo é usado para embutir o recurso, no caso uma imagem.

Outro arquivo que o Visual Studio cria é o “Resources.resx” este arquivo é criado quando inserimos imagens, textos, áudio, qualquer arquivo que iremos usar como um recurso em nossa aplicação.

Em asp isso também é possível, neste arquivo irei ensinar como fazer com javascript, mas isso não quer dizer que é possível apenas com javascript, pode ser utilizado para css, imagens e até mesmo páginas inteiras. Mas o nosso foco é javascript.

O que veremos?

Iremos ver desde o modo mais simples de embutir um recurso:

Até o tratamento com o nosso próprio handler.

Iremos ver aqui também como registrar este recurso dentro da tag HEAD e não na tag BODY

Ok! Chega de lero-lero e vamos ao que interessa, se você chegou até aqui é porque já está habituado com o visual Studio. Vou assumir que você tem conhecimento em desenvolvimento web.

Iniciando

Para o nosso exemplo iremos criar dois projetos, uma aplicação web e uma aplicação de controle.

Passos:

  1. Abra o Visual Studio  e crie uma nova aplicação web (ASP.Net Web Application), a minha eu chamei de EmbeddingJavascript;
  2. Adicione um segundo novo projeto à sua solução, do tipo “ASP.Net Server Control” o meu eu chamei de EmbeddingJavascriptMyControl.
  3. No projeto “EmbeddingJavascriptMyControl” adicione um novo item do tipo “ASP.Net Server Control” e de o nome de “MyControl”

Agora já temos dois projetos em nossa solução veja se a sua está parecida com a minha:

Agora vamos à montagem dos códigos e configurações necessárias para fazer o nosso script embutido.

Abra sua classe “MyControl.cs” no método RenderContents iremos criar o nosso componente, um componente simples que irá pedir um texto qualquer com um botão para ser clicado.

Veja o código

protected override void RenderContents(HtmlTextWriter output)
{
string html = @"Digite algo:
				<input type='text' id='txtAlgo' />
				<input type='button' value='Clique aqui' onclick='CallAlert(txtAlgo.value);' />";

output.Write(html);
}

Reparem na linha cinco do código que temos um onclick=’CallAlert(txtNome.value);’ para não dar erro em nossa aplicação temos que definir o script e criar a função js CallAlert.

Então mãos a obra.

No projeto “EmbeddingJavascriptMyControl”  Adicione um novo item do tipo “JScript File” o meu eu chamei de “MyControl.js” nele iremos digitar o nosso código para a função CallAlert

/**
* Exibe uma mensagem de alerta
* @param m (string) mensagem a ser exibida
*/
function CallAlert(m)
{
    if(m=='')
        alert("Informe a mensagem a ser exibida");
    else
        alert(m);
} 

Feito isso voltaremos ao nosso projeto “EmbeddingJavascript”, siga os passos descritos para colocar o nosso componente na página Default.aspx

  1. Faça a referencia ao projeto “EmbeddingJavascriptMyControl”;
  2. Feito a referencia abra o arquivo Default.aspx, nele iremos colocar uma diretiva para que possamos ter o nosso controle na página.
<%@ Register Assembly="EmbeddingJavascriptMyControl" Namespace="EmbeddingJavascriptMyControl"
    TagPrefix="myctl" %>

Repare na propriedade TagPrefix, é por ela que iremos acessar os nossos controles criados no namespace que foi carregado pela propriedade Namespace, é uma atalho, para não ter que ficar digitando todo o namespace.
Dentro da tag body iremos declarar agora nosso controle MyControl, para isso temos que usar a tag “myctl”, veja a declaração:

<myctl:MyControl runat="server" ID="myControl" />

Código completo da página Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="EmbeddingJavascript._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ Register Assembly="EmbeddingJavascriptMyControl" Namespace="EmbeddingJavascriptMyControl"
    TagPrefix="myctl" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Acessando recursos embutidos</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <myctl:MyControl runat="server" ID="myControl" />
    </div>
    </form>
</body>
</html>

Ok! Vamos agora executar nossa aplicação e clicar no botão que será criado no navegador.

🙁 Puxa. Primeiro erro, “CallAlert não está definido”.

Obvio, ainda não o definimos. Vamos fazer algumas configurações para que o nosso arquivo javascript seja carregado de forma correta.

Voltaremos ao nosso projeto “EmbeddingJavascriptMyControl” selecione o arquivo “MyControl.js” botão direito, “Propriedades” ou “Properties” na janela que abriu, em “Build Action” marque “Embedded Resource

Agora vamos executar nossa aplicação e clicar no botão novamente… e … erro de novo, o mesmo erro  “CallAlert não está definido”. Isso acontece porque não basta marcar como “Embedded Resource”, ainda temos mais duas alterações a fazer para que nosso recurso funcione. Então vamos a elas.

Primeira Alteração

Abra o arquivo AssemblyInfo.cs e nele digite a seguinte linha

 [assembly: System.Web.UI.WebResource("EmbeddingJavascriptMyControl.MyControl.js", "application/x-javascript")]

Esta linha dirá ao Assembly onde está o recurso embutido, mas muita atenção aqui a dois pontos:

  • É case sensitive (Sensível ao Caso), diferencia MAIÚSCULAS de minúsculas;
  • Onde informamos “EmbeddingJavascriptMyControl.MyControl.js” temos que prestar atenção a nossa estrutura de diretório, se nossos arquivos .js irão ficar em um diretório chamado “Resource/js/arquivo.js” temos que informar o caminho completo do arquivo, assim:  “EmbeddingJavascriptMyControl.Resource.js.arquivo.js”

O arquivo AssemblyInfo.js fica em embaixo do nó “Properties” do nosso projeto “EmbeddingJavascriptMyControl”

Segunda Alteração

Voltaremos ao nosso projeto “EmbeddingJavascript”, abra o arquivo “Default.aspx”.

Antes da nossa declaração <myctl:MyControl… cole a seguinte linha

 <asp:ScriptManager ID="ScriptManager1" EnablePartialRendering="True" runat="server">
            <Scripts>
                <asp:ScriptReference Assembly="EmbeddingJavascriptMyControl" Name="EmbeddingJavascriptMyControl.MyControl.js" />
            </Scripts>
        </asp:ScriptManager>

O Código da página deve ter ficado parecido com este:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="EmbeddingJavascript._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ Register Assembly="EmbeddingJavascriptMyControl" Namespace="EmbeddingJavascriptMyControl"
    TagPrefix="myctl" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Acessando recursos embutidos</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" EnablePartialRendering="True" runat="server">
            <Scripts>
                <asp:ScriptReference Assembly="EmbeddingJavascriptMyControl" Name="EmbeddingJavascriptMyControl.MyControl.js" />
            </Scripts>
        </asp:ScriptManager>
        <myctl:MyControl runat="server" ID="myControl" />
    </div>
    </form>
</body>
</html>

Agora podemos executar nossa aplicação e clicar no botão que tudo irá funcionar.

Abra o código fonte da página, vamos ver o que foi gerado.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>
	Acessando recursos embutidos
</title></head>
<body>
    <form name="form1" method="post" action="default.aspx" id="form1">
<div>
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTEyNjU1NTIyMDlkZBHjFb7Tj9qnQw+tUeeM3n2rSAIt" />
</div>

<script type="text/javascript">
//<![CDATA[
var theForm = document.forms['form1'];
if (!theForm) {
    theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}
//]]>
</script>

<script src="/WebResource.axd?d=vJiRxykuUPDnEnitlfulpUyNW8r5H6f6ZTXVc0STOzayuJB5KQYvdOM6qDn1woYY3aHrZvYEH3kaqgMXLvHN0f1Rgw81&amp;t=634245118914809245" type="text/javascript"></script>

<script src="/ScriptResource.axd?d=pQSfU-Gluj5fWKvwHugAhrYCplBo6HWtAZk18MYQFen7LCnH5eACZQlY2hQFj_mG8wBOBn8gzXjwh__AP4GvZAYiRAXNVfqsrwJ_47pioSwJ0Nt50nZsPwYb_ImjAVVyDYqnF5Mrxws1Hb-eiA7hx6mpB7JzD1kjO5Z0nisDG9Htu64q0&amp;t=fffffffff9d85fa6" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
if (typeof(Sys) === 'undefined') throw new Error('A estrutura do cliente do ASP.NET Ajax falhou ao ser carregada.');
//]]>
</script>

<script src="/ScriptResource.axd?d=NjPhQE1Bg_p8r6kqRbkzTPoHZEUVZljs8n4CqKl-4K_SKhwe4jodATh4CnF0I6areXjIfo1RXiLf4npDOWzM3aFzuXVJ8J5aUvDNXTQM0aIaoAgK-1desXxMafETtHLNsh1PEt5-FelvmW-JH-0-0vJV7Y2ziFETXay4wFRNjRdh_c1F0&amp;t=fffffffff9d85fa6" type="text/javascript"></script>
<script src="/ScriptResource.axd?d=BZRoaxvjoo_kLM6d5_RbZyrPibF04d5B4H_E9ve7x6R9w7j4h8eSLh1dELDU0HnMVzRTtC11C31fvdqZm20-B7I3y0joB9f7DDGCZbLa5ydhKnkJqYlCPlFvCw83WLuQCOYPbAJ9hEEPRfOGyysCkjJ9Pal1xeF5TbJH3LC4OYpMqqSQ0&amp;t=ffffffffb915d82c" type="text/javascript"></script>
    <div>
        <script type="text/javascript">
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('ScriptManager1', document.getElementById('form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls([], [], [], 90);
//]]>
</script>

        <span id="myControl">Digite algo:
                            <input type='text' id='txtAlgo' />
                            <input type='button' value='Clique aqui' onclick='CallAlert(txtAlgo.value);' /></span>
    </div>

<script type="text/javascript">
//<![CDATA[
Sys.Application.initialize();
//]]>
</script>
</form>
</body>
</html>

Podemos reparar que desta forma funciona, mas, não creio que atenda a nossa necessidade, alem de que é gerada muita coisa desnecessária em nossa página, vamos fazer uma análise de tudo que aconteceu na nossa página.

Veja quantas linhas de código foram geradas apenas para mostrar uma mensagem de alerta.

<script src="/WebResource.axd?d=vJiRxykuUPDnEnitlfulpUyNW8r5H6f6ZTXVc0STOzayuJB5KQYvdOM6qDn1woYY3aHrZvYEH3kaqgMXLvHN0f1Rgw81&amp;t=634245118914809245" type="text/javascript"></script>

<script src="/ScriptResource.axd?d=pQSfU-Gluj5fWKvwHugAhrYCplBo6HWtAZk18MYQFen7LCnH5eACZQlY2hQFj_mG8wBOBn8gzXjwh__AP4GvZAYiRAXNVfqsrwJ_47pioSwJ0Nt50nZsPwYb_ImjAVVyDYqnF5Mrxws1Hb-eiA7hx6mpB7JzD1kjO5Z0nisDG9Htu64q0&amp;t=fffffffff9d85fa6" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
if (typeof(Sys) === 'undefined') throw new Error('A estrutura do cliente do ASP.NET Ajax falhou ao ser carregada.');
//]]>
</script>

<script src="/ScriptResource.axd?d=NjPhQE1Bg_p8r6kqRbkzTPoHZEUVZljs8n4CqKl-4K_SKhwe4jodATh4CnF0I6areXjIfo1RXiLf4npDOWzM3aFzuXVJ8J5aUvDNXTQM0aIaoAgK-1desXxMafETtHLNsh1PEt5-FelvmW-JH-0-0vJV7Y2ziFETXay4wFRNjRdh_c1F0&amp;t=fffffffff9d85fa6" type="text/javascript"></script>
<script src="/ScriptResource.axd?d=BZRoaxvjoo_kLM6d5_RbZyrPibF04d5B4H_E9ve7x6R9w7j4h8eSLh1dELDU0HnMVzRTtC11C31fvdqZm20-B7I3y0joB9f7DDGCZbLa5ydhKnkJqYlCPlFvCw83WLuQCOYPbAJ9hEEPRfOGyysCkjJ9Pal1xeF5TbJH3LC4OYpMqqSQ0&amp;t=ffffffffb915d82c" type="text/javascript"></script>
    <div>
        <script type="text/javascript">
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('ScriptManager1', document.getElementById('form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls([], [], [], 90);
//]]>
</script>

Imagine se tivéssemos muitos controles carregados na página, o tanto de código que seria carregado.
E se precisássemos chamar algum método antes da tag body para que nosso controle funcione perfeitamente?

Outro problema, é que o utilizador de nossos componentes teria que saber a estrutura de nossos recursos e teria que se lembrar de ficar colocando o Script Manager e configurá-lo de forma adequada em todas as páginas, caso contrário daria erros. Isso não é legal.

Mas como eu prometi, vamos resolver todos estes problemas com o uso de Handlers e Modules.

Então, vamos a segunda parte do artigo.


Usando Handlers e Modules com recursos embutidos

Antes de iniciar vamos descobrir para que servem e o que são os handlers e modules.

O que são handlers (manipuladores)?

Os handlers, ou manipuladores, normalmente processam as requisições e não têm interatividade com o usuário, como páginas ou controles, e normalmente são responsáveis por camadas lógicas dentro da aplicação.

Podemos utilizar um handler quando precisamos executar algo no servidor sem a necessidade de intervenção do usuário final.

Os handlers implementam a interface IHttpHandler, o HttpHandler é responsável por atender as requisições do browser e a interface IHttpHandler possui as assinaturas necessárias para que estas requisições sejam atendidas.

O que são modules (módulos)?

Os módulos HTTP são executados antes e depois do handler (manipulador) e nos fornece métodos para interagir com a requisição (HttpRequest). Os módulos devem implementar a interface System.Web.IHttpModule.

São normalmente sincronizados com os eventos da classe System.Web.IHttpModule (implementado dentro do Global.asax.cs ou. vb).

A seguir uma lista de eventos que devem ser considerados na implementação do módulo:

  • BeginRequest
  • AuthenticateRequest
  • AuthorizeRequest
  • ResolveRequestCache
  • AcquireRequestState
  • PreRequestHandlerExecute
  • PostRequestHandlerExecute
  • ReleaseRequestState
  • UpdateRequestCache
  • EndRequest
  • * PreSendRequestHeaders
  • * PreSendRequestContent
  • * error

Os eventos identificados por um asterisco (*) pode ocorrer a qualquer momento dentro da requisição, todos os outros estão listados em sua ordem de chamada.

Agora que sabemos com o que iremos trabalhar, vamos dar uma limpada na nossa página “Default.aspx” remova a tag do Script Manager, não iremos usar mais ela.  O código deverá ter ficado assim:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="EmbeddingJavascript._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ Register Assembly="EmbeddingJavascriptMyControl" Namespace="EmbeddingJavascriptMyControl"
    TagPrefix="myctl" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Acessando recursos embutidos</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <myctl:MyControl runat="server" ID="myControl" />
    </div>
    </form>
</body>
</html>

Voltaremos ao nosso projeto “EmbeddingJavascriptMyControl” , adicionaremos uma nova classe ao nosso projeto, a minha eu chamei de “ResourceHandler.cs”

Segue o código completo da classe:

 #region Using
using System;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Text;
using System.Text.RegularExpressions;
using System.IO.Compression;
using System.Web.Caching;
using System.Net;
using System.Collections.Generic;
using System.Reflection;
#endregion

namespace EmbeddingJavascriptMyControl
{
    public class ResourceHandler : IHttpHandler
    {
        private const int DAYS_IN_CACHE = 30;
        public void ProcessRequest(HttpContext context)
        {
            string path = context.Request.QueryString["r"];
            path = "EmbeddingJavascriptMyControl." + path + ".js";
            string content = string.Empty;

            if (!string.IsNullOrEmpty(path))
            {
                if (context.Cache[path] == null)
                {
                    string[] scripts = path.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string script in scripts)
                    {
                        content += RetrieveScript(script) + Environment.NewLine;
                    }

                    content = StripWhitespace(content);
                    context.Cache.Insert(path, content, null, Cache.NoAbsoluteExpiration, new TimeSpan(DAYS_IN_CACHE, 0, 0, 0));
                }
            }

            content = (string)context.Cache[path];
            if (!string.IsNullOrEmpty(content))
            {
                context.Response.Write(content);
                SetHeaders(content.GetHashCode(), context);
                Compress(context);
            }
        }

        /// <summary>
        /// carrega o script em memória e depois devolve ao objeto chamador
        /// </summary>
        /// <param name="resourceFullName">caminho completo do recurso</param>
        private static string RetrieveScript(string resourceFullName)
        {
            string script = null;
            //get from resource
            Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceFullName);

            if (stream != null)
            {
                using (StreamReader sr = new StreamReader(stream))
                {
                    script = sr.ReadToEnd();
                    sr.Close();
                }
            }

            return script;
        }

        /// <summary>
        /// limpa o arquivo javascrip e retorna
        /// </summary>
        /// <param name="body">script</param>
        private static string StripWhitespace(string body)
        {

            /*
             * aqui eu retorno o o script diretamente
             * facilita para a depuração.
             * mas você pode tratar, se estiver em modo debug, não limpa
             * compilou, limpa
             */

            //remove newLine e comentários com //
            string[] lines = body.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            StringBuilder emptyLines = new StringBuilder();
            foreach (string line in lines)
            {
                string s = line.Trim();
                if (s.Length > 0 && !s.StartsWith("//"))
                {
                    if (s.Contains("//"))
                        s = s.Substring(0, s.IndexOf("//"));

                    emptyLines.AppendLine(s.Trim());
                }
            }

            body = emptyLines.ToString();

            // remove C styles comments
            body = Regex.Replace(body, "/\\*.*?\\*/", String.Empty, RegexOptions.Compiled | RegexOptions.Singleline);
            //// trim left
            body = Regex.Replace(body, "^\\s*", String.Empty, RegexOptions.Compiled | RegexOptions.Multiline);
            //// trim right
            body = Regex.Replace(body, "\\s*[\\r\\n]", "\r\n", RegexOptions.Compiled | RegexOptions.ECMAScript);
            // remove whitespace beside of left curly braced
            body = Regex.Replace(body, "\\s*{\\s*", "{", RegexOptions.Compiled | RegexOptions.ECMAScript);
            // remove whitespace beside of coma
            body = Regex.Replace(body, "\\s*,\\s*", ",", RegexOptions.Compiled | RegexOptions.ECMAScript);
            // remove whitespace beside of semicolon
            body = Regex.Replace(body, "\\s*;\\s*", ";", RegexOptions.Compiled | RegexOptions.ECMAScript);
            // remove newline after keywords
            //body = Regex.Replace(body, "\\r\\n(?<=\\b(abstract|boolean|break|byte|case|catch|char|class|const|continue|default|delete|do|double|else|extends|false|final|finally|float|for|function|goto|if|implements|import|in|instanceof|int|interface|long|native|new|null|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|true|try|typeof|var|void|while|with)\\r\\n)", " ", RegexOptions.Compiled | RegexOptions.ECMAScript);

            return body;
        }

        /// <summary>
        /// Isso fará com que o navegador e o servidor mantenham a saída
        /// em cache melhorando o desempenho.
        /// </summary>
        private static void SetHeaders(int hash, HttpContext context)
        {
            HttpCachePolicy cache = context.Response.Cache;
            DateTime modifiedDate = new DateTime(GetAssemblyTime(Assembly.GetExecutingAssembly())).ToUniversalTime();
            DateTime nowDate = DateTime.Now.ToUniversalTime().AddSeconds(-1);

            if (modifiedDate > nowDate)
            {
                modifiedDate = nowDate;
            }

            cache.SetLastModified(modifiedDate);
            cache.SetOmitVaryStar(true);
            cache.SetVaryByCustom("r");
            cache.SetExpires(DateTime.UtcNow.AddDays(365));
            cache.SetValidUntilExpires(true);
            cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
            cache.SetCacheability(HttpCacheability.Public);
            cache.SetLastModifiedFromFileDependencies();
            cache.SetMaxAge(new TimeSpan(DAYS_IN_CACHE, 0, 0, 0));
            cache.SetETag("\"" + hash.ToString() + "\"");

            context.Response.ContentType = "text/javascript";
            context.Response.Cache.VaryByHeaders["Accept-Encoding"] = true;
        }

        private static long GetAssemblyTime(Assembly assembly)
        {
            AssemblyName assemblyName = assembly.GetName();

            return File.GetLastWriteTime(new Uri(assemblyName.CodeBase).LocalPath).Ticks;
        }

        #region Compression

        private const string GZIP = "gzip";
        private const string DEFLATE = "deflate";

        private static void Compress(HttpContext context)
        {
            if (context.Request.UserAgent != null && context.Request.UserAgent.Contains("MSIE 6"))
                return;

            if (IsEncodingAccepted(GZIP))
            {
                context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
                SetEncoding(GZIP);
            }
            else if (IsEncodingAccepted(DEFLATE))
            {
                context.Response.Filter = new DeflateStream(context.Response.Filter, CompressionMode.Compress);
                SetEncoding(DEFLATE);
            }
        }

        /// <summary>
        /// Verifica os cabeçalhos da solicitação para ver se a
        /// codificação é aceita pelo cliente.
        /// </summary>
        private static bool IsEncodingAccepted(string encoding)
        {
            return HttpContext.Current.Request.Headers["Accept-encoding"] != null && HttpContext.Current.Request.Headers["Accept-encoding"].Contains(encoding);
        }

        /// <summary>
        /// Adiciona a codificação ao cabeçalho de resposta
        /// </summary>
        /// <param name="encoding"></param>
        private static void SetEncoding(string encoding)
        {
            HttpContext.Current.Response.AppendHeader("Content-encoding", encoding);
        }

        #endregion

        public bool IsReusable
        {
            get { return false; }
        }

    }

    public class ResourceModule : IHttpModule
    {

        #region IHttpModule Members

        void IHttpModule.Dispose()
        {

        }

        void IHttpModule.Init(HttpApplication context)
        {
            context.PostRequestHandlerExecute += new EventHandler(context_BeginRequest);
        }

        #endregion

        void context_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication app = sender as HttpApplication;
            if (app.Context.CurrentHandler is Page && !app.Request.RawUrl.Contains("serviceframe"))
            {
                if (!app.Context.Request.Url.Scheme.Contains("https"))
                {
                    app.Response.Filter = new WebResourceFilter(app.Response.Filter);
                }
            }
        }

        #region Stream filter

        private class WebResourceFilter : Stream
        {

            public WebResourceFilter(Stream sink)
            {
                _sink = sink;
            }

            private Stream _sink;

            #region Properites

            public override bool CanRead
            {
                get { return true; }
            }

            public override bool CanSeek
            {
                get { return true; }
            }

            public override bool CanWrite
            {
                get { return true; }
            }

            public override void Flush()
            {
                _sink.Flush();
            }

            public override long Length
            {
                get { return 0; }
            }

            private long _position;
            public override long Position
            {
                get { return _position; }
                set { _position = value; }
            }

            #endregion

            #region Methods

            public override int Read(byte[] buffer, int offset, int count)
            {
                return _sink.Read(buffer, offset, count);
            }

            public override long Seek(long offset, SeekOrigin origin)
            {
                return _sink.Seek(offset, origin);
            }

            public override void SetLength(long value)
            {
                _sink.SetLength(value);
            }

            public override void Close()
            {
                _sink.Close();
            }

            public override void Write(byte[] buffer, int offset, int count)
            {
                byte[] data = new byte[count];
                Buffer.BlockCopy(buffer, offset, data, 0, count);
                string html = System.Text.Encoding.Default.GetString(buffer);
                int index = 0;
                List<string> list = new List<string>();

                Regex regex = new Regex("<script\\s*src=\"((?=[^\"]*(webresource.axd|scriptresource.axd))[^\"]*)\"\\s*type=\"text/javascript\"[^>]*>[^<]*(?:</script>)?", RegexOptions.IgnoreCase);
                foreach (Match match in regex.Matches(html))
                {
                    if (index == 0)
                        index = html.IndexOf(match.Value);

                    string relative = match.Groups[1].Value;
                    list.Add(relative);
                    html = html.Replace(match.Value, string.Empty);
                }

                if (index > 0)
                {
                    string script = "<script type=\"text/javascript\" src=\"js.axd?path={0}\"></script>";
                    string path = string.Empty;
                    foreach (string s in list)
                    {
                        path += HttpUtility.UrlEncode(s) + ",";
                    }

                    html = html.Insert(index, string.Format(script, path));
                }

                byte[] outdata = System.Text.Encoding.Default.GetBytes(html);
                _sink.Write(outdata, 0, outdata.GetLength(0));
            }

            #endregion

        }

        #endregion

    }
}

Adicione outra classe ao seu projeto, a minha eu chamei de “JavascriptManager” será responsável por adicionar a tag script nas páginas que usam o nosso controle.

Código da classe:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.HtmlControls;
using System.Web.UI;

namespace EmbeddingJavascriptMyControl
{
    public static class JavascriptManager
    {
        /// <summary>
        /// Use este método para incluir um arquivo javascript na página
        /// </summary>
        /// <param name="_page">página que o arquivo deverá ser incluído</param>
        /// <param name="resourceName">caminho script</param>
        public static void IncludeResource(Page _page, string resourceName)
        {
            //aqui eu usei o nome EmbeddingJavascriptMyControl, mas você poderá usar o nome que quiser
            resourceName = "/EmbeddingJavascriptMyControl.axd?r=" + resourceName;

            HtmlGenericControl script = new HtmlGenericControl("script");
            script.Attributes.Add("type", "text/javascript");
            script.Attributes.Add("src", _page.ResolveClientUrl(resourceName));
            _page.Header.Controls.Add(script);
        }
    }
} 

Ufa! Já estamos quase lá. O bom disso é que só iremos ter o trabalho uma vez, e poderemos usar em todo o nosso projeto de componentes, podemos ter um ou cem componentes, sempre serão iguais e usarão os mesmos métodos.

Agora precisamos ajustar o nosso MyControl.cs para que ele faça a referência ao script que o controla.

Para isso, iremos sobrescrever o evento “OnInit”.

 protected override void OnInit(EventArgs e)
 {
 base.OnInit(e);

JavascriptManager.IncludeResource(Page,"MyControl");
 }

Você devará ter isso em todos os controles que você criar, eu tenho por hábito criar os controles e com os mesmos nomes dos arquivos.js, como eu fiz  com o MyControl.cs e MyControl.js, assim eu sei para que uso cada arquivo js, mesmo que fiquem em diretórios separados.

Feito isso, já podemos executar a nossa aplicação. Abra o código fonte da página e veja como ficou mais limpo. Economizamos algumas linhas e memória no navegador do cliente.

Percebam também que o nosso script foi registrado na tag head. Isso nos ajudará muito quando fizermos controles mais complexos e dinâmicos.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>
	Acessando recursos embutidos
</title><script type="text/javascript" src="/EmbeddingJavascriptMyControl.axd?r=MyControl"></script></head>
<body>
    <form name="form1" method="post" action="default.aspx" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTEzNjgwMjk4NDBkZMpc3l1d4uAY38/msqun4petNKEJ" />
</div>

    <div>
        <span id="myControl">Digite algo:
                            <input type='text' id='txtAlgo' />
                            <input type='button' value='Clique aqui' onclick='CallAlert(txtAlgo.value);' /></span>
    </div>
    </form>
</body>
</html>

Certo. Mas se você clicar no botão … dará o mesmo erro de sempre. “CallAlert não está definido”. Isso irá acontecer porque o nosso script é referenciado pela nossa url

<script type=”text/javascript” src=”/EmbeddingJavascriptMyControl.axd?r=MyControl“></script>

Veja que definimos um url diferente para o src de nosso script, é aqui que iremos usar o nosso handler e o module que criamos. É ele, o handler, quem será responsável por tratar o script e carregar no navegador, mas precisamente no cache do navegador.

Então vamos lá colocar o handler e o módulo para funcionar.

Voltamos ao nosso  projeto “EmbeddingJavascript” abra o arquivo “Web.config” nele precisamos configurar os nossos handlers e modules

Localize dentro do Web.config a chave <httpHandlers> antes da tag de fechamento iremos colocar o nosso handler

A minha ficou assim:

<httpHandlers>
	<remove verb="*" path="*.asmx"/>
	<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
	<add path="*/EmbeddingJavascriptMyControl.axd" verb="*" type ="EmbeddingJavascriptMyControl.ResourceHandler"/>
</httpHandlers>

Lembra do src do javascript? “/EmbeddingJavascriptMyControl.axd” é aqui que informamos quem será o manipulador do nosso handler.

Agora segue a declaração do módulo dentro do arquivo web.config. Declare entre as tags <httpModules> </httpModules>

<httpModules>
	<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
	<add name="EmbeddingJavascriptMyControlResourceModule" type="EmbeddingJavascriptMyControl.ResourceModule"/>
</httpModules>

Pronto. Agora pode executar a aplicação e clicar no botão. Se fez tudo correto. Vai funcionar. 🙂

Esta dica não serve apenas para javascript, com alguns ajustes no código do “ResourceHandler” você poderá usar para tratar imagens, css, e outros arquivos que forem necessários ao funcionamento do seu componente, mas nunca se esqueça de marcar como “Embedded Resource”.

A alteração que fizemos no web.config também podemos automatizá-la. Mas isso fica para um próximo artigo. Este já está muito extenso.

Segue o fonte do exemplo que eu usei no artigo Embedding JavaScript (110)


É isso ai pessoal 🙂
Até o próximo
? Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Paginação remota com ext.net, CSharp e ASPX

1
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (1 votos, média: 5,00 de 5)
Loading...
8 de dezembro de 2010

Olá Pessoal.

Neste tutorial eu vou escrever e falar  sobre paginação remota utilizando o Ext.Net, C# e ASP.Net.

Antes de começar, uma rápida revisão sobre paginação.

O que é paginação?
Paginação (paginar) consiste em dividir em partes (páginas).
Imagine o jornal que você lê, ele é dividido em páginas para que facilite a sua leitura, você pode virar as páginas ou ir para uma página específica do seu jornal e continuar a sua leitura, o mesmo acontece com livros, revistas etc.

Agora vamos imaginar a paginação dos dados.
Quando você abre um conjunto de registros, a exibição ao usuário pode se tornar lenta, se o número de registros for grande, neste momento você precisa paginar o seu conjunto de registros, ou tupla se você preferir.

Uma tupla é cada linha de registro de uma tabela na base de dados

A idéia de paginar os seus registros é o mesmo que o jornal faz, dividir e exibir o todo em partes.

Agora vamos ao que interessa.

No ext.net existe o componente PagingToolbar

<ext:PagingToolbar></ext:PagingToolbar>

Ele é o responsável pela interface ao usuário e por gerar os eventos necessários para a paginação.

Como tudo acontece.

Quando você clica no botão de navegação o ext envia uma requisição ao servidor, passando como parâmetros no Request as seguintes variáveis start e limit

Estas duas variáveis são responsáveis por dizer onde começa e qual o tamanho da página.

Ok! Mas como o ASPXvai capturar estas informações?

Para tratarmos estes eventos dentro do ASPX temos que usar handlers.

Opa! O que são handlers?

Os handlers são responsáveis por interceptar solicitações feitas ao servidor de aplicativo.
Eles são executados como processos em resposta a um pedido feito pelo site.

Você pode criar seus próprios handlers genéricos (ashx) que processam a saída e enviam ao navegador.

Para programar um handler genérico o mesmo deverá implementar a interface System.Web.IHttpHandler.
As interfaces exigem que você implemente o método ProcessRequest e a propriedade IsReusable.

O método ProcessRequest manipula o processamento para as solicitações feitas, enquanto o booleano IsReusable é a propriedade que diz se o manipulador é reutilizável ou se um novo manipulador é necessário para cada solicitação.

Declaração básica para um handler genérico:

Diretiva de página:
<%@ WebHandler Language=”C#Class=”DeclaracaoBasica %>

Código da página

using System;
using System.Web;
public class DeclaracaoBasica : IHttpHandler {
    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/plain";
        context.Response.Write("Hello World");
    }
    public bool IsReusable {
        get {
            return false;
        }
    }
}

Bom, agora que temos os pré-requisitos vamos à nossa vídeo-aula clique no vídeo abaixo e assista.

O som do vídeo está baixo, recomendo o uso de um fone de ouvido.

YouTube responded to TubePress with an HTTP 410 - No longer available

Se preferir, assista direto no youtube http://www.youtube.com/watch?v=BjFpW-Mg-bg&hd=1

Download do código de exemplo:
Paginação remota com Ext.net, handlers, aspx e C# (303)

Dúvidas? Poste-as no fórum referindo-se a este tutorial.
http://desenvolvedores.net/ext.net

É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s