.net

...now browsing by category

 

Kinect Hack

Quinta-feira, Fevereiro 2nd, 2012

33ª Edição - Fevereiro 2012

Para esta edição da Revista Programar, resolvi escrever um artigo sobre um dispositivo de hardware que está a mudar a forma como os utilizadores interagem com  jogos e  aplicações.

O Kinect é um dispositivo para a consola Microsoft Xbox 360, que através da detecção de movimentos e gestos realizados pelo utilizador ou até através do reconhecimento por voz, permite que o leitor consiga interagir com a consola e com os jogos de vídeo que suportam este hardware de uma forma natural, substituindo assim os controlos tradicionais que as consolas disponibilizam.
Foi anunciado pela primeira vez em Junho de 2009 conferência E3 sob o nome de código “Natal”. Colocado à venda em Novembro de 2010, o Kinect garantiu um lugar na história, em apenas 60 dias foram comercializados 8 milhões de unidades.
À primeira vista, o Kinect assemelha-se a uma webcam com um formato não muito convencional, mas na verdade, trata-se de muito mais do que isso. É composto por uma câmara RGB, um projector de raios IR que em conjunto com uma segunda câmara monocromática de IR constrói a informação em 3D com os metadados e um microfone vectorial (composto por quatro microfones colocados estrategicamente no dispositivo), que o tornam capaz de isolar e identificar as vozes dos vários jogadores e, para finalizar, possui um pequeno motor que lhe permite ajustar automaticamente o seu ângulo de visão para detectar todos os jogadores presentes.
As características técnicas que o Kinect apresenta a um preço acessível, suscitaram imediatamente muito interesse a uma comunidade que reconheceu de imediato as potencialidades que um dispositivo como este proporciona.
Sendo assim, ainda durante o mês de lançamento, uma empresa ofereceu uma recompensa a quem conseguisse desenvolver um driver open-source que permitisse o Kinect ser utilizado foram do seu âmbito, ou seja, fora do ecossistema da Xbox 360, para que o dispositivo pudesse ser utilizado para outros fins que não o entretenimento.
Apenas alguns dias após o seu lançamento, foi disponibilizado um driver para o Sistema Operativo Linux (Libfreenect) que permitia a utilização tanto da câmara RGB como dos sensores de profundidade 3-D. Em Dezembro do mesmo ano, a empresa PrimeSense, que é proprietária de tecnologia semelhante à apresentada pelo Kinect, lançou a sua própria versão do driver Open-Source para Kinect, bem como um Middleware dotado de um conjunto de funcionalidades até então apenas disponíveis na consola da Microsoft, como a capacidade de detecção de gestos e tracking de indivíduos.

Toda esta comoção gerada à volta do Kinect e a sua utilização fora do seu ambiente natural, levou à criação da organização sem fins lucrativos OpenNI (Open Natural Interaction) e em última instância, pressionou a Microsoft para relevar/alterar os seus planos para o Kinect e disponibilizar o Microsoft Kinect SDK.
Assim, o leitor tem ao seu dispor a escolha entre duas soluções para desenvolver as suas aplicações com o Kinect, a Framework OpenNI ou o Kinect SDK da Microsoft, atualmente em versão Beta, no entanto já foi anunciado que a versão comercial vai estar disponível em Fevereiro de 2012, acompanhada de uma nova versão de hardware, suportado oficialmente Microsoft.

Para ler mais sobre este e sobre outros temas, não deixe de descarregar a Revista Programar Nº33, disponível aqui.

kinect_hack_01.png

A aplicação descrita no decorrer de este artigo encontra-se disponível em http://kinecthack.codeplex.com

 

Artigos incluídos nesta edição:

Tema de capa
Kinect Hack

A programar
Geração de Números Aleatórios – Parte 3
Programação Orientada aos Objectos em Pascal
Herança com JavaScript
CORE DUMP – Ambientes Produtivos
Enigmas de C#: Disposable Structs
SQL Azure Federations na Pratica

Visual (Not) Basic
VISUAL (NOT) BASIC -Iterators

Comunidades
BizTalk Server – Princípios Básicos dos Mapas – Parte 2
Desenvolvimento em SharePoint 2010 – Parte 2

Silverlight & Model-View-ViewModel

Quinta-feira, Maio 26th, 2011

pic1

Existem várias formas diferentes de definir a camada de apresentação, pode-se seguir uma aproximação tradicional, na qual toda a responsabilidade da camada de apresentação se encontra na View ou então podemos seguir um pattern para prodecer à separação de responsabilidades da camada de apresentção.

A aproximação mais tradicional torna-se mais simples e criar permite rapidamente uma camada de apresentação. É ideal quando se trata de aplicações de pequena dimensão .

O problema é que não existem aplicações de pequena dimensão, algumas podem começar dessa forma, mas muito rapidamente crescem e transforma-se em uma dor de cabeça para qualquer equipa de manutenção, dando origem a mais uma “Black Box” passados alguns anos.

Para ajudar a solucionar esse tipo de problema recorrente, nasceram algumas patterns que nos permitem estruturar de uma melhor forma a camada de apresentação das aplicações, entre elas estão o MVC (Model-View-Controller), MPV (Model-View-Presenter) e o MVVM (Model-View-ViewModel) que é o tema de este post.

Como não podemos deixar de falar em Silverlight ou WPF quando o tema é MVVM, vou apresentar um tutorial no qual demonstro como implementar MVVM no Silverlight.

O MVVM pretende estabelecer uma separação clara de responsabilidades na camada de apresentação, a View apenas é responsável pela User Interface, o Model detém as regras de negócio (classes de negócio, acesso a dados, etc) e o ViewModel é o responsável de fornecer à View apenas os dados que ela necessita e é também o ViewModel quem tem a responsabilidade de reagir aos eventos  gerados pela View.

Uma das grandes vantagens na utilização de um pattern como este, é que permite alterar completamente a UI apresentada ao utilizador sem que se torne necessário alterar mais do que a View, ou então, num caso mais extremo, partilhar o Model e o ViewModel e criar dois Front-End diferentes, como Silverlight e Windows Phone 7.

Vamos então ao que interessa, criar uma Demo de uma aplicação de gestão de Clientes, que apresenta a lista  de clientes de uma empresa e também permite adicionar um novos clientes à lista actual.

Vamos criar um novo projecto do tipo Silverlight Application no VS2010

image

 

Seleccionar a check-box “Host the Silverlight application in a new Web site”

image

 

Adicionar duas novas referências ao projecto Silverlight

image

 

Adicionar as referências à página MainPage.xaml

<UserControl x:Class="Silverlight_MVVM_Demo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"             

    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">

    </Grid>
</UserControl>

 

Vamos definir a View

image

Agora em XAML

<UserControl x:Class="Silverlight_MVVM_Demo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="Gray" Width="800" Height="600">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--DataGrid Definition-->
        <sdk:DataGrid Height="300" Grid.Row="0" BorderThickness="1"/>

        <!--Data Input Definition-->
        <sdk:Label
            Grid.Row="1"
            Height="28"
            HorizontalAlignment="Left"
            Margin="10,10,0,0"
            VerticalAlignment="Top"
            Width="171"
            Content="Adicionar novo cliente"
            FontFamily="Verdana"
            FontSize="13.333"
            FontWeight="Bold" />
        <TextBlock
            HorizontalAlignment="Left"
            Margin="80,59,0,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="Nome"
            VerticalAlignment="Top"/>
        <TextBox
            HorizontalAlignment="Left"
            Margin="80,79,0,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            VerticalAlignment="Top"
            Width="245"/>
        <TextBlock
            HorizontalAlignment="Left"
            Margin="80,120,0,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="NIF"
            VerticalAlignment="Top"/>
        <TextBox
            HorizontalAlignment="Left"
            Margin="80,140,0,136"
            Grid.Row="1"
            TextWrapping="Wrap"
            Width="245"
            d:LayoutOverrides="Height"/>
        <TextBlock
            HorizontalAlignment="Left"
            Margin="80,0,0,98"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="Telemóvel"
            VerticalAlignment="Bottom"/>
        <TextBox
            HorizontalAlignment="Left"
            Margin="80,0,0,70"
            Grid.Row="1"
            TextWrapping="Wrap"
            Width="245"
            VerticalAlignment="Bottom"/>
        <TextBlock
            Margin="0,0,229,98"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="Número de Cliente"
            VerticalAlignment="Bottom"
            HorizontalAlignment="Right"
            Width="111"/>
        <TextBox
            Margin="0,0,96,70"
            Grid.Row="1"
            TextWrapping="Wrap"
            VerticalAlignment="Bottom"
            HorizontalAlignment="Right"
            Width="245"/>
        <TextBlock
            Margin="0,120,321,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="Fax"
            VerticalAlignment="Top"
            HorizontalAlignment="Right"
            RenderTransformOrigin="-1.579,0.438"/>
        <TextBox
            Margin="0,140,96,136"
            Grid.Row="1"
            TextWrapping="Wrap"
            d:LayoutOverrides="Height"
            HorizontalAlignment="Right"
            Width="245"/>
        <TextBlock
            Margin="0,59,293,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="Telefone"
            VerticalAlignment="Top"
            HorizontalAlignment="Right"/>
        <TextBox
            Margin="0,79,96,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            VerticalAlignment="Top"
            HorizontalAlignment="Right"
            Width="245"/>
        <Button
            Content="Adicionar"
            HorizontalAlignment="Right"
            Margin="0,0,8,8"
            Grid.Row="1"
            VerticalAlignment="Bottom"
            Width="75"/>
    </Grid>
</UserControl>

 

Criar a estrutura de directórios

image

 

De seguida adicionar ao directório Model uma nova classe com o nome Cliente.cs com a seguinte estrutura

namespace Silverlight_MVVM_Demo.Model
{
    public class Cliente
    {
        public string ClienteID { get; set; }
        public string Nome { get; set; }
        public string NIF { get; set; }
        public string Telefone { get; set; }
        public string Telemovel { get; set; }
        public string Fax { get; set; }

    }
}

Vamos adicionar duas classe base que implementam os Interfaces ICommand e INotifyPropertyChanged.

Classe RelayCommand

using System;
using System.Windows.Input;

namespace Silverlight_MVVM_Demo.ViewModel
{
    public class RelayCommand : ICommand
    {
        private Action<Object> _handler;
        public RelayCommand(Action<Object> handler)
        {
            _handler = handler;
        }

        private bool _isEnabled = true;
        public bool IsEnabled
        {
            get { return _isEnabled; }
            set
            {
                if (value != _isEnabled)
                {
                    _isEnabled = value;
                    if (CanExecuteChanged != null)
                    {
                        CanExecuteChanged(this, EventArgs.Empty);
                    }
                }
            }
        }

        public bool CanExecute(object parameter)
        {
            return IsEnabled;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            _handler(parameter);
        }
    }
}

Se seguirmos uma abordagem simplista, podemos olhar para o ICommand como o interface que permite o ViewModel receber notificações de eventos da View.

 

Classe ViewModelBase

namespace Silverlight_MVVM_Demo.ViewModel
{
    using System.ComponentModel;
    /*TODOS OS VIEWMODEL HERDAM DESTA CLASSE*/
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler  PropertyChanged;
        public void RaisePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Vamos herdar todos os ViewModel da classe ViewModelBase para simplificar as notificações quando existem alterações nos conteúdos das propriedades, assim apenas vai ser necessário chamar o método RaisePropertyChanged(“NomeDaPropriedade”) para notificar o ViewModel e a View caso tal seja necessário.

 

De seguida criamos a classe de ViewModel

namespace Silverlight_MVVM_Demo.ViewModel
{
    using Model;
    using System.Collections.ObjectModel;

    public class MainPageViewModel : ViewModelBase
    {
        #region View Data Collection
        private ObservableCollection<Cliente> _ClientList;
        public ObservableCollection<Cliente> ClientList
        {
            get { return _ClientList; }
            set
            {
                _ClientList = value;
                /*Notificar alteração de conteúdo*/
                RaisePropertyChanged("ClientList");
            }
        }
        #endregion

        /*Propriedades utilizadas na View*/
        #region View Properties
        private string _ClientID;
        public string ClientID
        {
            get { return _ClientID; }
            set
            {
                _ClientID = value;
                /*Notificar alteração de conteúdo*/
                RaisePropertyChanged("ClientID");
            }
        }

        private string _ClientName;
        public string ClientName
        {
            get { return _ClientName; }
            set
            {
                _ClientName = value;
                /*Notificar alteração de conteúdo*/
                RaisePropertyChanged("ClientName");
            }
        }

        private string _ClientNIF;
        public string ClientNIF
        {
            get { return _ClientNIF; }
            set
            {
                _ClientNIF = value;
                /*Notificar alteração de conteúdo*/
                RaisePropertyChanged("ClientNIF");
            }
        }

        private string _ClientPhone;
        public string ClientPhone
        {
            get { return _ClientPhone; }
            set
            {
                _ClientPhone = value;
                /*Notificar alteração de conteúdo*/
                RaisePropertyChanged("ClientPhone");
            }
        }

        private string _ClientFax;
        public string ClientFax
        {
            get { return _ClientFax; }
            set
            {
                _ClientFax = value;
                /*Notificar alteração de conteúdo*/
                RaisePropertyChanged("ClientFax");
            }
        }

        private string _ClientCell;
        public string ClientCell
        {
            get { return _ClientCell; }
            set
            {
                _ClientCell = value;
                /*Notificar alteração de conteúdo*/
                RaisePropertyChanged("ClientCell");
            }
        }
        #endregion

        #region Commands
        public RelayCommand LoadClientData { get; set; }
        public RelayCommand SubmitForm { get; set; }
        #endregion

        public MainPageViewModel()
        {
            /*Comandos que o ViewModel vai reconhecer da View*/
            this.LoadClientData = new RelayCommand(MockClientList);
            this.SubmitForm = new RelayCommand(Submit);
        }

        /// <summary>
        /// Adiciona um novo cliente
        /// </summary>
        /// <param name="parameter"></param>
        protected void Submit(object parameter)
        {
            /*
             Como utilizamos as notificações quando
             é alterado o conteúdo de uma propriedade
             da View, já temos no ViewModel todos os
             dados.
             */
            var c = new Cliente
            {
                ClienteID = ClientID,
                Fax = ClientFax,
                NIF = ClientNIF,
                Nome = ClientName,
                Telefone = ClientPhone,
                Telemovel = ClientCell
            };

            ClientList.Add(c);

            ClientID = string.Empty;
            ClientFax = string.Empty;
            ClientCell = string.Empty;
            ClientName = string.Empty;
            ClientPhone = string.Empty;
            ClientNIF = string.Empty;
        }

        /// <summary>
        /// Dados iniciais da DataGrid
        /// </summary>
        /// <param name="parameter"></param>
        public void MockClientList(object parameter)
        {
            ClientList = new ObservableCollection<Cliente>();

            for (int i = 0; i < 3; i++)
            {
                var c = new Cliente{
                 ClienteID=i.ToString(),
                 Fax="123456789",
                 NIF="789456123",
                 Nome="Client "+i,
                 Telefone="5556623",
                 Telemovel="3237656511"
                };

                ClientList.Add(c);
            }
        }
    }
}

Agora ja temos definidas as três componentes do MVVM, apenas falta ligar a View com o ViewModel, e isso é conseguido através do Databinding, que o Silverlight nos proporciona para facilitar essa operação. Vamos então completar a página MainPage.xaml com os bindings.

<UserControl x:Class="Silverlight_MVVM_Demo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
    xmlns:vm="clr-namespace:Silverlight_MVVM_Demo.ViewModel"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <!--DATACONTEXT DEFINITION-->
    <UserControl.DataContext>
        <!--Link entre o ViewModel e a View-->
        <vm:MainPageViewModel/>
    </UserControl.DataContext>

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <!--Quando a página é carregada é executado o comando LoadClientData-->
            <i:InvokeCommandAction Command="{Binding LoadClientData}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

    <Grid x:Name="LayoutRoot" Background="Gray" Width="800" Height="600">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--DataGrid Definition-->
        <sdk:DataGrid Height="300" Grid.Row="0" BorderThickness="1"/>

        <!--Data Input Definition-->
        <sdk:Label
            Grid.Row="1"
            Height="28"
            HorizontalAlignment="Left"
            Margin="10,10,0,0"
            VerticalAlignment="Top"
            Width="171"
            Content="Adicionar novo cliente"
            FontFamily="Verdana"
            FontSize="13.333"
            FontWeight="Bold" />
        <TextBlock
            HorizontalAlignment="Left"
            Margin="80,59,0,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="Nome"
            VerticalAlignment="Top"/>
        <TextBox
            Text="{Binding ClientName, Mode=TwoWay}"
            HorizontalAlignment="Left"
            Margin="80,79,0,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            VerticalAlignment="Top"
            Width="245"/>
        <TextBlock
            HorizontalAlignment="Left"
            Margin="80,120,0,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="NIF"
            VerticalAlignment="Top"/>
        <TextBox
            Text="{Binding ClientNIF, Mode=TwoWay}"
            HorizontalAlignment="Left"
            Margin="80,140,0,136"
            Grid.Row="1"
            TextWrapping="Wrap"
            Width="245"
            d:LayoutOverrides="Height"/>
        <TextBlock
            HorizontalAlignment="Left"
            Margin="80,0,0,98"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="Telemóvel"
            VerticalAlignment="Bottom"/>
        <TextBox
            Text="{Binding ClientCell, Mode=TwoWay}"
            HorizontalAlignment="Left"
            Margin="80,0,0,70"
            Grid.Row="1"
            TextWrapping="Wrap"
            Width="245"
            VerticalAlignment="Bottom"/>
        <TextBlock
            Margin="0,0,229,98"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="Número de Cliente"
            VerticalAlignment="Bottom"
            HorizontalAlignment="Right"
            Width="111"/>
        <TextBox
            Text="{Binding ClientID, Mode=TwoWay}"
            Margin="0,0,96,70"
            Grid.Row="1"
            TextWrapping="Wrap"
            VerticalAlignment="Bottom"
            HorizontalAlignment="Right"
            Width="245"/>
        <TextBlock
            Margin="0,120,321,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="Fax"
            VerticalAlignment="Top"
            HorizontalAlignment="Right"
            RenderTransformOrigin="-1.579,0.438"/>
        <TextBox
            Text="{Binding ClientFax, Mode=TwoWay}"
            Margin="0,140,96,136"
            Grid.Row="1"
            TextWrapping="Wrap"
            d:LayoutOverrides="Height"
            HorizontalAlignment="Right"
            Width="245"/>
        <TextBlock
            Margin="0,59,293,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            Text="Telefone"
            VerticalAlignment="Top"
            HorizontalAlignment="Right"/>
        <TextBox
            Text="{Binding ClientPhone, Mode=TwoWay}"
            Margin="0,79,96,0"
            Grid.Row="1"
            TextWrapping="Wrap"
            VerticalAlignment="Top"
            HorizontalAlignment="Right"
            Width="245"/>
        <Button
            Content="Adicionar"
            HorizontalAlignment="Right"
            Margin="0,0,8,8"
            Grid.Row="1"
            VerticalAlignment="Bottom"
            Width="75">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <!--Quando chamado o evento click, é executado o commando SubmitForm no ViewModel -->
                    <i:InvokeCommandAction Command="{Binding SubmitForm}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </Grid>
</UserControl>


E Finalmente temos a aplicação a funcionar com MVVM e nenhum código na View a não ser a definição do User Interface.

image

 

Solução VS2010 do projecto executado neste tutorial disponível aqui para download.

Referências:

http://www.silverlight.net/learn/videos/silverlight-4-videos/mvvm-introduction/

http://www.silverlight.net/learn/tutorials/silverlight-4/using-the-mvvm-pattern-in-silverlight-applications/

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090016

Comunidade NetPonto na UTAD – Evento Presencial

Segunda-feira, Janeiro 31st, 2011

No dia 12/02/2011 a Comunidade NetPonto irá reunir-se em Vila Real pela 1ª vez, na Universidade de Trás-os-Montes e Alto Douro (UTAD). Para participar, efectue o registo de acordo com as instruções abaixo.

Agenda

13:45  – Recepção dos participantes

14:00 – Boas-vindas e apresentação da Comunidade NetPonto

14:10 – Desenvolvimento de Aplicações para Android em C# com o MonoDroidBruno Pires

15:40 – Integração Contínua com o HudsonBruno Lopes

17:10 – Coffee-break

17:40 – Desenvolvimento de Jogos em XNA para Windows Phone 7Virgílio Esteves e Andreas Vilela

19:10 – Painel de Discussão


Registo / Inscrição

Para participar, basta efectuar a inscrição através do site http://netponto-utad-fevereiro-2011.eventbrite.com/.
A entrada é gratuita.
Qualquer dúvida / esclarecimento, entre em contacto connosco.


Local

UTAD (Vila Real) – Engenharias I
Quinta de Prados
5001-801 Vila Real

Clique para ampliar o mapa.

Trajecto aconselhado desde Lisboa, Coimbra, Évora, Beja, Faro:
http://bit.ly/ehzMEE
Trajecto aconselhado desde o Porto:
http://bit.ly/fcgQIl
Trajecto aconselhado desde Portalegre, Castelo Branco, Guarda:
http://bit.ly/dHHt07

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

Herança, Polimorfismo e Classes Abstractas – Quick Tips #2

Terça-feira, Julho 6th, 2010

Quando, a nível profissional trabalhamos em certas áreas de negócio, por vezes torna-se muito complexo implementar alguns dos conceitos básicos de POO (Programação Orientada a Objectos) que aprendemos.

Nestes casos é necessário fazer um esforço extra, abstrairmo-nos dos problemas de implementação das regras de negócio, dos sistemas existentes e desenvolver uma plataforma que, além de conciliar as regras de negócio e regras de arquitectura de software, tira partido das melhores funcionalidades oferecidas pela linguagem a ser utilizada.

É certamente o caminho para uma manutenção acessível e escalável, que vai garantir a longevidade da plataforma.

Herança e Polimorfismo são alguns dos conceitos mais importantes de POO, vou falar um pouco de cada um e apresentar uma pequena demonstração.

 

Herança

Por vezes as próprias regras de negócio estão agrupadas em famílias, temos que aproveitar esse factor a nosso favor e desenvolver uma estrutura do mesmo tipo por forma a que a arquitectura final seja consistente.

Recorrendo à herança entre classes podemos desenvolver uma estrutura de classes agrupadas por família, e que por sua vez têm informação organizada e transversal a toda a aplicação.

Vamos olhar para um exemplo real:

public class Angariacao
{
    public string Nome          { get; set; }
    public string Morada        { get; set; }
    public string EpocaBaixa    { get; set; }
    public string EpocaAlta     { get; set; }
    public string Enquadramento { get; set; }
}

public class CasaDeCampo : Angariacao
{
    public int    Capacidade    { get; set; }
    public string Tipologia     { get; set; }
}

public class HotelRural : Angariacao
{
    public string Actividades   { get; set; }
    public string Exterior      { get; set; }
    public string AreaSocial    { get; set; }
}

 

Como podemos verificar facilmente depois de analisar o código apresentado anteriormente, existe uma lógica para agrupar todas as propriedades partilhadas pelas classes CasaDeCampo e HotelRural.

A maioria das propriedades que cada uma das classes teria, estão agora na classe Angariação e essas propriedades vão ser partilhadas nas classes  CasaDeCampo e HotelRural através de herança.

Temos um grande número de vantagens ao utilizar este conceito de POO, uma delas é a fácil manutenção: caso se tenha que alterar/adicionar/remover uma propriedade na classe base, essa alteração é reflectida de forma automática em todas as classes que herdam da classe base.  Torna-se também mais rápido e eficiente adicionar novas classes que herdem da classe base, pois já existem nela implementadas muitas das funcionalidades básicas bem como as propriedades partilhadas que são transversais à família de classes.

 

Polimorfismo e Classes Abstractas

Polimorfismo significa “tomar várias formas”, quando utilizado em POO significa que uma assinatura de um método pode ter várias implementações diferentes.

Uma classe abstracta não pode ser instanciada, apenas define as propriedades e métodos que as classes filho que vão herdar.

Para exemplificar este conceito vamos recorrer às classes apresentadas no exemplo anterior e continuar a explorar o mesmo conceito:

//A classe passou a ser abstracta
public abstract class Angariacao
{
    public string Nome          { get; set; }
    public string Morada        { get; set; }
    public string EpocaBaixa    { get; set; }
    public string EpocaAlta     { get; set; }
    public string Enquadramento { get; set; }

    //Definição do método a ser implementado nas
    //classes que herdam de Angariacao
    public abstract void ImprimirTipoClasse();

}

public class CasaDeCampo : Angariacao
{
    public int    Capacidade    { get; set; }
    public string Tipologia     { get; set; }

    //Quando o método tem a assinatura abstract na classe base
    //é obrigatório implementar o override nas classes que herdam
    //da classe base
    public override void ImprimirTipoClasse()
    {
        Console.WriteLine(string.Format("Tipo: {0}", this.GetType()));
    }
}

public class HotelRural : Angariacao
{
    public string Actividades   { get; set; }
    public string Exterior      { get; set; }
    public string AreaSocial    { get; set; }

    //Quando o método tem a assinatura abstract na classe base
    //é obrigatório implementar o override nas classes que herdam
    //da classe base
    public override void ImprimirTipoClasse()
    {
        Console.WriteLine(string.Format("Tipo: {0}", this.GetType()));
    }
}

 

Neste momento temos definida a família angariação, que tem como base  a classe Angariação e filhos as classes CasaDeCampo e HotelRural.

A classe Angariação é abstract porque na lógica de negócio a implementar não existem objectos da classe base.

Como na classe base está declarado o método ImprimirTipoClasse com a assinatura abstract, a implementação de este método é obrigatória nas classes filho.

Para validar estes conceitos, criei um programa muito simples e que demonstra o potencial da Herança, Polimorfismo:

    class Program
    {
        static void Main(string[] args)
        {
            //lista de objectos da classe base
            var list = new List<Angariacao>();

            //adicionar à lista objectos que 
            //herdam da classe base
            list.Add(new CasaDeCampo());
            list.Add(new HotelRural());

            foreach (var item in list)
            {
                //Imprimir no ecrã o tipo de objecto
                //Demonstração final de Polimorfismo
                item.ImprimirTipoClasse();
            }

            Console.ReadKey();
        }
    }

Que resulta no seguinte ecrã quando se executa a aplicação:

heranca_e_polimorfismo

 

 

Para concluir, podemos verificar ao analisar a execução do programa que, embora a lista seja de objectos da classe base, podemos adicionar objectos que herdam da classe base, e quando invocamos o método ImprimirTipoClasse, o resultado é a execução do método que está definido classe filho.

Para criar um exemplo mais real, se a classe base tiver um método abstract que se chama AdicionarAngariacao, quando executado no programa da mesma forma que no exemplo acima demonstrado, iria ser adicionada uma Casa de Campo e um Hotel Rural ao sistema de acordo com as suas propriedades partilhadas e específicas, pois cada uma das classes filho implementa o método AdicionarAngariacao da forma mais conveniente.

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.

Visual Studio Configuration Manager

Segunda-feira, Maio 3rd, 2010

visual_studio_ars

Durante o ciclo de vida de uma aplicação, seja durante o seu desenvolvimento ou manutenção, é prática comum distribuir a aplicação por um conjunto distinto de ambientes, de forma a garantir  qualidade no software produzido.

 

Estes ambientes são definidos em conjunto com as funções que desempenham, normalmente são três: desenvolvimento, testes e produção.

Cada ambiente é uma caixa estanque, tem o seu próprio conjunto de servidores (Web, SQL, etc..) de forma a que estejam reunidas todas as condições para que um ambiente não necessite de aceder a recursos de outro ambiente para funcionar, garantindo-se assim consistência.

Podemos olhar para cada um destes ambientes como uma etapa no decorrer de um desenvolvimento num projecto. No ambiente de desenvolvimento o developer pode testar toda e qualquer situação sem necessitar de se preocupar por exemplo com  a consistência de dados na base de dados. O ambiente de testes é aquele em que depois de estarem concluídos os desenvolvimentos são executados todo o tipo de testes para garantir que os desenvolvimentos efectuados se encontram correctos, tanto do ponto de vista técnico como nas regras de negócio. Normalmente este ambiente necessita de ter garantida a consistência de dados. Depois de estes dois estágios estarem concluídos temos o ambiente de produção, este é o ambiente no qual  utilizador da aplicação  efectua as suas operações e toda a aplicação necessita estabilidade.

Como estamos a falar de três ambientes, cada um deles com configurações específicas, como é o caso de endereços de servidores web, sql, entre outros, por vezes torna-se difícil gerir as configurações correctas para o ambiente que vamos executar uma publicação.

Para nos ajudar nesta situação, o Visual Studio disponibiliza uma ferramenta para este fim, o Configuration Manager.

Todos os developers utilizam esta ferramenta diariamente, só que normalmente apenas para trocar a configuração de Debug para Release ou vice-versa. No entanto esta ferramenta permite fazer muito mais, permite criar ambientes personalizados com configurações específicas, como um web.config personalizado e adequado a cada um dos ambientes.

Neste post vou explicar como criar e configurar ambientes personalizados no Configuration Manager do Visual Studio, que permitem garantir que, ao alterar o ambiente em utilização, a aplicação está a utilizar todas as configurações específicas desse mesmo ambiente de forma automática e sem a necessidade de intervenção do developer.

 

1º Passo:

config_01

Criar uma directoria na raíz do projecto, com o nome “Ambientes”, e dentro dessa pasta criar uma directoria para cada um dos ambientes que vamos personalizar.

Copiar para a directoria de cada ambiente o seu web.config personalizado e adequado.

De seguida abrir o Configuration Manager.

 

2º Passo:

config_02

config_03

 

Criar uma configuração para cada um dos ambientes que vamos criar, neste exemplo vou criar os ambientes Local, Testes e Produção.

Para os ambientes Local e Testes, vou copiar as configurações base do ambiente de Debug e para o ambiente de Produção vou efectuar a cópia do ambiente de Release.

 

3º Passo:

config_04config_05 

Remover as configurações de Debug e Release da Solution.

 

4º Passo:

config_06

Remover as configurações de Debug e Release do projecto.

 

5º Passo

config_07

config_08

Nas propriedades do projecto, ir a “Build Events” e carregar em “Edit Post-Build” e de seguida colar na caixa de texto a seguinte macro:

xcopy /R /Y "$(ProjectDir)Ambientes\$(ConfigurationName)\Web.config" "$(ProjectDir)Web.config"

Agora, sempre que necessitar de executar uma publicação, apenas necessita de seleccionar o ambiente que deseja publicar, fazer build e tem todas as configurações necessárias para executar a aplicação correctamente no ambiente seleccionado.

 

Criei um projecto de teste, uma aplicação web em que a página Default.aspx mostra que ambiente está em utilização.

config_09

Pode fazer o download do projecto de teste aqui.

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