HTML

...now browsing by category

 

jQuery + Ajax + ASP.NET Generic Handler = Interactividade e Performance

Segunda-feira, Agosto 23rd, 2010

jquery-logo

O título pode parecer demasiado confuso com a utilização de tantos termos, no entanto estou confiante que no fim do artigo, vai estar de acordo com a escolha.

Recentemente deparei-me com uma nova situação, por um lado necessitava de toda a interactividade e flexibilidade proporcionada pelo jQuery no cliente mas também queria continuar a utilizar no lado do servidor Microsoft ASP.NET de forma convencional, ou seja, eu precisava do melhor dos dois mundos.

Depois de executar alguns testes, encontrei rapidamente alguns entraves, limitações e complexidades, o que me levou à procura de uma solução simples e  eficaz.

Deparei-me com um cenário em que o utilizador necessitava de preencher um formulário extenso, para tornar mais agradável a experiência do utilizador, para isso implementei vários plugins do jQueryUI, conforme se pode verificar nas imagens em baixo.

 

img_6

img_7

img_8

 

A informação a inserir é extensa e necessita de validação no lado do servidor, não se torna prático executar tudo isso postback atrás de postback até estar tudo correcto, utilizar update panels torna-se demasiado complexo para o tipo de validação necessário e a biblioteca Ajax do ASP.NET não é das mais agradáveis de utilizar quando necessitamos de performance, optei por isso experimentar utilizar o método Ajax disponibilizado pelo jQuery.

Para simplificar o artigo, e porque o que realmente interessa é o conceito, optei por criar um formulário de teste cujo objectivo é adicionar um cliente ao sistema.

img_9

 

Depois adiciona-se um novo item ao projecto, um Generic Handler

img_10

Visto de uma forma mais simplista, um Generic Handler é uma página ASP.NET que contém apenas um método e não suporta eventos nem controlos.

De seguida vamos adicionar um novo ficheiro JS e utilizando método Ajax da biblioteca jQuery, vai ser chamado o Generic Handler AdicionarClienteHandler.

A função Ajax é de simples implementação, como se pode verificar ao analisar o script apresentado.

$(document).ready(function () {

    //activa o plugin Tabs
    $(function () {
        $("#tabs").tabs();
    });

    //método adicionar cliente
    $('#btnAdicionar').click(function () {

        $.ajax({
            url: '../../Handlers/AdicionarClienteHandler.ashx',
            data: $('form').serialize(),
            type: 'POST',
            success: function (data) {
                alert('Cliente adicionado com sucesso.');
                $('#listClientes').append(data);
            },
            error: function (data) {
                alert("Ocorreu um erro ao processar o seu pedido.");
            }
        });
    });
});

O utilizador vai preencher o formulário e quando é executado o click sobre o botão adicionar, vai ser executado o script apresentado anteriormente.

img_1

Este script vai serializar todos os campos de input que se encontram dentro da tag <form> e enviar esta informação para o Generic Handler através de um POST executado de forma assíncrona.

img_2

 

Depois o Generic Handler vai processar a informação enviada, executar as suas validações e construir a resposta.

using System;
using System.Linq;
using System.Web;
using System.Text;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace WebApplication1.Handlers
{

    public class AdicionarClienteHandler : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            var form = context.Request.Form;
            context.Response.ContentType = "text/html";
            context.Response.Write(ProcessaFormulario(form));
        }

        protected string ProcessaFormulario(NameValueCollection form)
        {
            string username, nome, email, telefone, telemovel, fax;

            nome        = form["txtNome"];
            username    = form["txtUsername"];
            email       = form["txtEmail"];
            telefone    = form["txtTelefone"];
            telemovel   = form["txtTelemovel"];
            fax         = form["txtFax"];

            var buildResponse = new StringBuilder();

            buildResponse.AppendLine("<div class=\"DefaultRow\">");
            buildResponse.AppendLine(string.Format("<h6>{0}</h6>", nome));
            buildResponse.AppendLine(string.Format("<span class=\"DafaultRowLabel\">Username: {0}</span>", username));
            buildResponse.AppendLine(string.Format("<span class=\"DafaultRowLabel\">Email: {0}</span>", email));
            buildResponse.AppendLine(string.Format("<span class=\"DafaultRowLabel\">Telefone: {0}</span>", telefone));
            buildResponse.AppendLine(string.Format("<span class=\"DafaultRowLabel\">Telemóvel: {0}</span>", telemovel));
            buildResponse.AppendLine(string.Format("<span class=\"DafaultRowLabel\">Fax: {0}</span>", fax));
            buildResponse.AppendLine("</div>");

            return buildResponse.ToString();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

 

A resposta por sua vez, é a informação do cliente formatada de acordo com a forma pretendida e pronta para ser adicionada à lista de clientes.

img_3

img_5

Tudo isto é executado de forma assíncrona, simples, eficaz e sem a necessidade de executar qualquer postback.

 

/*Versão JSON*/

Depois de verificar que surgiram alguns pedidos relativamente à utilização de JSON na resposta do Generic Handler, deixo aqui as alterações necessárias:

using System;
using System.Linq;
using System.Web;
using System.Text;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Web.Script.Serialization;

namespace WebApplication1.Handlers
{

    public class AdicionarClienteHandler : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            var form = context.Request.Form;
            var serializer = new JavaScriptSerializer();

            context.Response.Charset = "utf-8";
            context.Response.ContentType = "application/json";
            context.Response.Write(serializer.Serialize(JsonResponse(form)));
        }

        public List<string> JsonResponse(NameValueCollection form)
        {
            string username, nome, email, telefone, telemovel, fax;

            var lista   = new List<string>();
            nome        = form["txtNome"];
            username    = form["txtUsername"];
            email       = form["txtEmail"];
            telefone    = form["txtTelefone"];
            telemovel   = form["txtTelemovel"];
            fax         = form["txtFax"];

            lista.Add(nome);
            lista.Add(username);
            lista.Add(email);
            lista.Add(telefone);
            lista.Add(telemovel);
            lista.Add(fax);

            return lista;

        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

Como podemos verificar na imagem em baixo, a resposta é agora JSON

foto_2

 

img_5

O projecto de teste aqui demonstrado , desenvolvido em Visual Studio 2010, está disponível para download aqui.

 

Referências:

http://api.jquery.com/serialize/

http://api.jquery.com/category/ajax/

http://msdn.microsoft.com/en-us/library/bb398986.aspx

Nested ListViews em ASP.NET

Quinta-feira, Julho 1st, 2010

ASP.net_100_logo_5

Por vezes temos necessidade de integrar muitos dados num ecrã para os apresentar da forma mais conveniente ao utilizador.

O que acontece durante este processo é que por vezes enfrentamos algumas dificuldades em transpor a informação que temos organizada na camada de negócio para a camada de apresentação de uma forma simples e sem grandes  alterações na sua estrutura.

Por exemplo, quando temos uma classe Contacto que além das suas propriedades mais comuns, contém também uma propriedade que é uma estrutura de dados da classe Servico, por vezes a melhor solução é optar por utilizar ListViews encadeadas.

public class Contacto
{
    public int id { get; set; }
    public string nome { get; set; }
    public IEnumerable<Servico> servicos { get; set; }

    public Contacto()
    {
        servicos = new List<Servico>();
    }

    public IEnumerable<Contacto> ObterContactos()
    {
        for (int i = 0; i < 3; i++)
        {
            var c = new Contacto();
            c.id = i;
            c.nome = "nome_" + i;
            c.servicos = (new Servico()).ObterServicos(i);

            yield return c;
        }

        yield break;
    }
}

Como podem verificar no código apresentado anteriormente, a classe Contacto tem uma propriedade do tipo IEnumerable<Servico>, que é uma estrutura de dados da classe Servico.

public class Servico
{
    public int id { get; set; }
    public string descricao { get; set; }

    public IEnumerable<Servico> ObterServicos(int id)
    {
        var listaServicos = new List<Servico>();

        for (int i = 0; i < 3; i++)
        {
            var serv = new Servico();
            serv.id = i;
            serv.descricao = string.Format("servico_{0}_{1}", i, id);
            listaServicos.Add(serv);
        }

        return listaServicos;
    }
}

Numa situação em que temos que apresentar num ecrã a informação da classe Contacto, a melhor solução seria poder alimentar um único controlo com uma lista de elementos desta classe e, este se encarrega-se de agregar a informação.

Sugiro algo assim:

<div>
        <h2>Exemplo de Nested ListViews</h2>
        <asp:ListView ID="ListViewPai" runat="server">
            <LayoutTemplate>
                <asp:PlaceHolder runat="server" ID="itemPlaceholder"></asp:PlaceHolder>
            </LayoutTemplate>
            <ItemTemplate>
                <h5>Informacao da ListView Pai</h5>
                <p>Nome:<%#Eval("nome") %></p>
                <asp:ListView ID="ListViewFilho" runat="server" DataSource='<%# Eval("servicos") %>'>
                    <LayoutTemplate>
                        <h6>Informacao da ListView Filho</h6>
                        <asp:PlaceHolder runat="server" ID="itemPlaceholder"></asp:PlaceHolder>
                    </LayoutTemplate>
                    <ItemTemplate>
                        <div style="border:1px solid black; width:250px; margin:15px;">
                            <p>id:<%#Eval("id") %></p>
                            <p>descricao:<%#Eval("descricao") %></p>
                        </div>
                    </ItemTemplate>
                </asp:ListView>
                <hr />
            </ItemTemplate>
        </asp:ListView>
    </div>

Como podem verificar, a fonte de dados que alimenta a ListViewFilho é         “<%# Eval("servicos") %>” que é o nome da propriedade existente na classe Contacto e que é do tipo IEnumerable<Servico>.

Ou seja, cada item do ListViewPai vai alimentar o ListViewFilho com a sua propriedade Servico.

O que resulta num código de carregamento de dados extremamente simples, como podemos verificar:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebApplication1
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                CarregarDadosListView();
            }
        }

        public void CarregarDadosListView()
        {
            ListViewPai.DataSource = (new Contacto()).ObterContactos();
            ListViewPai.DataBind();
        }
    }
}

Ao utilizar este método apenas basta alimentar o datasource da ListViewPai, depois é cada item desta listview quem se encarrega de dar informação a ser apresentada à ListViewFilho.

Faça aqui o download  do projecto de demonstração em VS2010

Visual Studio 2010 Quick Tips #1

Terça-feira, Junho 15th, 2010

Hoje em dia com a vulgaridade da utilização de ecrãs de resolução Wide Screen, por vezes não utilizamos o espaço que temos disponível da forma mais conveniente.

O Visual Studio disponibiliza uma funcionalidade que permite optimizar o espaço extra que o utilizador tem disponível.

normal_VS2010

Quando está a desenvolver projectos projectos web, pode dividir o a sua área de trabalho verticalmente e visualizar  simultaneamente o design e o source da página em que se encontra a trabalhar.

Para isso basta ir ao menu tools->options, depois seleccionar a caixa “show all settings” e seleccionar HTML Designer. Nas propriedades selecionar a caixa “Split views vertcally”.

change_settings

Depois é só fechar as janelas que tem abertas na sua área de trabalho do Visual Studio e abrir-las novamente.

 

new_vs2010

E assim pode optimizar de uma forma mais eficiente a área de trabalho que tem disponível.

ASP.NET Google Maps

Terça-feira, Abril 20th, 2010

google

O ASP.NET Google Maps é um Web User Control que implementa a API v2 do Google Maps.

Para utilizar esta API e este Web User Control é necessário registar-se aqui e obter uma chave de utilização.

A utilização deste controlo é em tudo semelhante ao ASP.NET Bing Maps, é apenas executar o Drag & Drop do controlo na página de implementação e configurar as propriedades do controlo.

Como utilizar?

Substituir no ficheiro GoogleMapsControl.ascx “YOU_KEY” pela chave que lhe foi atribuída durante o processo de registo na API do Google Maps.

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="GoogleMapsControl.ascx.cs" Inherits="GoogleMaps.GoogleMaps.GoogleMapsControl" %>

<script type="text/javascript" src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=YOU_KEY"></script>
<script type="text/javascript" src="GoogleMaps/GoogleMaps.js"></script>
<div id="myMap"></div>

Implementação do Web User Control numa página Default.aspx

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

<%@ Register src="GoogleMaps/GoogleMapsControl.ascx" tagname="GoogleMapsControl" tagprefix="uc1" %>

<!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 runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <uc1:GoogleMapsControl ID="GoogleMapsControl" runat="server" latitude="38.69052609303235" longitude="-9.297544956207275"
        pushPin="true" pushPinTitle="Sample Title" pushPinDescription="Sample Description" zoom="17" MapWidth="600px" MapHeight="400px"
        mapStyle="G_SATELLITE_MAP" />
    </div>
    </form>
</body>
</html>

Nas propriedades do controlo podemos definir um conjunto de funcionalidades disponibilizadas pela API do Google Maps, como o estilo de apresentação do mapa, definir a utilização de um marcador bem como o seu título e descrição.

Deixo aqui algumas imagens do controlo:

google_map_standard

google_satelite

google_satelite_2

google_terrain

Demo projecto de VS2008 e Web User Control disponível em:

http://aspnetgooglemaps.codeplex.com

ASP.NET Bing Maps

Terça-feira, Abril 20th, 2010

Bing_maps

O ASP.NET Bing Maps é mais um Web User Control que desenvolvi e decidi partilhar com a comunidade.

Por vezes damos por nós a fazer tarefas ou processos repetitivas durante o processo de desenvolvimento de aplicações, quando isso acontece é sinal que provavelmente podemos criar um controlo ou um fluxo genérico em substituição de todo esse trabalho. Os controlos genéricos são fáceis de manter actualizados, a actualização do controlo reflecte-se em todos os locais onde este é utilizado, são fáceis de implementar, são reutilizáveis e extensíveis.

Decidi criar um  Web User Control que implementa o Bing Maps porque necessitava de um controlo que eu pudesse controlar e alterar  programaticamente as propriedades de um mapa numa aplicação web.

Como utilizar?

projectlocation Criar no projecto de Visual Studio uma pasta com o nome BingMaps, adicionar os ficheiros BingMaps.ascx e BingMaps.js.

Depois, já na página onde se pretende implementar o controlo, é necessário registar o controlo como qualquer outro Web User Control (normalmente o Visual Studio faz isso por nós quando se executa o drag & drop do controlo sobre a página de implementação).

Exemplo de implementação:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="BingMaps._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<!-- Registo do controlo na página -->
<%@ Register src="BingMaps/BingMaps.ascx" tagname="BingMaps" tagprefix="UserControl" %>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Teste Bing Maps</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <UserControl:BingMaps runat="server" ID="BingMapContainer" runat="server" distanceUnit="Kilometers"  MapWidth="600px"
        MapHeight="400px" mapMode="Mode2D" mapStyle="BirdseyeHybrid" pushPin="true" pushPinTitle="Sample Title" zoom="1"
            pushPinDescription="Sample Description" latitude="38.69052609303235" longitude="-9.297544956207275"/>
    </div>
    </form>
</body>
</html>

Nas opções do controlo podemos definir o modo de apresentação do mapa (2D/3D), o estilo do mapa (Birds Eye, Road, Aerial, etc).

É possível definir um marcador, com um título e descrição,  para uma melhor indicação no mapa do local que pretendemos  apresentar.

Deixo aqui algumas imagens de demonstram as capacidades do controlo:

bingmaps_2d_birdseye

bingmaps_2d_aerial

bingmaps_2d_road

bingmaps_3d

Demo projecto de VS2008 e Web User Control disponível em:

http://aspnetbingmaps.codeplex.com

ASP.NET DataPager – EFDataPager

Terça-feira, Abril 20th, 2010

att9ae36 O EFDataPager é um Web User Control que permite paginação com Entity Framework ou qualquer outro tipo de repositório de dados. A grande diferença entre este DataPager e o controlo standard que é disponibilizado no Visual Studio é que ao contrário do controlo standard este apenas vai buscar ao repositório de dados a informação que está a ser apresentada na página seleccionada, o que permite uma melhoria substancial na performance de acesso a dados e tempos de carregamento da página web.

Exemplo de uma ListView com o DataPager do VS2008:

image_3

image_4

 

Como se pode verificar na Imagem1 e Imagem2 acima apresentadas, quando estamos a utilizar o Standard DataPager para controlar a paginação de uma ListView, este carrega todos os dados do repositório e posteriormente organiza-os por páginas.

O maior problema de este controlo é que, em cada postback efectuado, é carregada novamente para a ListView a informação de todas as páginas mesmo que, como podemos ver na imagem2, apenas estejam a ser apresentados 3 registos ao utilizador. Como consequência o tempo de carregamento da página da página é penalizado, pois são carregadas informações que não vão ser apresentadas ao utilizador,  o que gera um declínio na experiência de utilização.

 

Exemplo de uma ListView com o EFDataPager:

image_1

image_2

 

Por outro lado, ao utilizar o EFDataPager, apenas são carregados para a ListView os registos que são apresentados (Imagem3 e Imagem4), garantindo assim uma melhor experiência de utilização por parte do utilizador e um tempo de carregamento da página substancialmente menor.

Pode utilizar o EFDataPager com qualquer repositório de dados, incluindo com Entity Framework.

 

Implementação

Além de ser muito fácil de implementar o EFDataPager, é também um controlo extensível e facilmente personalizado para ir de encontro a necessidades mais específicas.

image_5

Para implementar o EFDataPager é necessário adicionar ao projecto o ficheiro ControloDataPager.ascx e o ficheiroPagerEventArgs.cs.

Depois é necessário, como em qualquer outro WebUserControl, registar o controlo na página em que este vai ser utilizado.

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ControloListView.ascx.cs" Inherits="EFDataPager.UserControls.ControloListView" %>
<%@ Register src="ControloDataPager.ascx" tagname="ControloDataPager" tagprefix="UserControl" %>

<h1>Notícias</h1>

<div class="noticias-pager">
    <UserControl:ControloDataPager ID="ControloDataPager1" runat="server" ViewStateMode="Enabled" />
    <asp:Label ID="NumberOfRowsLoadedFromDataSource" runat="server"></asp:Label>
    <br />
    <asp:Label ID="NumberOfRowsLoadedFromDataSourceEN" runat="server"></asp:Label>
</div>

<asp:ListView ID="listviewNoticias" runat="server" >
    <LayoutTemplate>
        <asp:PlaceHolder runat="server" ID="itemPlaceholder"></asp:PlaceHolder>
    </LayoutTemplate>
    <ItemTemplate>
        <div class="noticias-title" >
            <h3><%#Eval("Titulo")%></h3>
        </div>
        <div class="body-container">
            <p><%#Eval("Descricao") %></p>
        </div>
    </ItemTemplate>
    <EmptyItemTemplate>
        <h1>Não existem notícias disponíveis neste momento.</h1>
    </EmptyItemTemplate>
</asp:ListView>

Depois é necessário implementar o método RegistarEventos, utilizá-lo no Page_Load, implementar os eventos registados no método  RegistarEventos e implementar o método CarregarDadosListview.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace EFDataPager.UserControls
{
    using EFDataPager.BusinessObjects;

    public partial class ControloListView : System.Web.UI.UserControl
    {

        protected void Page_Load(object sender, EventArgs e)
        {
            RegistarEventos();
            ControloDataPager1.PageSize = 5;
            ControloDataPager1.RowCount = GetNumberOfRows();

            if (!Page.IsPostBack)
            {
                CarregarDadosListview(ControloDataPager1.PageSize, 0);
            }
        }

        private void RegistarEventos()
        {
            ControloDataPager1.First    += new ControloDataPager.ListViewPagerFirstHandler(ControloDataPager1_First);
            ControloDataPager1.Previous += new ControloDataPager.ListViewPagerPreviousHandler(ControloDataPager1_Previous);
            ControloDataPager1.Next     += new ControloDataPager.ListViewPagerNextHandler(ControloDataPager1_Next);
            ControloDataPager1.Last     += new ControloDataPager.ListViewPagerLastHandler(ControloDataPager1_Last);
        }

        void ControloDataPager1_Last(object sender, PagerEventArgs e)
        {
            CarregarDadosListview(e.pageSize, e.currentRow);
        }

        void ControloDataPager1_Next(object sender, PagerEventArgs e)
        {
            CarregarDadosListview(e.pageSize, e.currentRow);
        }

        void ControloDataPager1_Previous(object sender, PagerEventArgs e)
        {
            CarregarDadosListview(e.pageSize, e.currentRow);
        }

        void ControloDataPager1_First(object sender, PagerEventArgs e)
        {
            CarregarDadosListview(e.pageSize, e.currentRow);
        }

        private void CarregarDadosListview(int pageSize, int currentRow)
        {
            var data = new DadosMock();
            listviewNoticias.DataSource = data.ObterDadosMock(pageSize, currentRow).ToArray();
            listviewNoticias.DataBind();

            var nRows = data.ObterDadosMock(pageSize, currentRow).ToArray().Count();
            NumberOfRowsLoadedFromDataSource.Text = string.Format("Número de elementos carregados: {0}", nRows);
            NumberOfRowsLoadedFromDataSourceEN.Text = string.Format("Number of binded elements: {0}", nRows);
        }

        private int GetNumberOfRows()
        {
            var data = new DadosMock();
            return data.ObterRowCount();
        }
    }
}

 

Está classe é um Mock para simular o comportamento de um repositório de dados.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace EFDataPager.BusinessObjects
{
    public class DadosMock
    {
        public int ID { get; set; }
        public string Titulo { get; set; }
        public string Descricao { get; set; }

        public DadosMock()
        {

        }

        public DadosMock(int id, string title, string desc)
        {
            this.Descricao = desc;
            this.ID = id;
            this.Titulo = title;
        }

        public IQueryable<DadosMock> ObterDadosMock()
        {
            var lista = new List<DadosMock>();

            for (int i = 0; i < 18; i++)
            {

                var item = new DadosMock(i, string.Format("Titulo {0}", i), string.Format("Descricao {0}", i));
                lista.Add(item);
            }

            return lista.ToArray().AsQueryable();
        }

        public IQueryable<DadosMock> ObterDadosMock(int pageSize, int currentRow)
        {
            var lista = new List<DadosMock>();

            //only for generate test data
            for (int i = 0; i < 18; i++)
            {
                var item = new DadosMock(i, string.Format("Titulo {0}", i), string.Format("Descricao {1}", i, i));
                lista.Add(item);
            }

            return lista.ToArray().Skip(currentRow).Take(pageSize).AsQueryable();
        }

        public int ObterRowCount()
        {
            return ObterDadosMock().Count();
        }
    }
}

Com o recurso ao linq, podemos facilmente controlar a paginação dos dados pedidos ao repositório de dados através dos métodos Skip e Take.

Demo: http://blastersystems.com/efdatapager/

O EFDataPager e o projecto vs2008 de teste estão disponíveis em:

http://efdatapager.codeplex.com/

IE 8 & VS2005 Debug

Sábado, Março 13th, 2010

VisualStudioLogoO Internet Explorer 8 tem melhorias e novas funcionalidades. Entre elas está o LCIE (Loosely Coupled Internet Explorer).

Essencialmente o LCIE é um conjunto de mudanças internas na arquitectura do Internet Explorer 8 que têm com objectivo melhorar a performance, escalabilidade e robustez do browser.

Entre essas melhorias está uma em especial, que trouxe alguns problemas inesperados aos programadores que utilizam o Visual Studio 2005 juntamente o com Internet Explorer 8.

No IE7 cada janela do browser (UI Frame) tem o seu processo, todos os novos separadores e controlos ActiveX estavam residem nesse processo. O problema com este modelo é, quando um separador provoca um “access violation” ou “stack overflow”, o processo é terminado, logo todos os separadores que estão no mesmo UI Frame vão ser terminados.

ie-tabsPara isso não acontecer no IE8, cada separador tem um processo próprio, assim quando existe um erro provocado por um separador, apenas o processo do separador é terminado, não o processo da janela do browser.

Na imagem apresentada, o browser tem três separadores abertos, o que no IE7 se irá traduzir num único processo, mas no IE8 se vai traduzir em 4 processos como se pode verificar na imagem em baixo.task-manager

Um dos processos é do UI Frame e os restantes três pertencem cada um a cada um dos separadores activos na janela do browser.

É sem dúvida uma excelente melhoria face à versão anterior do browser, no entanto surgiram alguns problemas aos programadores no momento que estes fazem o debug de aplicações  web com o Visual Studio 2005 e IE8.

O que acontece é que se, ao executar o debug da aplicação já existir um processo com o Internet Explorer a ser executado, o debugger do Visual Studio para e ignora os “break points” porque não encontra qual o processo do Internet Explorer que está a ser depurado.

Para resolver esta situação é necessário desabilitar a funcionalidade “grouwth” do LCIE.

Os passos são os seguintes:

1 – Abrir o RegEdit

 

2- Percorer HKEY_LOCALMACHINE -> SOFTWARE -> Microsoft -> Internet Explorer –> Main

 

3 – Adicionar uma DWORD com o nome “TabProcGrowth”

 

4 – Colocar no valor de “TabProcGrowth”  0

 

Fontes:

http://blogs.msdn.com/ie/archive/2008/03/11/ie8-and-loosely-coupled-ie-lcie.aspx

http://weblogs.asp.net/abdullaabdelhaq/archive/2009/06/01/VS-Debug-Problem-with-IE8.aspx

ASP.NET jQuery MessageBox

Terça-feira, Março 9th, 2010

4qjbsb5pxk1lp7f1pf66w3ls2a O ASP.NET MessageBox  é um Web User Control que integra a framework jQuery , é um controlo extensível que possibilita várias formas diferentes de apresentar MessageBox’s ao utilizador, utilizando duas tecnologias que em conjunto permitem a construção de um controlo dinâmico, extensível e de agradável utilização.

Disponível em http://jquerymessagebox.codeplex.com

Nesta primeira versão o controlo permite dois tipos diferentes de messagebox, uma do tipo “bubble” e outra standard.

O controlo possibilita a criação automática de um botão para interagir com o controlo, no entanto este é apenas opcional e pode-se desligar esta funcionalidade facilmente.

O design do controlo assenta nos plugins de jquery jQueryUI e plugin BlockUI, apenas com alguma modificações para estes se integrarem melhor num Web User Control de ASP.NET.

Vou passar agora a exemplificar a utilização do ASP.NET jQuery MessageBox com um exemplo para a MessageBox do tipo buble:

 

Página de exemplo que vai utilizar o controlo (default.aspx)

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="jQueryMessageBox._Default" %>
<%@ Register src="WebUserControl/MessageBoxControl.ascx" tagname="MessageBoxControl" tagprefix="uc1" %>
<!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 runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <uc1:MessageBoxControl ID="MessageBoxControl1" runat="server" />
    </div>
    </form>
</body>
</html>

 

 

Code behind (default.aspx.cs)


using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;

namespace jQueryMessageBox
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            MessageBoxControl1.Title = "Test jQueryMessageBox";
            MessageBoxControl1.NameBtn = "Open Message";
            MessageBoxControl1.NameBtnMessageBox = "Close";
            MessageBoxControl1.tipo =
                WebUserControl.MessageBoxControl.typeMessageBox.growlMessageBox;

            MessageBoxControl1.Text = "Login succeeded!";
        }

        protected void btn_Click(object sender, EventArgs e)
        {
            MessageBoxControl1.btn_Click(sender, e);
        }
    }
}

 

Ao carregar no botão visível na imagem, pode-se visualizar a mensagem dirigida ao utilizador.

bubble_message

 

Por outro lado se o tipo de mensagem que nos interessa mostrar ao utilizador contém muita informação e obriga a uma maior atenção por parte do utilizador, podemos utilizar este tipo de MessageBox, alterando o tipo de mensagem a apresentar e alterando o ficheiro default.aspx.cs:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;

namespace jQueryMessageBox
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            MessageBoxControl1.Title = "Test jQueryMessageBox";
            MessageBoxControl1.NameBtn = "Open Message";
            MessageBoxControl1.NameBtnMessageBox = "Close";

            /*Alteração do tipo de MessageBox*/
            MessageBoxControl1.tipo =
                WebUserControl.MessageBoxControl.typeMessageBox.centerMessageBox;

            var dados = new StringBuilder();

            dados.Append("jQuery is a lightweight cross-browser JavaScript library that emphasizes interaction between JavaScript and HTML. It was released in ");
            dados.Append("January 2006 at BarCamp NYC by John Resig. Used by over 27% of the 10,000 most visited websites, jQuery is the most popular ");
            dados.Append("JavaScript library in use today.");
            dados.Append("jQuery is free, open source software, dual-licensed under the MIT License and the GNU General Public License, Version 2. jQuerys ");
            dados.Append("syntax is designed to make it easier to navigate a document, select DOM elements, create animations, handle events, and develop Ajax ");
            dados.Append("applications. jQuery also provides capabilities for developers to create plugins on top of the JavaScript library. Providing this option, ");
            dados.Append("developers are able to create abstractions for low-level interaction and animation, advanced effects and high-level, theme-able widgets. This");
            dados.Append("contributes to the creation of powerful and dynamic web pages.");
            dados.Append("Microsoft and Nokia have announced plans to bundle jQuery on their platforms, Microsoft adopting it initially within Visual Studio for use ");
            dados.Append("within Microsoft ASP.NET AJAX framework and ASP.NET MVC Framework whilst Nokia will integrate it into their Web Run-Time platform.");

            MessageBoxControl1.Text = dados.ToString();
        }

        protected void btn_Click(object sender, EventArgs e)
        {
            MessageBoxControl1.btn_Click(sender, e);
        }
    }
}

 

E o resultado ao executar o click no mesmo botão é apresentado em baixo:

center_meaage

 

O projecto de teste e o código fonte do controlo está disponível em http://jquerymessagebox.codeplex.com

 

Fontes de informação úteis:

http://weblogs.asp.net/scottgu/archive/2008/11/21/jquery-intellisense-in-vs-2008.aspx

http://jquery.com/

http://jqueryui.com/

IE8 e o modo de compatibilidade

Sexta-feira, Março 5th, 2010

internet-explorer Já foi anunciada a morte do Internet Explorer 6, a versão 7 do mesmo browser parece ter passado de moda muito depressa porque agora existe o IE8.

No passado mês de Fevereiro de 2010, no top 5 dos browsers mais utilizados na web estão as três últimas versões do Internet Explorer (IE6, IE7, IE8).

Esta situação provoca uma grande dor de cabeça nos developers de aplicações web, pois é necessário garantir a compatibilidade das aplicações com os browsers que detêm maior quota de mercado e ao mesmo tempo é necessário conseguir implementar nas aplicações novas funcionalidades disponíveis apenas nos browsers mais recentes.

O Internet Explorer 8 é a versão mais  recente disponibilizada pela Microsoft e a mais próxima aos standards do W3C, houve por isso mesmo alguma mudanças importantes. No entanto este browser apresenta algumas funcionalidades que garantem aos developers e às empresas a compatibilidade de este browser com as suas aplicações desenvolvidas para versões anteriores.

Está situação pode acontecer  em empresas que estejam a executar processos de migração para o Windows 7, e no qual não é possível instalar versões do browser anteriores à versão 8.

Existem várias formas de garantir a compatibilidade das aplicações com o IE8, como por exemplo, configurar no servidor web o modo de compatibilidade em cada uma das aplicações (mais info sobre como garantir a compatibilidade no IIS aqui, e no Apache aqui), no entanto neste artigo vou falar apenas nas funcionalidades que o IE8 disponibiliza para garantir este processo.

O Internet Explorer 8 tem 3 modos de funcionamento, estes são:

  • IE 8 Standards Mode – Este modo garante que as páginas vão ser processadas pelo browser utilizando os últimos  standards utilizados pela Microsost para esta versão de browser.

 

  • IE 7 Compatibility Mode – Com este modo o browser interpreta as páginas recorrendo ao DOCTYPE do IE7, contudo continua a ser utilizado o motor do IE8 para processar as páginas.

 

  • IE7 Mode – Com este modo as páginas vão ser executadas de acordo os standards do IE7 e o DOCTYPE do IE7.
Agora vou propor a análise de outro problema. Vamos supor  estamos a desenvolver uma aplicação web para uma máquina de windows 7 com IE8 com compatibility mode activo, não temos acesso ao servidor web nem podemos alterar a configuração do IE8.
 
Como desenvolver uma aplicação que utiliza todas vantagens que o IE8 disponibiliza e garantir que vai funcionar correctamente nesta situação?
 
Podemos garantir isso com uma alteração, incluindo  “meta tag” no cabeçalho da aplicação web que vai forçar o browser a sair do modo em que está a operar e processar as páginas da aplicação em causa com o motor e DOCTYPE do IE8 nativo.
 
Para isso basta inserir esta “meta tag” no cabeçalho das páginas:
<meta http-equiv="X-UA-Compatible" content="IE=8"/>
 
Veja as diferenças da utilização dos vários modos que o IE8 suporta em acção:
 
Nesta imagem podemos ver que o browser está configurado para correr em modo de compatibilidade, no entanto a ”meta tag” que está no cabeçalho obriga-o a comporta-se como o IE8 nativo, ou seja, a utilizar o motor de processamento do IE8 e o DOCTYPE do IE8.
 
IE8-mode
 

Nesta imagem podemos ver que o browser está configurado para correr em modo de compatibilidade, podemos verificar esta situação recorrendo ao Developer Tools, ferramenta incluída no IE8, que este está a utilizar o motor de IE8 em modo de compatibilidade e a utilizar o DOCTYPE do IE7.EmulateIE7-mode

 

Aqui podemos verificar que ao utilizar na “meta tag” o “IE=7”, o browser utiliza o modo e o DOCTYPE do IE7, embora nas preferências do browser esteja activo o modo de compatibilidade.

IE7-mode

 

Mais informação sobre o IE8 e os seus modos de funcionamento em:

http://msdn.microsoft.com/en-us/library/cc288325(VS.85).aspx#DCModes

Tech Days 2010

Terça-feira, Fevereiro 9th, 2010

techdays2010_logo

Nos dias 20 a 22 de Abril de 2010, a Microsoft vai realizar no Lagoas Park o Tech Days 2010.

O evento vai contar com a presença de cerca de 40 oradores portugueses e estrangeiros para assegurar as mais de 90 sessões e 40 laboratórios técnicos sobre XNA, Visual Studio 2010, Expression Studio, Sharepoint, Silverlight, WPF, Office, Exchange, SQL Server, Windows 7, Windows server 2008 e Windows Azure.

Também vão existir sessões direccionadas à arquitectura de software, Best Practices no desenvolvimento de software e User Exprerience (UX).

Este é o maior evento em Portugal na área das TI, um evento a não perder neste ano de 2010.

Como não podia deixar de ser, a comunidade NetPonto vai estar representada no evento.

Até dia 5 de Março existe um desconto de 75€ na inscrição.   

Mais informações em: http://www.techdays2010.com/