Pokazywanie postów oznaczonych etykietą html. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą html. Pokaż wszystkie posty

środa, 18 listopada 2009

[Silverlight 4 nowości] WebBrowser

Kilka godzin temu pojawiła się nowa wersja Silverlighta 4, a wraz z nią setki nowości (tak, tak, nie przesadzam.. setki!!! J ). Te najważniejsze znajdziecie chociażby w artykule Bartłomieja Zassa czy też na blogu Tim'a lub Mike'a. Ja swoją przygodę rozpoczynam od kontrolki WebBrowser, a więc czegoś co pozwoli mi wreszcie na wyświetlenie kodu html w aplikacji Silverlight :]

Niestety nie wszystko tak pięknie działa jak byśmy mogli sobie to wymarzyć i już na początku zobaczymy, że kontrolka ta działa tylko gdy uruchamiamy aplikację jako "zainstalowaną lokalnie", czyli OutOfBrowser (OOB) i dodatkowo aplikacji powinna mieć włączony tryb działania z pełnymi uprawnieniami (to również nowości w Silverlight 4) jeśli chcemy oglądać strony które nie należą do nas (cross-domain-policy). Mimo to, kontrolka działa, a kod z niej wygenerowany wygląda jak z przeglądarki :)

Demo

Zaczynamy od instalacji Silverlight 4 (http://silverlight.net/getstarted/silverlight-4-beta/ ), a więc pobieramy Silverlight 4 Tools for Visual Studio 2010 i instalujemy (zakładam że macie już Visual Studio 2010 Beta 2 - download). Po instalacji tworzymy nowy projekt Silverlight i w momencie zapytania o wygenerowanie projektu Web, wybieramy z listy Silverlight 4 (patrz rysunek).


W nowym Visual Studio 2010 + Silverlight 4 mamy kolejną nowość w postaci pełnego edytora xaml-a, a więc Blend 3 nie będzie już nam aż tak potrzebny :]

WebBrowser

Dodajemy w XAML kontrolkę WebBrowser oraz nazwę, tak abyśmy mogli odwołać się do niej z code-behind. Np. w ten sposób:

<UserControl x:Class="SilverlightApplication1.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"
   mc:Ignorable="d"
   d:DesignHeight="300" d:DesignWidth="400">
   <Grid x:Name="LayoutRoot" Background="White">
      <WebBrowser x:Name="webBrowser" />
   </Grid>
</UserControl>
Następnie w code-behind dodajemy ładowanie adresu:

public MainPage()
{
   InitializeComponent();
   this.Loaded += (o, e) => { webBrowser.Navigate(new Uri("http://microsoft.com", UriKind.Absolute)); };
}
Ostatnim krokiem jest włączenie opcji instalacji aplikacji (OOB) oraz włączenie potrzeby użycia większych uprawnień. Jeżeli nie włączymy zwiększenia uprawnień dla naszej aplikacji, będziemy mogli oglądać tylko strony które mają ustawiony dla nas cross-domain-policy lub strony wygenerowane z naszego kodu html (a więc poprzez opcję NavigateString, gdzie parametrem jest kod html).

W celu włączenia wszystkiego, klikamy prawym na projekcie Silverlightowym i wybieramy Properites:



Run and Fun

Jeśli wszystko się kompiluje, jesteśmy gotowi aby sprawdzić działanie naszej przeglądarki. Uruchamiamy aplikację i klikamy prawym klawiszem myszki gdziekolwiek. Z otwartego menu wybieramy opcję "Install …. onto this computer "


A następnie w nowym oknie opcję "Install". Zwróćcie uwagę, że skoro zaznaczyliśmy opcję potrzeby używania dodatkowych uprawnień, użytkownik otrzymuje o tym informację przed instalacją (patrz rysunek).


Kiedy aplikacja zainstalowana lokalnie uruchomi się, powinniśmy zobaczyć działającą stronę internetową wewnątrz naszej aplikacji Silverlight.




Pod prawym klawiszem myszki będziemy mieli menu prosto z przeglądarki, a nie z aplikacji Silverlight. Co fajniejsze, jeśli wejdziemy np. na http://youtube.com, zobaczymy że aplikacje Flash, czy też Silvelright również działają.

PS. W ramach zabawy próbowałem otworzyć moją aplikację Silverlight w aplikacji Silverlight, ale niestety, a raczej i dobrze!, Silverlight nie pozwolił zainstalować się ponownie z samego siebie. Aplikacja wykryła że jest zainstalowana. ;]

Moje demo

W ramach zabawy stworzyłem swoją mini przeglądarkę dostępną pod poniższym adresem (pamiętajcie o potrzebie jej zainstalowania przed użyciem):
Run demo

Przycisk "Przejdź" otwiera stronę spod wpisanego adresu, a "Hello World" generuje stronę z kodu html podanego jako string. Okno jest podzielone na dwie części, lewa jest to kontrolka WebBrowser która wyświetla stronę html, a prawa to Rectangle który jest malowany nowym typem brusha, "HtmlBrush". W momencie załadowania się strony, na Rectangle wywołuję metodę "Redraw()" i maluje wszystkim co znajduje się w kontrolce WebBrowser. Możemy także go odmalować klikając przycisk "Odmaluj".

Jak zapewne sami zauważyliście, próbowałem zrobić coś z Projection, niestety kontrolka WebBrowser sama poddaje się tym operacjom, ale już wyświetlona zawartość jest normalnie odwzorowana. A szkoda, już myślałem że będę mógł czytać sobie bloga do góry nogami :D

Kod źródłowy do dema:
WebBrowserDemo.zip
Słowem zakończenia, chciałem tylko podkreślić że wymienione funkcjonalności to nie wszystko. W internecie już możemy się doszukać przykładów z odwołaniem do kodu javascript i na odwrót, a więc całkiem pokaźny zbiór nowych możliwości.

Pozdrawiam i lecę do innych nowinek.. :]

Jacek Ciereszko

poniedziałek, 3 listopada 2008

Jak w blogger.com dodać obiekt Silverlight?

No więc, dziś otrzymałem powyższe pytanko od jednego z czytelników. Ponieważ chciałem odpisać na nie jak najsumienniej, to wyszedł z tego co raz to dłuższy tekst, aż ewaluował do rozmiaru artykułu, w którym to opiszę krótko, jak można na dwa sposoby dodać aplikację Silverlight na stronie html bloga w blogger.com i nie tylko.

Otóż moje dwa najlepsze sposoby na umieszczenie aplikacji, to:

  1. Zagnieżdżenie aplikacji w iframe,
  2. Użycie Live Control

Zanim rozpocznę opisywanie metod, powiem tylko, że mowa o edycji kodu html na blogger.com do którego najłatwiej dostać się poprzez wybranie "Dostosuj" na stronie bloga, a następnie przez Układ -> Edytuj kod HTML. Na tej stronie zobaczymy pełny kod html używany do generowania strony bloga. Tu też będziemy wprowadzać zmiany.

IFrame

Iframe zna chyba każdy i jest to najłatwiejszy sposób na umieszczenie aplikacji Silverlight na stronie html. W ten oto sposób został zamieszczony powyższy nagłówek na blogu. Kod który za to odpowiada, to:

 ..kod html...
<body>
<div id='outer-wrapper'>

<iframe frameborder='0' scrolling='no'
src='http://silverlight.services.live.com/invoke/81369/Blog%20Header/iframe.html'
style='position:absolute; width:1024px; height:350px; left: 115px; z-index: 10; '/>

<div id='wrap2' style='position:absolute; width: 842px; z-index: 20; left: 100px; top: 260px; '>
… reszta kodu html

Zwróć uwagę na ustawiony "style" na "position:absolute". Nagłówek ma to do siebie, że się nie przemieszcza, a więc może być na samej górze na stałe. Parametr "z-index" ustawiam na 10 dla nagłówka, a dla pozostałej części tekstu ustawiam 20, dzięki czemu nagłówek jest przykryty contentem z bloga.

Live Control

Live Control jest drugą opcją wymienioną na stronie http://silverlight.live.com, możemy ją znaleźć zaraz po wgraniu aplikacji (Upload application) "Add this Application to a Web Page " -> "Method 2: Use a Live Control". To co należy zrobić, to dodać linki do dwóch plików javascript oraz atrybut do taga <html> :

<html xmlns:devlive=http://dev.live.com  ….inne parametry…>
…kod html…

<script src='https://controls.services.live.com/scripts/base/v0.3/live.js' type='text/javascript'/>
<script src='https://controls.services.live.com/scripts/base/v0.3/controls.js' type='text/javascript'/>

</head>
<body >
<div>
<devlive:slscontrol silverlightVersion='2.0' src='/81369/Blog Stopka/'/>
</div>
…kod html…

A więc dodałem atrybut do tagu html (xmlns:devlive=http://dev.live.com) oraz dwa pliki javascript (live.js i controls.js). Aby umieścić aplikację wystarczył wpis <devlive:slscontrol …/>. W wyżej wymieniony sposób mam umieszczoną stopkę na stronie bloga. Zaletą tego rozwiązania jest to, że aplikacja jest traktowana jak element strony, nie musi być na stałe umieszczona przez style, np. dzięki temu moja stopka na stronie jest zawsze na końcu artykułu.

Pozdrawiam
Jacek Ciereszko

piątek, 24 października 2008

Komunikacja pomiędzy Silverlight-em 2.0, a kodem javascript

W poprzednim artykule poświeconym połączeniom pomiędzy Silverlight-em, a kodem HTML (link), pokazałem jak w prosty sposób wskazać elementy strony html z poziomu aplikacji Silverlight i je użyć. Dowiedzieliśmy się także, w jaki sposób podpiąć Silverlight-a pod zdarzenia z kodu html. Całość zaowocowała nam "grą" w Ping Ponga :)

W tym artykule, chciałem krótko pokazać w jaki sposób możemy wywołać metody aplikacji Silverlight-a z kodu javascript i na odwrót, czyli metody javascript z kodu aplikacji Silverlight. Funkcjonalność ta okazuje się bardzo przydatna, szczególnie przy pracy z Virtual Earthem, Google Maps, obiektami JSONowymi i innymi aplikacjami/skryptami stworzonymi w javascript-cie.

Agenda

  1. Odwołanie do metody w javascript-cie z Silverlight-a
  2. Odwołanie do kodu aplikacji Silverlight, z javascript-u
  3. Obsługa event-ów wywołanych w Silverlight-cie, w kodzie javascript
  4. Przykładowy działający kod

Odwołanie do metody w javascript-cie z Silverlight-a

W pliku cs (np. Page.cs) dodajemy import Browser-a:

using System.Windows.Browser;

a w pliku javascript (np. Test.js), utworzonym przez nas w projekcie, dodajemy metodę

function testMethod(nothing) {
window.alert("[javascript] hello from javascript!");
}

i wywołujemy ją z kodu Silverlight-a (Page.cs):

Object[] args = null;
Object result = HtmlPage.Window.Invoke("testMethod", args);

, parametr args jest opcjonalny, tak samo jak wartość zwracana - result.

Plik Test.js musi być podłączony do strony html/aspx, która zawiera aplikacje Silverlight

<script type="text/javascript" src="Test.js"></script>

Alternatywnie, metoda testMethod może znajdować się bezpośrednio w pliku html/aspx, za pośrednictwem znaczników

<script type="text/javascript"> ... </script>

Odwołanie do kodu aplikacji Silverlight, z javascript-u

W celu odwołania się do Silverlight-a, musimy oznaczyć nasze zmienne, klasy, properties-y i eventy specjalnymi znacznikami ([ScriptableType], [ScriptableMember]). Dlatego też, klasa do której będziemy mogli się odwołać z javascript-u, będzie miała ten atrybut

[ScriptableType]
public class ScriptableClass
{
}

, natomiast jej zmienne i metody muszą mieć znaczniki ScriptableMember, np.:

[ScriptableType]
public class ScriptableClass
{
[ScriptableMember]
public string ScriptableMethod(string value){…}

[ScriptableMember]
public string val
{
get { return _val; }
set { _val = value; }
}
}

Następnie klasa musi zostać zarejestrowana na stronie html, poprzez wywołanie (np. w pliku Page.cs):

ScriptableClass scriptableClass;
scriptableClass = new ScriptableClass();
HtmlPage.RegisterScriptableObject("ScriptableClass", scriptableClass);

Na końcu, aby odwołać się do obiektu scriptableClass i jego metod/zmiennych w kodzie javascript, piszemy (w pliku Test.js):

var control = document.getElementById("Xaml1");
result = control.Content.ScriptableClass.scriptableMethod("Hello from javascript");

, a więc odnajdujemy kontrolkę Silverlight-a po jej id na stronie html/aspx (tu ta wartość może się u Was różnić jeśli modyfikowaliście kod html/aspx, w moim wypadku jest to "Xaml1") i wskazujcie zarejestrowany obiekt (ScriptableClass) i wywołujecie na nim metody (np. scriptableMethod(..)).

<div style="height: 100%;">
<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/SilverlightApplication1.xap"
MinimumVersion="2.0.31005.0" Width="100%" Height="100%" />
</div>

Mamy już wszystko co trzeba. Po wywołaniu metody scriptableMethod() z klasy ScriptableClass w kodzie javascript, wykona się metoda w kodzie Silverlight-a.

Obsługa event-ów wywołanych w Silverlight-cie w kodzie javascript

Aby event mógł być obsłużony, należy utworzyć EventHandler (np. public event EventHandler SilverHandler) w aplikacji (ScriptableClass.cs)

[ScriptableType]
public class ScriptableClass
{
[ScriptableMember]
public event EventHandler SilverEvent;

public virtual void OnEvent(ScriptableEventArgs e)
{
if (null != SilverEvent)
{
SilverEvent(this, e);
}
e.val.ToString(); //value from javascript
}
}

Dodaliśmy także metodę OnEvent do ScriptableClass, która wywoła event SilverEvent. Teraz potrzebujemy metody która wywoła OnEvent (Page.cs):

ScriptableClass scriptableClass;

private void Button_Click_1(object sender, RoutedEventArgs e)
{
ScriptableEventArgs sea = new ScriptableEventArgs();
sea.val = "Event Args from Silverlight";

scriptableClass.OnEvent(sea);
}

Jak widać, pojawiła się dodatkowo klasa ScriptableEventArgs. Klasa ta, to nadpisana własna klasa EventArgs-ów. Musimy ją nadpisać, ponieważ potrzebujemy EventArgs-ów które są także typu [ScriptableType] ( ScriptableEventArgs.cs.)

[ScriptableType]
public class ScriptableEventArgs : EventArgs
{
private string _val;

[ScriptableMember]
public string val
{
get { return _val; }
set { _val = value; }
}
}

A więc mamy event (SilverEvent), mamy klasę argumentów (ScriptableEventArgs) oraz metodę która wywoła event. To co potrzebujemy, to obsługi eventa po stronie javascript (Test.js)

function loadedd(cos) {
var control = document.getElementById("Xaml1");
control.Content.ScriptableClass.SilverEvent = eventfunction;
}

function eventfunction(sender, args) {
value = args.val;
window.alert("[javascript] " + value);
args.val = "javascript values"; //return string
}

I tak, metoda eventfunction zostanie wywołana w momencie wciśnięcia przycisku w aplikacji Silverlight (Button_Click_1), ponieważ została przypisana do eventa SilverEvent. Przypisanie robimy podobnie jak dla wywołania metod z obiektu ScriptableClass, tylko tym razem przypisujemy metodę pod event.

control.Content.ScriptableClass.SilverEvent = eventfunction;

Ponieważ przypisanie może zostać wykonane dopiero po inicjalizacji aplikacji Silverlight, dlatego też w/w przypisanie eventa zostało zamknięte w metodę, którą ja wywołuje dopiero z metody Page_Loaded

public Page()
{
InitializeComponent();

this.Loaded += new RoutedEventHandler(Page_Loaded);
}

void Page_Loaded(object sender, RoutedEventArgs e)
{
HtmlPage.Window.Invoke("loadedd");
}

W ten sposób mam pewność, że event SilverEvent zostanie prawidłowo przypisany.

Przykładowy działający kod

Dla celów tego artykułu, stworzyłem demo aplikacji, która zapewnia prostą komunikację pomiędzy Silverlightem i javascriptem. W aplikacji mamy trzy przyciski:

  • "Call javascript" – wywoła metodę w kodzie javascript (Test.js) z aplikacji Silverlight
  • "Call Silverlight" – wywoła metodę javascritp, która wywoła metodę w aplikacji Silverlight
  • "Call javascript event" – zostanie wywołany event (SilverEvent) w obiekcie ScriptableClass. Event zostanie przekazany i obsłużony w kodzie javascript. Kod javascript zwróci rezultaty poprzez użycie wartości w klasie ScriptableEventArgs

Plik Page.xaml:

<UserControl x:Class="SilverlightApplication1.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Canvas x:Name="LayoutRoot" Background="White">
<Button Content="Call javascript" Click="Button_Click" Canvas.Left="100" Canvas.Top="50" />
<Button Content="Call javascript event" Click="Button_Click_1" Canvas.Left="100" Canvas.Top="100" />
</Canvas>
</UserControl>

Plik SilverlightApplicationTestPage.aspx

<%@ Page Language="C#" AutoEventWireup="true" %><%@ Register
Assembly="System.Web.Silverlight" Namespace="System.Web.UI.SilverlightControls"
TagPrefix="asp" %>
<!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" style="height: 100%;">
<head runat="server">
<title>SilverlightApplication1</title>

<script type="text/javascript" src="Test.js"></script>

</head>
<body style="height: 100%; margin: 0;">
<form name="CallButtons" action="" method="GET">

<input type="button" name="button" value="Call Silverlight" onclick="testScriptableClassMethod()">

</form>
<br />
<form id="form1" runat="server" style="height: 100%;">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div style="height: 100%;">

<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/SilverlightApplication1.xap"
MinimumVersion="2.0.31005.0" Width="100%" Height="100%" />

</div>
</form>
</body>
</html>

Plik Page.cs

public partial class Page : UserControl
{
ScriptableClass scriptableClass;
public Page()
{
InitializeComponent();
scriptableClass = new ScriptableClass("Hello from Silverlight");
HtmlPage.RegisterScriptableObject("ScriptableClass", scriptableClass);

this.Loaded += new RoutedEventHandler(Page_Loaded);
}

void Page_Loaded(object sender, RoutedEventArgs e)
{
HtmlPage.Window.Invoke("loadedd");
}

private void Button_Click(object sender, RoutedEventArgs e)
{
Object[] args = null;
Object result = HtmlPage.Window.Invoke("testMethod", args);
}

private void Button_Click_1(object sender, RoutedEventArgs e)
{
ScriptableEventArgs sea = new ScriptableEventArgs();
sea.val = "Event Args from Silverlight";
scriptableClass.OnEvent(sea);
}
}

Plik ScriptableClass.cs

[ScriptableType]
public class ScriptableClass
{
private string value;

[ScriptableMember]
public event EventHandler SilverEvent;

public ScriptableClass(string val)
{
value = val;
}

[ScriptableMember]
public string scriptableMethod(string val)
{
string temp = value;
value = val;
return temp;
}

public virtual void OnEvent(ScriptableEventArgs e)
{
if (null != SilverEvent)
{
SilverEvent(this, e);
}
e.val.ToString(); //value from javascript
}
}

Plik ScriptableEventArgs.cs

[ScriptableType]
public class ScriptableEventArgs : EventArgs
{
private string _val;

[ScriptableMember]
public string val
{
get { return _val; }
set { _val = value; }
}
}

Plik Test.js

function testMethod(nothing) {
window.alert("[javascript] hello from javascript!");
}


function testScriptableClassMethod() {
var control = document.getElementById("Xaml1");
result = control.Content.ScriptableClass.scriptableMethod("Hello from javascript");

window.alert("[javascript] " + result);
}

function eventfunction(sender, args) {
value = args.val;
window.alert("[javascript] " + value);
args.val = "javascript values";
}

function loadedd(cos) {
var control = document.getElementById("Xaml1");

control.Content.ScriptableClass.SilverEvent = eventfunction;
}

Pełny kod dema znajdziecie tu -> link

Resources


Pozdrawiam,
JAcek Ciereszko

wtorek, 1 kwietnia 2008

Ping pong with html and Silverlight 2.0 RTW

W moim kolejnym artykule, chciałem Wam pokazać, jak w łatwy sposób odwoływać się do elementów kodu html z poziomu aplikacji Silverlight i na odwrót. Działanie aplikacji w połączeniu z Silverlightem zaprezentowałem na podstawie prostej "gry" ping pong. Z jednej strony gra kod html, a z drugiej Silverlight. Gracz ma 8 sekund na wciśnięcie przycisku, po tym czasie gra się niby kończy. Punktów nie ma, zasad nie ma, a wszystko dlatego, że jest to przykład do nauki a nie grania!

Ponieważ kod jest już nawet duży, to omówię tylko wybrane sposoby na odwołanie się do znaczników html, a po szczegóły implementacyjne odsyłam Was do kodu źródłowego aplikacji.

Aby Silverlight mógł zawołać html

Żeby odwołać się do elementu w kodzie html, najłatwiej nadać mu znacznik "id". Choć możemy to zrobić także przeszukując potomków w drzewie HtmlDocument Najpierw stwórzmy obiekt reprezentujący kod html, w którym umieszczona jest nasza aplikacja Silverlight 2.0:

HtmlDocument _htmlDocument = HtmlPage.Document;

, gdzie HtmlPage
pochodzi z System.Windows.Browser;

HtmlPage dostarcza informacji nie tylko o stronie html, z klasy tej dowiemy się także jakiej przeglądarki używamy:

HtmlPage.BrowserInformation.Name,

i czy np. mamy włączone ciasteczka:

HtmlPage.BrowserInformation.CookiesEnabled

Aby znaleźć elementy w kodzie html, daj my na to jakis div: <div id="szukajMnie" /> wystarczy wyszukać w elemencie HtmlDocument.

HtmlElement _div = _htmlDocument.GetElementById("szukajMnie");

Warto sprawdzić przed użyciem czy otrzymany _div nie jest nullem, bardzo często się to zdaża (wiem, w kodzie demo nie sprawdzam ale się spieszyłem ;) ).

Na wyszukanym elemencie możemy zrobić dowolną operację, zmienić mu style, właściwości, usunąć wcześniej wprowadzone parametry itd.

_div.SetStyleAttribute("color", "#FF0000");

Ustawienia działają błyskawicznie i dają dużo nowych możliwości.

Aby html mógł zawołać Silverlighta

Tutaj nasze możliwości są troszkę mniejsze, choć wszystko da się zrobić poprzez małe work-around.

Jeżeli chcemy aby coś z poziomu kodu html wywołało metodę w kodzie Silverlight-a, to powinniśmy w kodzie html dodać znaczniki <form>, a w nich tagi <input> o typie "button", czyli jednym słowem, przyciski.

<form action="">

<input type="button" id="htmlButton" value="Push me! I'm in Html"/><br/>

</form>

Weźmy sobie taki przycisk jak powyżej. Skoro chcemy, aby mógł coś zmienić w aplikacji SL należy podpiąć mu Eventa.

_htmlDocument.GetElementById("htmlButton ").AttachEvent("onclick", new
EventHandler<HtmlEventArgs>(this.OnHtmlButtonClick));


Dalej postępujemy jak ze zwykłym wywołaniem. Jako przykład podam zmianę koloru we wszystkich znacznikach <p>:

publicvoid OnHtmlButtonClick(object sender, HtmlEventArgs args)
{

ScriptObjectCollection htmlElements = _htmlDocument.GetElementsByTagName("p");
foreach (HtmlElement h1Element in htmlElements)

{

h1Element.SetStyleAttribute("color", "#FFFF00");

}

}


Resoruces


Demo





Source code

(Jeśli link nie będzie działać, bardzo proszę o powiadomienie mnie o tym, komentarz czy też e-mail jest zawsze mile widziany).


kick it on DotNetKicks.com

Jacek Ciereszko