Friday, July 25, 2008
#
(PT)
Baseado no post do Delay acerca de como podemos reduzir o tamanho de uma aplicação Silverlight 2.0 (XAP), lembrei-me de uma ferramenta que é muito conhecida nos domínios da programação para Java ME (onde cada byte conta): KZip do Ken Silverman. Para além de conseguir uma taxa de compressão superior em relação a muitos (todos?) os compressores compatíveis com PKZIP, é gratuito tanto para uso privado como comercial.
Não fiz testes tão exaustivos como o Delay, mas cá vão os resultados que obtive com a aplicação Silverlight de que falei num post anterior:
| Tamanho Inicial | PKZip (como referido pelo Delay) | Ganho com o PKZip | KZip | Ganho com o KZip |
| 334.027 bytes | 259.355 bytes | 22% | 244.340 bytes | 27% |
90KB podem não parecer nada nos dias de hoje, mas visto do ponto de vista do servidor, se tiver milhares de pedidos, esta solução já começa a ter algum interesse. Ainda para mais tendo em conta que uma vez preparado o ambiente, não dá trabalho nenhum… E mesmo o setup inicial não é assim tão complexo.
Para utilizar o KZip, foi necessário efectuar algumas alterações ao ficheiro .cmd proposto pelo Delay. Ora cá vai a minha versão:
@REM XapShrink - Recompresses XAP files smaller - Based on XapReZip by Delay, but using KZIP to do the dirty work
@REM Invoke as a VS post-build event like so:
@REM <WhereEver you created the .cmd file>\XapShrink.cmd $(TargetDir)$(TargetName).xap
@echo off
setlocal
REM Define paths to zip.exe and unzip.exe
set ZIPEXE="%~p0kzip.exe"
set UNZIPEXE="%~p0unzip.exe"
REM Define paths for intermediate files
set XAP=%1
set XAPDIR=%~p1
set XAPBAK=%1.bak
set XAPZIP=%1.zip
set TMPDIR=%XAPDIR%tmp\
REM Output a banner message
echo XapShrink: %XAP%
REM Change to XAP file directory
pushd %XAPDIR%
REM Create the temporary work folder
md tmp
REM Unzip the current XAP file
%UNZIPEXE% -o %XAP% -d %TMPDIR%
REM Change to temp file directory
cd %TMPDIR%
REM Create the new XAP using KZIP
%ZIPEXE% /y %XAPZIP% %TMPDIR%*
REM Abort if something went wrong
if ERRORLEVEL 1 goto :DONE
REM Replace original XAP file with smaller one
move /y %XAPZIP% %XAP%
del /Q *.*
cd..
rd tmp
REM Output a success message
echo XapShrink: Success
:DONE
REM Restore previous directory
popd
endlocal
Para associar este comando ao vosso projecto, basta criar um ficheiro .cmd, colar o conteúdo anterior, e de seguida, no Visual Studio, associar o comando ao evento de Post-Build, como ilustrado de seguida:
Quando é feito o build, o output do processo de recompressão é exibido na janela de Output:
Para finalizar, aqui fica o ficheiro XapShrink.cmd, prontinho para ser associado ao projecto.
(EN)
While reading Delay's post about how can we reduce the size of a Silverlight 2.0 application(XAP), it stroke me about a tool very well known from the Java ME programming domain (where every single byte counts): KZip by Ken Silverman. This tool is not only able to get a compression rate better than most (all?)PKZip compatible compressors, but is also free for private and commercial use.
I haven’t made tests as exhaustive as Delay’s but here goes the results I got, regarding the application I presented in a previous post:
| Initial Size | PKZip (as suggested by Delay) | Winnings with PKZip | KZip | Winnings with KZip |
| 334.027 bytes | 259.355 bytes | 22% | 244.340 bytes | 27% |
90KB might not look as much nowadays, but from the server’s point of view, if it serves thousands of requests, this solution starts to gather some interest. And we are talking about an optimization that once set up doesn’t need any action from the programmer’s side… And even the set ups is not that complex…
To support KZip, it was necessary to make a few changes to the .cmd file proposed by Delay. Here goes my version:
@REM XapShrink - Recompresses XAP files smaller - Based on XapReZip by Delay, but using KZIP to do the dirty work
@REM Invoke as a VS post-build event like so:
@REM <WhereEver you created the .cmd file>\XapShrink.cmd $(TargetDir)$(TargetName).xap
@echo off
setlocal
REM Define paths to zip.exe and unzip.exe
set ZIPEXE="%~p0kzip.exe"
set UNZIPEXE="%~p0unzip.exe"
REM Define paths for intermediate files
set XAP=%1
set XAPDIR=%~p1
set XAPBAK=%1.bak
set XAPZIP=%1.zip
set TMPDIR=%XAPDIR%tmp\
REM Output a banner message
echo XapShrink: %XAP%
REM Change to XAP file directory
pushd %XAPDIR%
REM Create the temporary work folder
md tmp
REM Unzip the current XAP file
%UNZIPEXE% -o %XAP% -d %TMPDIR%
REM Change to temp file directory
cd %TMPDIR%
REM Create the new XAP using KZIP
%ZIPEXE% /y %XAPZIP% %TMPDIR%*
REM Abort if something went wrong
if ERRORLEVEL 1 goto :DONE
REM Replace original XAP file with smaller one
move /y %XAPZIP% %XAP%
del /Q *.*
cd..
rd tmp
REM Output a success message
echo XapShrink: Success
:DONE
REM Restore previous directory
popd
endlocal
To associate this command to your project, you just need to create a .cmd file and paste the above content (or download the file that I link below). Then, in Visual Studio, you need to associate the command to the Post-Build event, as the following picture shows:
When you build your application, the output window shows the recompression process evolution:
To sum things up, here is the XapShrink.cmd file, ready to be associated to your favorite project :).
?>
(PT)
Como alguns de vós sabem, saiu uma nova versão (beta 2) do Silverlight 2.0.
As aplicações desenvolvidas para a versão anterior (beta 1, como é o caso da aplicação que falei no meu anterior post), devem pois ser migradas para esta nova versão. Existe a necessidade de mudar algumas chamadas, mas nada de muito preocupante no meu caso. Não vou enunciar as alterações para esta nova versão, até porque estão bem documentadas, por exemplo aqui, ou aqui. O pior foi mesmo a alteração do formato dos ficheiros que são utilizados pelo controlo MultiScaleImage. Isto fez com que fosse obrigado a obter a nova versão do DeepZoom Composer. “Até aqui tudo bem”, pensei eu, “é só abrir o projecto anterior, e exportar novamente”. Pois… Não foi bem assim… Ao abrir o projecto anterior, o Deep Zoom Composer ficou durante uns valentes 45 minutos (o tempo de ir almoçar) a “abrir o projecto”…e nada… Acabei por criar um novo projecto, importar as imagens, compor a collection, e exportar…
Já agora, saiu também uma nova versão do Expression Blend, mas esta não foi necessária para a migração da minha aplicação.
Moral da história: Ai queres usar plataformas em versão beta, queres??
Digo isto sem malícia, uma das coisas que nos é mostrada vezes sem conta na documentação que acompanha estas versões beta é precisamente que não devem ser utilizadas em ambiente de produção. É um risco que fica à responsabilidade de cada um.
(EN)
As some of you might know, there is a new Silverlight 2.0 Version available (beta 2).
Applications developed for the previous version (beta 1, as is the case of the application I presented in one of my previous posts), must be converted ASAP for this new release. There are code-breaking changes, but nothing to worry about in my case. I will not enumerate the changes, as they are well documented (see this, or this). The sad part of the history came when I realized that the output of the Deep Zoom Composer (and consequently, the input for MultiScaleImageControl) has changed. To try and solve this, I downloaded the new version of DeepZoom Composer. “So far, so good”, I thought, “I’ll just open the previous project, and export it again”. Yeah.. right.. I opened the previous project in this new version of Deep Zoom Composer, noticed that it was taking a bit to load and went lunch. After 45 minutes, it was still “opening the project” I eventually got tired of it, and ended up creating a new project, importing the images, laying out the collection and exporting…
Even though I haven’t needed it for this migration process, for the sake of completeness, I must also tell that there is a new version of Expression Blend available.
Moral: So you want to use beta version platforms, huh??
No harm intended, really. One of the things we are presented while installing and downloading such beta versions is precisely that these versions should not be used in production environment. So it is a risk that one must know how to control.
Friday, June 27, 2008
#
(PT)
Já passou muito tempo desde que fui convidado para fazer alguns blocos para o popfly, utilizando serviços do sapo. Os blocos viriam a ser depois utilizados para uma demonstração do popfly no Sapo Code Bits 2007 (eu avisei que já tinha passado muito tempo). Finalmente, "encontrei" algum tempo, e decidi agarrar num dos (vários) workitems que tenho pendentes nesta "categoria" :)
Para quem não conhece, o Popfly é uma ferramenta online para a criação de mashups. Para quem não sabe o que é um Mashup... bem... resta-me perguntar em que buraco tem andado enfiado nos últimos anos... :)
Software necessário
- Web browser :) - Testado por mim no Firefox e no Internet Explorer 7. A aplicação é baseada em Silverlight, por isso, deverá funcionar em qualquer browser que suporte Silverlight.
- Opcionalmente, pode ser utilizado um editor de texto, para a edição e manutenção de uma cópia local do código antes de submissão na aplicação Silverlight.
Nota: O código-fonte dos blocos fica visível para outros utilizadores, por isso cuidado com o código que escreverem.
Software recomendado
Referência
Para instalar o Popfly Explorer sobre o Visual Web Developer, é necessário efectuar o registo deste último. O registo é gratuito, e fica associado ao Live ID.
Quando criei os blocos Sapo Pharmacy e Sapo Maps para apresentar no Sapo Code Bits, não existia este plug-in, pelo que já agora, aproveito para o apresentar, uma vez que me parece simplificar muito o processo.
Para começar, vou apresentar os conceitos associados aos blocos, bem como o código / markup criado para a criação dos blocos Sapo Pharmacy e Sapo Maps.
Mas afinal, o que é um bloco?
Um bloco representa um serviço, e é composto por várias operações. Em Popfly, um bloco é composto por uma parte em XML, que descreve o bloco, e por uma parte em JavaScript, onde se define o comportamento do bloco.
Como começar?
Para começar, vamos ao serviço Web de pesquisa de Farmácias do Sapo, e escolhemos uma operação. Por exemplo, vamos começar com a operação "GetPharmaciesAtServiceByCoordinates". Podemos usar esta página para efectuar alguns testes, e observar o output da chamada da operação do serviço.
O que colocar nestes campos? Bem, é fácil, podemos utilizar o Sistema de Informação Geográfica do Sapo para, por exemplo obter uma lista de Distritos, ordenada pelo nome.
Surgirá uma lista de concelhos, onde cada um deles, terá o seguinte aspecto:

Fica a faltar o campo "radius". Este campo é utilizado para definir o raio da pesquisa (em metros), em torno das coordenadas seleccionadas.
Assim sendo, escolhemos um concelho, e usamos os valores associados na página anterior. (Escolhi um raio de 10Km).
Seja quais forem os parâmetros escolhidos, desde que não exista um erro nos argumentos, surgirá uma resposta semelhante a esta:
O que há aqui a reter é a estrutura da resposta, o url que surge no browser após a satisfação do pedido, que neste caso é algo do género:
http://services.sapo.pt/Pharmacy/GetPharmaciesAtServiceByCoordinates?latitude=38.8031578&longitude=-9.381098&radius=10000
Este dado também nos vai ser útil.
Criar o bloco
Esta é a parte fácil: no Popfly, menu "Create Stuff"-> "Block"
Estrutura (Markup)
Assim sendo, já é mais fácil justificar o markup que tem de ser criado para dar suporte a esta operação, no bloco Popfly (recomenda-se uma passagem de olhos rápida pelo documento que consta nas referências):
<?xml version="1.0" encoding="utf-8"?>
<!-- O código Javascript do bloco reside na classe SapoPharmacyClass, que tem um método initialize -->
<block class="SapoPharmacyClass" hasInitialize="true">
<providerName>Sapo</providerName>
<operations>
<!-- A lista de operações disponibilizadas pelo bloco. Para já apenas uma. -->
<operation name="getPharmaciesAtServiceByCoordinates"> <!-- O nome do método em JavaScript que será chamado (sobre uma instância do tipo previamente identificado) -->
<description>
Obtém uma lista de farmácias de serviço a partir das coordenadas, e num raio especificado
</description>
<inputs>
<!-- A caracterização de cada um dos argumentos da operação -->
<input name="latitude" required="true" type="latitude">
<description>A latitude do local</description>
<defaultValue>38.9982</defaultValue>
<constraints/>
</input>
<input name="longitude" required="true" type="longitude">
<description>A longitude do local</description>
<defaultValue>-9.16351</defaultValue>
<constraints/>
</input>
<input name="radius" required="true" type="nonNegativeInteger">
<description>O raio da pesquisa</description>
<defaultValue>10000</defaultValue>
<constraints/>
</input>
</inputs>
<outputs>
<!-- A caracterização do resultado da operação -->
<output isArray="true" type="custom" object="SapoPharmacy"/>
</outputs>
</operation>
</operations>
<objects>
<!-- Definição do tipo custom "SapoPharmacy", referenciado no argumento de saída -->
<object name="SapoPharmacy">
<field name="LastUpdate" isArray="false" type="date"/>
<field type="nonNegativeInteger" isArray="false" name="Code"/>
<field type="string" isArray="false" name="Name"/>
<field type="phoneNumber" isArray="false" name="Phone" />
<field type="phoneNumber" isArray="false" name="Fax" />
<field type="string" isArray="false" name="Director" />
<field type="string" isArray="false" name="Distance" />
<field type="custom" isArray="true" name="Services" object="ServiceType"/>
<field type="boolean" isArray="false" name="IsAtService"/>
<field type="boolean" isArray="false" name="IsLateNight"/>
<field type="location" isArray="false" name="Street"/>
<field type="latitude" isArray="false" name="Latitude"/>
<field type="longitude" isArray="false" name="Longitude"/>
<field type="zipCode" isArray="false" name="ZipCode"/>
<field type="integer" isArray="false" name="DistrictId"/>
<field type="location" isArray="false" name="District"/>
<field type="integer" isArray="false" name="MunicipalityId"/>
<field type="location" isArray="false" name="Municipality"/>
<field type="integer" isArray="false" name="ParishId"/>
<field type="location" isArray="false" name="Parish"/>
</object>
<!-- Definição do tipo custom "ServiceType", referenciado no tipo custom SapoPharmacy, no campo "Services" -->
<object name="ServiceType">
<field type="date" isArray="false" name="Date"/>
<field type="string" isArray="false" name="Type"/>
<field type="date" isArray="false" name="LastUpdate"/>
</object>
</objects>
</block>
Penso que a descrição é bastante auto-explanatória (hmm isto existe?..), e que não são necessárias mais explicações desta parte.
Comportamento (JavaScript)
De seguida, basta definir o código JavaScript para realizar a operação definida no Markup:
function SapoPharmacyClass()
{
}//Esta função é chamada antes de ser iniciado o render do bloco.
SapoPharmacyClass.prototype.initialize = function() {
if (environment.designTime)
throw "Isto não é suportado pelo bloco de farmácias do sapo em modo de desenho.";
environment.output("<LINK REL=StyleSheet HREF=\"http://farmacias.sapo.pt/css/farmacias.css\" TYPE=\"text/css\>");
}
//A operação propriamente dita:
SapoPharmacyClass.prototype.getPharmaciesAtServiceByCoordinates = function (latitude, longitude, radius) {
//Lembram-se da query string que surgiu quando foi feito o pedido com os argumentos de teste?
url = "http://services.sapo.pt/Pharmacy/GetPharmaciesAtServiceByCoordinates?latitude="+latitude+"&longitude="+longitude+"&radius="+radius;
return this.__getResults(url, "GetPharmaciesAtServiceByCoordinatesResult");
};
SapoPharmacyClass.prototype.__getElementValue = function (rootElement, tagName){
return rootElement.getElementsByTagName(tagName)[0]&&rootElement.getElementsByTagName(tagName)[0].firstChild!==null?rootElement.getElementsByTagName(tagName)[0].firstChild.nodeValue : "";
};
SapoPharmacyClass.prototype.__getResults = function (url, firstElemTagName) {
var resultsArray = new Array();
var resultXML = environment.getXml(url); //Este método é fornecido pelo popfly. Sem este método, seria impossível realizar este código, porque o browser não permite chamadas em script para outros domínios.
//boring... interpretação da resposta......
if(resultXML.getElementsByTagName(firstElemTagName).length >= 1)
{
var total = resultXML.getElementsByTagName("Total")[0]?resultXML.getElementsByTagName("Total")[0].firstChild.nodeValue : "";
var pharmaciesNodes = resultXML.getElementsByTagName("Pharmacy");
var pharmaciesCount = pharmaciesNodes.length;
for(var i = 0; i < pharmaciesCount; i++)
{ //Elementos simples
var LastUpdate = this.__getElementValue(pharmaciesNodes[i], "LastUpdate");
var Code = this.__getElementValue(pharmaciesNodes[i], "Code");
var Name = this.__getElementValue(pharmaciesNodes[i], "Name");
var Phone = this.__getElementValue(pharmaciesNodes[i], "Phone");
var Fax = this.__getElementValue(pharmaciesNodes[i], "Fax");
var Director = this.__getElementValue(pharmaciesNodes[i], "Director");
var Distance = this.__getElementValue(pharmaciesNodes[i], "Distance");
var IsAtService = this.__getElementValue(pharmaciesNodes[i], "IsAtService");
var IsLateNight = this.__getElementValue(pharmaciesNodes[i], "IsLateNight");
//tipos complexos
//Address
var Address = pharmaciesNodes[i].getElementsByTagName("Address")[0];
var Street = this.__getElementValue(Address, "Street");
var ZipCode = this.__getElementValue(Address, "ZipCode");
var DistrictId = this.__getElementValue(Address, "DistrictId");
var District = this.__getElementValue(Address, "District");
var MunicipalityId = this.__getElementValue(Address, "MunicipalityId");
var Municipality = this.__getElementValue(Address, "Municipality");
var ParishId = this.__getElementValue(Address, "ParishId");
var Parish = this.__getElementValue(Address, "Parish");
//Coordinates
var Coordinates = Address.getElementsByTagName("Coordinates")[0];
var Latitude = this.__getElementValue(Coordinates, "Latitude");
var Longitude = this.__getElementValue(Coordinates, "Longitude");
//Services
var ServicesNodes = pharmaciesNodes[i].getElementsByTagName("Service");
var ServicesCount = ServicesNodes===null?0:ServicesNodes.length;
var servicesArray = new Array();
for(var k = 0; k < ServicesCount; k++){
var Date = this.__getElementValue(ServicesNodes[k], "Date");
var Type = this.__getElementValue(ServicesNodes[k], "Type");
var LastUpdate2 = this.__getElementValue(ServicesNodes[k], "LastUpdate");
servicesArray[k] = new SapoPharmacyService(Date, Type, LastUpdate2);
}
resultsArray[i] = new SapoPharmacy(LastUpdate, Code, Name, Street, Latitude, Longitude, ZipCode, DistrictId, District, MunicipalityId, Municipality, ParishId, Parish, Phone, Fax, Director, Distance, servicesArray, IsAtService, IsLateNight);
}
}
return resultsArray; //array de instâncias do tipo SapoPharmacy, tal como descrito no Markup.
};
//usado para encapsular tipo de dados SapoPharmacyService
function SapoPharmacyService(date, type, lastUpdate){
this.Date=date;
this.Type=type;
this.LastUpdate=lastUpdate;
}
//usado para encapsular tipo de dados SapoPharmacy
function SapoPharmacy(lastUpdate, code, name, street, latitude, longitude, zipCode, districtId, district, municipalityId, municipality, parishId, parish, phone, fax, director, distance, services, isAtService, isLateNight)
{
this.LastUpdate = lastUpdate;
this.Code=code;
this.Name=name;
this.Phone=phone;
this.Fax=fax;
this.Director=director;
this.Distance=distance;
this.Services=services;
this.IsAtService=isAtService;
this.IsLateNight=isLateNight;
this.Street = street;
this.ZipCode = zipCode;
this.DistrictId = districtId;
this.District = district;
this.MunicipalityId = municipalityId;
this.Municipality = municipality;
this.ParishId = parishId;
this.Parish = parish;
this.Latitude = latitude;
this.Longitude = longitude;
}
//Utilizado pelo popfly quando o bloco é o último na cadeia, não entregando os resultados a outro bloco.
SapoPharmacy.prototype.toString = function(){
var sb = new Sys.StringBuilder();
sb.append("<div style='height:110px' class='farm-destaque'><h2>");
sb.append(this.Name);
sb.append("</h2>");
if (this.Distance>0){
sb.append("Diste2ncia ");
var dist = parseFloat(this.Distance);
dist=dist/1000;
sb.append(dist.toFixed(2));
sb.append(" Km <br />");
}
sb.append(this.Street);
sb.append("<br/>");
sb.append(this.ZipCode);
sb.append(" ");
sb.append(this.Parish);
sb.append("<br/>Tel. ");
sb.append(this.Phone);
sb.append("<br/><a href='http://mapas.sapo.pt/#c");
sb.append(this.Latitude);
sb.append("_");
sb.append(this.Longitude);
sb.append("_17'>Mapa</a></br></div>");
return sb.toString(); //tirar partido dos mapas do sapo para apresentar a localização da farmácia em questão
};
Este último método (toString), é chamado pelo Popfly quando o output do nosso mashup não é entregue a mais nenhum bloco, isto é, quando o nosso bloco está no fim da cadeia de invocação:
Entretanto reparei que a página dos mapas foi alterada, e os pedidos na forma http://mapas.sapo.pt/#c<LATITUDE>_-<LONGITUDE>_<NIVEL_ZOOM> que utilizava no toString, deixaram de funcionar desde que fiz o bloco (era apresentado um mapa centrado nas coordenadas especificadas, ao nível de zoom indicado)... Estive a ver assim meio à pressa e não vi como é o novo formato para obter o mesmo efeito. Se alguém souber, apite!
Pronto agora basta guardar, dar nome, etc etc.
Bem, agora que temos o nosso bloco criado, toca a fazer Mashups!
Criar um Mashup
Aqui vem a parte divertida da coisa!
Menu "Create Stuff"->"Mashup"
Do lado esquerdo da aplicação, existe uma lista de blocos já existentes. Mesmo no fim dessa lista, surgem os blocos criados por nós.
Lá deve residir o nosso Sapo Pharmacy. Cliquem nele (ou arrastem-no para a superfície de edição), e alterem as definições dos dados de entrada:
Aqui coloquem os valores que pretenderem (ou obtenham-nos através de outro bloco...)
Para um exemplo simples, vamos apenas exibir os resultados da pesquisa num mapa do Virtual Earth:
Agora basta fazer "Run", e voilá:
Agora que me preparava para juntar aqui o Bloco do Sapo Mapas que fiz também para o Sapo Code Bits, reparei que a malta do sapo mudou também o controlo dos mapas! Aqueles malandros... Lixaram-me a demo... Pronto, mas já deu para ficar com a ideia de como criar um bloco simples que consome dados de um Serviço. O dos mapas também tinha alguns conceitos engraçados, mas paciência... O código continua disponível no Popfly (tornei o bloco público), mas actualmente não funciona.
Para a próxima vez que não tiver nada para fazer num serão (ou que não me apeteça fazer o que devia :) ), apresento o Popfly Explorer. Uma das coisas interessantes que vi é a geração de blocos de forma automática a partir do contrato WSDL. Isto promete...
Sunday, June 15, 2008
#
(PT)
À algumas semanas atrás, o meu primo desafiou-me a fazer um pequeno facelift ao site da empresa onde ele trabalha.
Não sendo propriamente uma pessoa com uma veia artística muito forte (zero...), centrei o facelift na componente tecnológica, e aproveitei para experimentar algumas tecnologias, APIs e bibliotecas que nunca tinha utilizado, tais como flash, script.aculo.us, google maps (2D e 3D) e, claro está, Silverlight 2.0.
Ora uma das novidades em Silverlight 2.0, é a integração do trabalho realizado pela equipa do SeaDragon, sob a forma do controlo MultiScaleImage.
Para explorar este controlo, adquiri as imagens com o scanner, a 800dpi, cortei as imagens de forma a ficarem todas da mesma dimensão, adicionei uma marca de água (requisito do "cliente" :)), e importei as imagens para o DeepZoom Composer. Como queria ficar com a hipótese de ordenar as imagens e ter controlo sobre cada uma delas individualmente, criei uma colecção no DeepZoom composer. Desta forma, cada imagem passa a ser representada por uma instância de MultiScaleSubImage, e podem ser manipuladas individualmente.
Durante o desenvolvimento desta demo, deparei-me com diversas questões e problemas, que eventualmente apresentarei à medida que for tendo disponibilidade.
Aqui fica um exemplo de como Silverlight abre portas para um conjunto de soluções muito interessantes, com pouco esforço (relativamente ao que seria normal). Para ficarem com uma ideia da simplicidade do desenvolvimento desta demo, demorei mais ou menos o mesmo tempo a preparar a colecção de imagens (aquisição, corte, marcas de água, e finalmente, composição no DeepZoom Composer) que a desenvolver a aplicação em Silverlight...
A colecção de imagens tem, no total, cerca de 600 MB (a versão após tratamento por parte do DeepZoom composer, com os diversos tiles nos diferentes níveis de zoom).
Deixo só uma nota final para a possibilidade de efectuar Debug das aplicações Silverlight 2.0, quando a aplicação está a correr no browser, que para mim, é fantástico.
É até possível fazer debug de aplicações Silverlight quando o browser em utilização é o Firefox, por exemplo. Depois disto, quando fui fazer as partes em Flash, fiquei mesmo com saudades desta capacidade...
(EN)
A few weeks ago, my cousin, challenged me to do a small facelift to the site of the company he works on.
Not being someone into design (no, not really...), I tried to compensate it on the technological side, and so I used this opportunity to try some Web Technologies, APIs and libraries that I have never used before, such as flash, script.aculo.us, google maps (2D e 3D) and, finally, the motif of this post, Silverlight 2.0.
One of the new things in Silverlight 2.0, that I explored is the integration of SeaDragon's Team work, that is working under the hood of the new MultiScaleImage control.
To explore this control, I acquired the images with the scanner, at 800dpi, cut the images such that all ended up the same size, added some watermark (as required by the "customer" :)), and imported the images to DeepZoom Composer. Because I wanted to be able to order and manipulate each of the images individually, I've created a DeepZoom collection. This way, each image gets represented in Silverlight, as an instance of the type MultiScaleSubImage, and we can deal with each one of them individually.
During the development of this demo, I've encountered several issues and problems, and I will eventually show the solution to some of them, as I get some spare time.
So here it goes, an example of how you can use Silverlight to a set of very interesting solutions, with relative low effort. To get an idea of the effort involved in the development of this demo, I took more or less the same time to prepare the image collection (including acquisition, cut, watermarking, and composition in DeepZoom Composer) and develop the Silverlight application.
The image collection has, in it's final state (as processed by DeepZoom Composer, and deployed to the server, including the various tiles at different zoom levels), around some 600 MB.
Just a final note on Silverlight 2.0 application's Debug support while it is working in the Browser, that just RULES!! :)
You can actually debug a Silverlight Application even if it is running on Firefox. Man, I missed this a lot when I made the flash controls of the site...
Friday, March 14, 2008
#
Todos nós já participámos em várias apresentações em que damos pelo orador (por vezes, nós...) a tentar descobrir como aumentar o tamanho da letra de uma determinada aplicação para que todos os participantes consigam ler o conteúdo que o orador refere.
Por exemplo, no Visual Studio, o "truque" normalmente utilizado é o aumento do tamanho da letra. No entanto, caso se esteja a demonstrar a User Interface, ou outro item que não permita a alteração da dimensão, o problema persiste. (Como uma nota adicional, achei muito interessante a possibilidade oferecida pelo Expression Blend, em que é possível redimensionar toda a UI).
Pois bem, durante uma sessão do TechDays 2008 dada pelo Edwin Yuen, encontrei uma solução muito interessante para este problema (sim, a sessão em si também foi muito interessante :) ).
O apresentador em causa estava a utilizar um pequeno aplicativo (44KB) de seu nome ZoomIt, do nosso amigo Mark Russinovich (SysInternals), que já nos vem habituando a software muito prático, de grande qualidade e de grande utilidade, tal como o Process Explorer, Registry Monitor, etc etc (Aconselho vivamente uma visita ao site da Sysinternals (recentemente adquirida pela Microsoft)). Todas estas ferramentas são de download e utilização gratuita.
Este software foi criado pelo autor para uso pessoal durante as suas apresentações, e entre outras coisas, permite escolher uma tecla ou combinação de teclas que activa o modo de zoom. Este zoom é feito em torno da região onde se encontra o ponteiro do rato. De seguida, à medida que o ponteiro do rato é movido, é também alterada a região que se encontra ampliada.
Adicionalmente, é ainda possível desenhar e escrever sobre a área aumentada, ou em qualquer altura (com a dimensão original) para por exemplo, realçar determinados aspectos.
Outra funcionalidade disponível nesta pequena ferramenta é a apresentação de um timer em full screen, em contagem decrescente. Isto é útil entre outras coisas, para os tempos mortos antes da sessão, dando aos espectadores a noção de quanto tempo falta para que o orador comece a sua apresentação.
Monday, December 31, 2007
#
Este é o primeiro dos meus posts únicamente em Inglês. Eu sei que a comunidade é PT, mas também sei que todos sabemos pelo menos ler o Inglês razoavelmente.
A escrita dos posts só em Inglês tem a ver com a minha necessidade pessoal de praticar o Inglês escrito, e com a falta de tempo para escrever os posts em 2 línguas.
Aceitam-se comentários e/ou sugestões.
One of the Imagine Cup competitions I'm participating is the IT Challenge.
This phrase that captures the meaning of this challenge:
The IT Invitational highlights the art and science of developing, deploying, and maintaining IT systems that are efficient, functional, robust and secure.
The challenge goes around most of the IT infrastructure design and management, and students aspiring to be in France for the finals must go through a series of tests.
The first, is the online quiz (also known as round 1). All competitors go online and answer a 30 question quiz. Only the ones with more than 15 correct answers go to round 2.
The next, more challenging one is the creation of a case study around a proposed business scenario. You can check the last year's business scenario here.
The challenge, as expected relies on using Microsoft technologies for accomplishing the final design specification for the case study. However, additional networking and other general IT knowledge is essential to get even through the round 1. For example, in both quizzes I've participated, there was at least one question about CIDR addressing.
Last year, I've also participated, advanced to round 2, but haven't got the time to create the Case Study.
You can check last year's finalists proposals through Imagine Cup's website or directly through the following links:
1st place: China - Zhifeng Chen - Hua Zhong University of Science and Technology
2nd place: France - Jean-Benoit Paux - INSA de Lyon
3rd place: France - Romain Larmet - INSA de Lyon
4th place: Poland - Paul Wojcicki Jarocki - School of Banking and Management in Krakow
5th place: Romania - Andrei Alexandroni - Politehnica University of Timiosara
6th place: Romania - Ilie Cosmin Viorel - Alexandru Ioan Cuza University of Iai
The final challenge happens where the finals take place. Armed with all the necessary software and equipment, the finalists have a deadline to implement their proposal. So if you are planning on passing to the 3rd (and final) round of the IT Challenge, don't expect to see much of the City of the lights...
The first quiz for the Imagine Cup IT Challenge happened last Thursday, 27th Dec. According to the stats of one of the members of the organizing team, Portugal ranked 10th in a list with 43 countries. The highest score in Portugal was 19 out of 30 correct answers.
In this first quiz, we had 6 Portuguese participants, and all of them managed to get through!
| Rank | Country | NickName | Score |
| 92 | PT | chocolate_jesus | 19 |
| 130 | PT | ricardo-portela | 17 |
| 137 | PT | hfcpereira | 16 |
| 144 | PT | Luis Gomes | 16 |
| 148 | PT | Pedro Quinta | 16 |
| 152 | PT | ajnm | 15 |
As you can see, I am one of the competitors that advanced to round 2. I hope I can find the time to create my Case Study this year.
Just a little sidenote: As you may know, I am a Microsoft Student Partner. One of the many advantages of being a MSP, is access to some information that is not on the public domain, as well as a MSDN subscription, which in this case can enable us to simulate the scenario, as all the software is available (admitting that one also has the very expensive hardware). Also, we get to know some of the people inside MSFT PT who know a lot about these technologies. That may seem unfair to other contestants, and has been addressed by the IC organization:
(...)If you are a Microsoft campus representative (such as Microsoft Student Partners http://student-partners.com/) and you meet the eligibility criteria set forth above (Note: see the full rules here), you may enter, but you are prohibited from using Microsoft property or resources, including without limitation, Microsoft networks, hardware tools and technology resources and/or the counsel of Microsoft employees, in connection with the creation or execution of your entry.
These rules are pretty simple to follow, and this is why some of us (MSPs) are participating in this contest. I think some of us might still participate in the quizzes that follow, but in this list we have at least one more MSP (hfcpereira).
If you are a student and have registered in the IT Challenge, stay tuned for the next quizzes. There are still more chances for you to advance to round 2!
The next quiz takes place January 9th, at 00:00 GMT.
Good Luck!
Saturday, August 18, 2007
#
Este semestre eu e o meu colega Paulo Vieira do ISEL, preparámos uma apresentação acerca deste tema, no contexto da cadeira de Fundamentos de Sistemas Distribuídos.
Existiam vários temas à escolha, de entre os quais WCF, mas como este último já tinha sido escolhido, e existia a hipótese de propôr um tema que se enquadrasse, a nossa escolha recaiu sobre este tema.
Deixo aqui os conteúdos que preparámos para a apresentação, caso seja útil para quem se esteja a iniciar nesta tecnologia, ou queira dar uma apresentação sobre o tema.
O ficheiro .ZIP contém:
- Ficheiro PPTX que contém os slides preparados para a apresentação, em PowerPoint, com comentários.
- Ficheiro DOCX que contém um resumo mais detalhado da matéria em estudo. Serve de suporte adicional aos slides.
- Solution VS2008 (CodeName Orcas) com o código de exemplo que dá suporte às demos da apresentação.
Caso identifiquem alguma incorrecção ou observação, deixe em comentário, que terei todo o gosto de discutir o tema.
Wednesday, August 15, 2007
#
Ainda há dias vi este post do Daniel, e lembrei-me de uma outra forma, porventura mais interessante de obter a connection string.
Para evitar o suspense e para poupar o tempo de quem já conhece o "truque", cá vai: Ficheiros com a extensão UDL (a.k.a. "Microsoft Data Link").
Esta funcionalidade existe (pelo menos) nos Windows XP e Vista.
Passos:
1 - Criar um ficheiro de texto
2 - Mudar a extensão do ficheiro para .udl
3 - Clicar com o botão direito sobre o ficheiro, e ver as respectivas propriedades.
Aqui já começamos a ver coisas que nos são familiares...
4 - Começamos por escolher o "provider"
5 - De seguida, os passos variam consoante o "provider" seleccionado. Aqui vou ilustrar os passos para o SQL Server, via OLE DB.
É até possível obter a lista dos servidores disponíveis, assim como das bases de dados existentes num servidor específico (desde que sejam inseridas credenciais válidas para esse mesmo servidor).
É ainda possível configurar os timeouts através da aba "Advanced".
Todas as propriedades disponíveis para a ligação são configuráveis através da aba "All".
6 - De seguida, basta clicar em "Apply" para guardar as definições. Caso estejam a usar SQL Authentication e escreveram a palavra passe, vão receber um aviso, a indicar que a palavra passe fica guarada "em claro".
7 - Agora, basta abrir o ficheiro em questão (.udl) no bloco de notas, e vão encontrar a connection string, pronta a ser usada:

Boas ligações!
Tuesday, May 08, 2007
#
No passado dia 25 de Abril de 2007, eu, o Nelson Correia, o Ricardo Calejo e o Simão Pereira realizámos em conjunto um Workshop sobre Windows Forms e Web Forms no ISEC. Eu e o Simão ficámos encarregues da parte de desenvolvimento de aplicações Desktop, e o desenvolvimento Web ficou a cargo do Nelson Correia e do Ricardo Calejo.
Se quiserem saber mais detalhes do evento, podem seguir o link que se segue:
http://weblogs.pontonetpt.com/calejo/posts/13907.aspx
O que me leva a escrever este post é a vontade de disponibilizar os conteúdos que foram de alguma maneira da minha responsabilidade.
O workshop começou com uma breve introdução acerca da plataforma .NET dada por mim, com muito nervosismo à mistura... (Agora é que começo a dar o verdadeiro valor aos professores...).
Os slides que utilizei para essa sessão estão disponíveis neste endereço:
http://pwp.net.ipl.pt/alunos.isel/27394/WorkshopISEC/Slides/dotNetPPT.zip [Versão Power Point 97-2003]
http://pwp.net.ipl.pt/alunos.isel/27394/WorkshopISEC/Slides/dotNetPPTX.zip [Versão Power Point 2007]
Quem já teve aulas no ISEL, vai reconhecer alguns, senão a maioria dos slides, já que na maior parte dos casos fiz apenas alterações cosméticas, só tendo criado alguns de raíz. Os slides são de cadeiras como AVE, onde usei os acetatos do Eng. Jorge Martins e CP (Luís Falcão).
De seguida, cada uma das “turmas” foi para um laboratório diferente, e lá a ideia era criar parte de uma aplicação Windows Forms que seria responsável pela gestão de um sistema de vendas, e dos produtos e clientes associados.
De forma a simplificar a tarefa dos “alunos”, e como a ideia era desenvolver a utilização de Windows Forms, foi criada uma camada de acesso a dados que seria depois utilizada pela camada de apresentação Windows Forms. Essa camada de acesso a dados foi fornecida (incompleta, infelizmente) aos alunos, que assim poderiam utilizar os dados presentes na BD sem terem de se preocupar com os detalhes de ADO.NET.
Esta camada foi entretanto terminada, e foram criados alguns testes unitários para efectuar algumas validações. Neste campo ainda existe espaço para progredir, mas o foque da sessão não era esse, e como tal não foi dispensado muito tempo para essa componente.
Apesar de o acesso a dados não ser um dos temas principais, foi abordada a utilização de código parcialmente independente de provider (utilização do namespace System.Data.Common). A string de ligação à base de dados é carregada a partir do ficheiro App.config, onde reside também a informação acerca do