background image

Komponenty wizualne i XAML 

Informacje o module 

Opis modułu 

W  tym  rozdziale  poznasz  strukturę  komponentów  wizualnych  Silverlight: 
kształty,  kontrolki  i  kontenery.  Nauczysz  się  podstaw  manipulacji 
własnościami  komponentów  z  użyciem  składni  XAML.  Nauczysz  się 
manipulowad układami współrzędnych. 

Cel modułu 

Moduł  ma  za  zadanie  wprowadzid  strukturę  komponentów  SIlverlight  w 
sposób  funkcjonalny.  Wprowadza  się  podstawy  składni  XAML.  Szczegółowo 
omawiana jest manipulacja układami współrzędnych: tworzenie i składanie. 

Uzyskane kompetencje 

Po zrealizowaniu modułu będziesz: 

 

wiedział  jak  wygląda  organizacja  komponentów  Silverlight  oraz  jakie 
funkcjonalności wyodrębniono, 

 

potrafił  manipulowad  własnościami  wizualnymi  komponentów  z 
poziomu XAML,  

 

rozumiał zasady rządzące wyświetlaniem komponentów wizualnych. 

Wymagania wstępne 

Przed przystąpieniem do pracy z tym modułem powinieneś: 

 

znad podstawy składni XML 

 

rozumied zasady dziedziczenia w projektowaniu obiektowym 

 

pamiętad  czym  jest  prostokątny  układ  współrzędnych  oraz  czym  są 
współrzędne punktu w takim układzie 

 
 

background image

 

Przygotowanie teoretyczne 

Przykładowy problem 

Załóżmy, że musisz szybko stworzyd mały edytor który pozwoli rozmieścid na białej kartce zdjęcia i 
teksty. Użytkownik powinien mied możliwośd przesuwania, obracania i skalowania tych elementów. 
Jeśli  dopiero  rozpoczynasz  przygodę  z  Silverlight,  to  bezwzględnie  najważniejsze  jest  poznanie 
struktury komponentów wizualnych oraz zasad nimi rządzących. 

Silverlight 4 daje Tobie do dyspozycji ponad 60 kontrolek. Mimo tego, że kontrolki te realizują różne 
zadania  posiadają  ważne  wspólne  cechy.  Poznanie  tych  cech  pozwoli  Ci  łatwo  projektowad 
rozwiązania wykorzystując już przygotowane elementy. 

Podstawy teoretyczne (czyli od ogółu do szczegółu) 

Praca z XAML 
Tworzenie kodu interfejsu aplikacji przy pomocy XAML pozwala ująd przy pomocy jednego wspólnego 
standardu takie cechy jak: 

 

Struktura komponentów (logiczna i wizualna), 

 

Style oraz wzorce kontrolek, 

 

Automatycznie tworzony kod działający w tle, 

 

Możliwośd importowania wzorców z narzędzi stworzonych dla grafików (jak Expression Blend). 

 
Jak wiesz z poprzedniego rozdziału kod XAML najprostszej aplikacji Silverlight rozpoczyna się od 

<Application  
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 

 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" …> 

</Application> 

Natomiast kod głównej strony (kontrolki) rozpoczyna się od 

<UserControl  

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 

    xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml …> 

</UserControl> 

Cechą  wspólną  są  przestrzenie  nazw.  Przestrzeo  nazw  presentation  zawiera  definicje 
najważniejszych elementów Silverlight, np.: wzorce językowe kontrolek, atrybutów. 

Oznacza to, że interpreter przetwarzając wiersz (umieszczony w znaczniku UserControl) 

<Button Name=”button1”>Przycisk</Button> 

Poza  samą  analizą  składni  (znacznik  otwarty  –  znacznik  zamknięty)  sprawdzi  czy  znacznik  Button 
może  posiadad  własnośd  Name. Maszyna  uruchomieniowa  Silverlight  strukturze  znacznik  –  dziecko 
nada znacznie tworząc elementy interfejsu użytkownika.  

Podobne efekt możemy uzyskad w C#: 

Button bt = new Button(); 

bt.Name = “button1”; 

TextBlock tb = new TextBlock(); 

Tb.Text = “Przycisk”; 

background image

bt.Content = tb; 

Podobieostwo  występuję  tylko  na  pierwszy  rzut  oka,  natychmiast  pojawia  się  wątpliwośd  co  do 
kontekstu  stworzenia  przycisku.  Kod  XAML  ze  względu  na  strukturę  element  nadrzędny  -  element 
podrzędny potrafi określid strukturę kontrolka-rodzic/kontrolka-dziecko, wiążąc kontrolki w strukturę 
drzewa wizualnego

Zmiana zawartości drzewa wizualnego (zmiana rozmieszczenia kontrolek) możliwa jest przez edycję 
kodu XAML i nie wymaga specjalnie przygotowanego środowiska uruchomieniowego. 

Elastycznośd interfejsu użytkownika uzyskujemy dzięki dobrze zaprojektowanemu zbiorowi kontrolek 
oraz możliwości ich kontroli. Kontrola komponentów odbywa się przez system własności (szerokośd, 
wysokośd, czcionka, kolor itp.:). Kolejne rozdziały opisują własności podstawowe kontrolek Silverlight. 
Dla programisty XAML ważny jest sposób definiowania tych własności. Trzeba pamiętad, że finalnie 
przypisanie dowolnej wartości wymagało będzie stworzenia przez Silverlight odpowiednich obiektów. 

Przypisywanie  atrybutowe  wartości  polega  na  dostępie  do  własności  jak  do  atrybutu  XML  i 
przypisaniu jej wartości w postaci łaocucha znaków, tzn.: 

<znacznik atrybut=”wartość” … /> 

Silverlight znając typ atrybutu dokona w tym przypadku konwersji łaocucha (o ile jest możliwa), np.: 

<Button Background=”Yellow” /> 

Drugą metodą przypisania wartości jest użycie składni własności, tzn.: 

<znacznik> 

  <znacznik.własność> 

   

Wartość możliwa do budowy jako obiekt 

  </znacznik.własność> 

</znacznik> 

Wcześniejszy przykład napisad można dokładniej, mianowicie: 

<Button> 

  <Button.Background> 

   

<SolidColorBrush Color="Yellow"/> 

  </Button.Background> 

</Button> 

Porównanie  obu  przykładów  pozwala  wnioskowad  jak  działa  konwerter  przypisania  atrybutowego. 
Konwerter ten rozpoznając łaocuch jako literał związany z kolorem podstawowym tworzy w sposób 
niewidoczny  obiekt  typu  SolidColorBrush,a  następnie  jego  własności  Color  przypisuje  kolor 
odpowiadający łaocuchowi Yellow

Przestrzeo  nazw  XAML  dostępna  dzięki  przedrostkowi  x:  pozwala  w  szczególności  nadad  nazwę 
dowolnemu elementowi XAML dzięki konstrukcji x:Name, np.: 

<SolidColorBrush x:Name="kolor_wypelnienia" Color="Yellow"/> 

Więcej cech języka XAML poznasz w pozostałych modułach. Zaprezentowany tutaj zarys w zupełności 
wystarcza aby rozpocząd przygodę z Silverlight 4. 

Struktura projektowa Silverlight 
Wszystkie  komponenty  wizualne  w  Silverlight  posiadają  wspólne  cechy.  Cech  te  ujawnia  struktura 
dziedziczenia typów podstawowych: 

background image

DependencyObject

UIElement

FrameworkElement

 

Rys. 1 Typy podstawowe 

Podstawowy  typ,  czyli  DependencyObject,  pozwala  połączyd  kontrolki  (oraz  własności)  ze 
specyficznym dla Silverlight systemem własności zależnych (temat ten rozwinięty zostanie w module 
4
 – Własności zależne i doczepiane). 

Klasa  UIElement  zawiera  podstawowe  cechy  odpowiedzialne  za  pracę  z  wyglądem  i  aranżacją 
kontrolek. Daje również podstawę do przetwarzania interakcji kontrolek z użytkownikiem. 

Ostatni  w  podstawowej  hierarchii  dziedziczenia  jest  typ  FrameworkElement.  Typ  ten  rozszerza 
możliwości  aranżacji  komponentów,  w  szczególności  dodaje  mechanizm  kontroli  zasobów  i 
przydzielania stylów, lokalizacji językowej oraz łączenia danych. Ważna własnośd eksponowana przez 
FrameworkElement to własnośd Name, dzięki niej możesz nadad kontrolce unikatową nazwę, np. 

<Button 

Name=”przycisk_start”

>START</Button> 

Nazwa ta na etapie projektowania widoczna jest z poziomu kodu C#. Można również odnaleźd żądaną 
kontrolkę dzięki metodzie FindName klasy FrameworkElement. 

Komponenty  w  Silverlight  można  aranżowad  w  dowolny  sposób,  w  edytorze  możesz  umiejscowid 
jedną  kontrolkę  na  drugiej.  Pochodne  klasy  FrameworkElement,  które  widzisz  w  przyborniku, 
podzielone  są,  przede  wszystkim,  w  zależności  od  sposobu  zarządzania  zawartością.  Zarządzanie 
zawartością  powinieneś  tutaj  rozumied  jako  sposób  dostępu  do  kontrolek  zdefiniowanych  jako 
kontrolki-dzieci.  

Z poprzedniego modułu pamiętasz poniższy szkielet wyglądu twojej aplikacji: 

<UserControl …> 

  <Grid …> 

   

<Button … /> 

   

<TextBox … /> 

  </Grid> 

</UserControl> 

Próba  zadeklarowania  więcej  niż  jednej  kontrolki-dziecka  w  komponencie  UserControl, 
powodowała informację o już istniejącej zawartości. Komponent Grid natomiast nie posiada takich 
ograniczeo, może posiadad tyle dzieci ile potrzebujesz. Zachowanie to wynika ze struktury Silverlight. 
Przyjrzyjmy się podstawowym typom dziedziczącym z FrameworkElement: 

background image

FrameworkElement

Control

Shape

Panel

UserControl

ContentControl

Grid

StackPanel

Button

 

Rys. 2 Typy rozszerzone 

Typ Shape: 

 

Typ bazowy służący do rysowania kształtów (jak prostokąty, linie, elipsy), 

 

Nie może zawierad żadnych kontrolek-dzieci. 

Typ Control: 

 

Typ bazowy kontrolek które mogą posiadad co najwyżej jedno dziecko, 

 

Dziecko nie zawsze jest publicznie dostępne. 

Typ Panel: 

 

Podstawowy typ służący zarządzaniu kolekcjami dzieci, 

 

Typy pochodne odpowiedzialne są za aranżację wyglądu kontrolek-dzieci. 

Dzieci komponentów to dowolne elementy typu UIElement.  
W  przypadku  klasy  Panel  dostęp  do  kolekcji  dzieci  uzyskujemy  przez  własnośd  Children  typu 
UIElementCollection.  
Poniższy kod w XAML: 

  <Grid …> 

   

<Button … /> 

   

<TextBox … /> 

  </Grid> 

Równoważnie zapisad można w postaci: 

Grid gr = new Grid(); 

Button bt = new Button(); 

TextBox tb = new TextBox(); 

gr.Children.Add(bt); 

gr.Children.Add(tb); 

Operacje dodawania do kolekcji są prawidłowe, gdyż zarówno klasa Button jak i TextBox pośrednio 
dziedziczą z klasy UIElement. 
Typy  UserControl  i  ContentControl  z  punktu  widzenia  projektowego  odróżnia  zakres 
widoczności  własności  Content,  która  przechowuje  kontrolkę-dziecko.  Własnośd  ta  w  klasie 
UserControl  jest  własnością  chronioną,  natomiast  jest  to  własnośd  publiczna  w  klasie 
ContentControl.  Oznacza  to,  że  użytkownik  komponentu  który  projektujesz  nie  będzie  miał 

background image

możliwości  zmiany  jego  zawartości  gdyż  jest  to  pochodna  typu  UserControl  (jest  to  cecha 
pożądana,  gdyż  logika  działania  twojego  komponentu  może  wymagad  danych  zależnych  od 
użytkownika). Z drugiej strony nie ma najmniejszego problemu, aby na powierzchni przycisku (klasa 
Button) umieścid obrazek oraz napis (a nawet inne przyciski), np.: 

<Button Height="72" Width="234" > 

  <StackPanel Name="sp_1" Orientation="Horizontal"> 

   

<Button Width="100" Height="30" Content="Przycisk"> </Button> 

   

<StackPanel Name="sp_2" > 

   

 

<Image Source="silver.png" Width="30" Height="30"></Image> 

   

 

<TextBlock Width="100">podpis</TextBlock> 

   

</StackPanel> 

  </StackPanel> 

</Button> 

Własności Content przycisku przypisywany jest obiekt typu StackPanel nazwany sp_1, przypisanie 
to  jest  oczywiście  prawidłowe  gdyż  StackPanel  dziedziczy  z  UIElement.  Panel  o  nazwie  sp_1 
posiada  dwójkę  dzieci  -  przycisk  oraz  kolejny  StackPanel  nazwany  sp_2.  Panel  o  nazwie  sp_2 
posiada dwójkę dzieci tj.: kontrolkę Image oraz TextBlock. 
 

Zarządzanie rozmieszczeniem 
Twoja aplikacja Silverlight tuż po uruchomieniu, a jeszcze przed narysowaniem interfejsu musi ustalid 
layout  (wygląd,  rozmieszczenie  komponentów),  można  zapytad  dlaczego?  Spędzamy  przecież  czas 
projektując  layout  i  nie  powinien  się  on  zmienid.  Tak  jest  w  istocie,  pamiętad  jednak  musisz,  że 
aplikacja Silverlight może dowiedzied się jaki posiada rozmiar w przeglądarce dopiero po inicjalizacji. 
Oznacza to, że wiedząc ile miejsca przydzielono mu na stronie system zarządzania wyglądem winien 
uzgodnid położenia wszystkich komponentów w zależności od zadanego rozmiaru. 
Ten sposób postępowania dotyczy również składowych naszego interfejsu użytkownika, mianowicie: 
każdy rodzic powinien uzgodnid ze swoimi dziedmi ich wymiary i położenie. Są one pośrednio zależne 
od miejsca przeznaczonego dla rodzica. 
Proces rozmieszczania dzieli się na dwie fazy: 

 

Odpytywanie kontrolek-dzieci o rozmiar (faza Measure), 

 

Ustalanie pozycji kontrolek-dzieci (faza Arrange).  

Pierwsza  faza,  to  faza  w  której  rodzic  odpytuje  kontrolki-dzieci  o  rozmiar  którego  wymagają  do 
odrysowania.  Odpytywane  są  wszystkie  dzieci  z  wyjątkiem  tych  które  posiadają  własnośd 
Visibility ustawioną na false. Oczywiście gdy kontrolka-dziecko jest np. pochodną klasy Panel 
przegląda swoje dzieci w ten sam sposób oraz uwzględnia sposób ich rozmieszczenia. Na podstawie 
tak uzyskanych informacji  żąda pewnego obszaru do wyświetlenia swojej zawartości. 

Rodzic

Dziecko

szerokość

w

ys

ok

ć

szerokość

w

ys

ok

ć

 

Rys. 3 Zapotrzebowanie kontrolki na obszar 

background image

Faza druga to faza która na podstawie zebranych informacji przydziela się miejsce oraz kontrolkom. 
Co się stanie gdy kontrolka-rodzic będzie miała zbyt mało miejsca na wyświetlenie swojego dziecka?  

Rodzic

Dziecko

szerokość

w

ys

o

ko

ść

szerokość

w

ys

o

ko

ść

 

Rys. 4 Przycinanie 

W takim przypadku dziecko zostaje obcięte (ang. Clipping) do powierzchni rodzica. 
Klasa  UIElement  która  implementuje  podstawy  mechanizmu  rozmieszczania  komponentów 
posługuje się wyłącznie strukturą typu Size do opisu żądanego miejsca (własnośd  DesiredSize), 
nie posiada jednak żadnych własności które mogłyby mied wpływ na tę wielkośd. Jest tak dlatego, że 
oczekiwany rozmiar może zostad określony dla kontrolek które przechowują tekst albo obrazek. 
Bezpośredni potomek klasy UIElement czyli klasa FrameworkElement wprowadza własności które 
pozwalają ustalid zarówno wysokośd jak i szerokośd kontrolki, są to własności  Width oraz Height. 
Dodatkowe  własności  ActualWidth  oraz  ActualHeight  zawierają  informacje  o  rzeczywistych 
rozmiarach  kontrolki  po  jej  odrysowaniu  (mogą  byd  różne  niż  Width  i  Height  jeśli  np.:  rodzic 
kontrolki zdecyduje o jej rozciągnięciu). 
Własnośd  Margin  pochodząca  z  klasy  FrameworkElement    jest  przykładem  własności  która  ma 
wpływ  na  wielkośd  komponentu  na  ekranie. W  najprostszym  scenariuszu  użycia  margines  pozwala 
zdefiniowad odstępy pomiędzy kontrolkami.  

Rodzic

Dziecko 1

Dziecko 2

Góra (top)

Dół (bottom)

Lewy

(left)

Prawy

(right)

 

Rys. 5 Marginesy 

Zależnie od tego, czy zawartośd kontrolki posiada wymuszony rozmiar (przez zawartośd, albo jawne 
zadanie szerokości i wysokości) marginesy mogą wymusid zmniejszenie kontrolki albo powiększenie 
obszaru potrzebnego do odrysowania. Możesz to zaobserwowad, pisząc: 

<StackPanel Height="64" Width="220"> 

  <Button Content="Przycisk A"/> 

  <Button 

Margin="10 10 10 10"

 Content="Przycisk B"/> 

</StackPanel> 

 

Rys. 6 Rezultat 

Ponieważ wyświetlany na powierzchni przycisku tekst (niejawnie stworzony obiekt typu TextBlock) 
posiada  niewielką  długośd,  drugi  przycisk  zaakceptował  mniejszą  szerokośd.  Natomiast  wysokośd 
potrzebnego do wyświetlenia obszaru została powiększona o odpowiednio margines górny i dolny. 
Gdy rodzic kontrolki posiada więcej miejsca aniżeli potrzebują jego kontrolki dzieci musi zdecydowad 
gdzie umiejscowid kontrolki na swojej powierzchni. Decyzja podejmowana jest na podstawie wartości 

background image

VerticalAlignment 

oraz 

HorizontalAlignment 

(eksponowanych 

przez 

klasę 

FrameworkElement). 
Wartości dopuszczalne dla VerticalAlignment to: 

 

Top – do górnej krawędzi, 

 

Center – środek kontrolki w środku ojca, 

 

Bottom – do dolnej krawędzi, 

 

Stretch  –  rozciągnięcie  tzn.:  górna  krawędź  wyrównana  do  górnej  krawędzi  ojca,  dolna 
wyrównana do dolnej krawędzi. 

Domyślną wartością VerticalAlinement jest Stretch. 
Wartości dopuszczalne dla HorizontalAlignment to: 

 

Left – do lewej krawędzi, 

 

Center – środek kontrolki w środku ojca, 

 

Right – do dolnej krawędzi, 

 

Stretch  –  rozciągnięcie  tzn.:  lewa  krawędź  wyrównana  do  lewej  krawędzi  ojca,  prawa 
wyrównana do prawej krawędzi ojca. 

Domyślną wartością HorizontalAlignment jest Stretch. 
 
Omówione wcześniej własności uważane są za mniej lub bardziej standardowe, a ich odpowiedniki 
znasz  zapewne  z  innych  bibliotek  komponentów  wizualnych  (jak  chodby  Windows  Form).  Dalej 
zajmiemy  się  unikatowymi  własnościami  Silverlight,  który  czynią  mechanizm  rozmieszczania 
kontrolek jeszcze bardziej elastycznym. 
W klasie UIElement zdefiniowano dwie arcyciekawe własności, mianowicie: 

 

RenderTransform – zawiera układ współrzędnych kontrolki (dwuwymiarowy), 

 

Projection – zawiera układ współrzędnych kontrolki (trójwymiarowy). 

Obie własności przechowują układ współrzędnych w postaci macierzy o strukturze 

Gdzie  t  –  jest  wektorem  wierszowym  przesunięcia  środka  układu  współrzędnych,  0  jest  wektorem 
kolumnowym  złożonym  z  samych  zer,  natomiast 

  jest  podmacierzą  kwadratową  która 

przechowuje  współrzędne  osi  układu  współrzędnych  w  postaci  wierszowej.  Innymi  słowy  jest  to 
afiniczny układ współrzędnych. Taka postad układu współrzędnych pozwala w szczególności oddzielid 
przesuwanie  kontrolki  od  obracania.  Mianowicie  jeśli  współrzędne  wierzchołka  w  kontrolki 
przechowujemy w wektorze 

, wtedy przekształcenie  

 

możemy przeczytad jako przedstawienie położenia w układzie współrzędnych R przesuniętym o t

Własność RenderTransform 

Macierz RenderTransform jest formatu 3x3. Macierz ta w swojej naturalnej postaci dostępna jest 
przez  własności  klasy  Matrix,  mianowicie:  M11,  M12,  M21,  M22,  OffsetX,  OffsetY.  Ich 
znaczenie jest następujące: 

Oczywiście elementów które mają ustalone wartości typ Matrix nie eksponuje (ostatnia kolumna). 
Przykładowe przypisanie 

background image

<StackPanel Background="Beige" Height="64" 
Width="220"> 

 <Button  Content="Przycisk" > 

  <Button.RenderTransform> 

   <MatrixTransform   > 

     <MatrixTransform.Matrix > 

       <Matrix M11="0" M12="1" M21="1" M22="0"                   
   

 

OffsetX="100" OffsetY="-75"/> 

     </MatrixTransform.Matrix> 

   </MatrixTransform> 

  </Button.RenderTransform> 

 </Button> 

</StackPanel> 

 

Rys. 7 Rezultat 

Pokazuje,  że  gdy  RenderTransform  wyprowadza  kontrolkę-dziecko  poza  obszar  widoczności 
rodzica, Silverlight nie będzie dziecka obcinał do powierzchni rodzica. Dodatkowo zauważmy, że 
przycisk nie posiada ustalonej szerokości. Została ona zao ustalona w procesie Arrange jako równa 
szerokości rodzica (zostanie to wyjaśnione  dalej), dopiero wtedy kontrolka podlega przekształceniu 
układu współrzędnych.  
Domyślną wartością RenderTransform jest macierz identycznościowa, tj. macierz postaci.: 

Definiuje  ona  prostokątny  układ  współrzędnych  którego  środek  znajduje  się  w  lewym  górnym 
narożniku kontrolki, mianowicie: 

X

Y

 

Rys. 8 Domyślny układ współrzędnych 

W przeciwieostwie do układów współrzędnych które znasz, w Silverlight oś Y ma zwrot w dół. 
Manipulacja  współczynnikami  macierzy  jest  uciążliwa  dlatego  wprowadzono  zbiór  klas  które 
ułatwiają zmianę układu współrzędnych w standardowych scenariuszach użycia, Są to mianowicie: 

 

Przesunięcie – TranslateTransform, 

 

Obrót – RotateTransform, 

 

Skalowanie – ScaleTransform, 

 

Gięcie – SkewTransform, 

 

Typowe złożenie – CompositeTransform. 

W tabeli zebrano przykłady dla przycisku, 

Nazwa 

Przykład 

Rezultat 

TranslateTransform 

<Button.RenderTransform> 

<

TranslateTransform X ="30" Y ="-10"

 /> 

</Button.RenderTransform> 

 

background image

RotateTransform 

<Button.RenderTransform> 

<

RotateTransform Angle="45"

 /> 

</Button.RenderTransform> 

 

ScaleTransform 

<Button.RenderTransform> 

<

ScaleTransform 

 

ScaleX="0.5" ScaleY="4.5"

/> 

</Button.RenderTransform> 

 

SkewTransform 

<Button.RenderTransform> 

<

SkewTransform  

 

AngleX="30" AngleY="30"

 /> 

</Button.RenderTransform> 

 

Dla  wszystkich  tych  przekształceo  poza  przesunięciem  zdefiniowane  są  własności  CenterX  oraz 
CenterY,  odpowiadają  one  za  położenie  punktu  względem  którego  przekształcenie  jest 
wykonywane.  
Każda z uproszczonych form definiowania układu współrzędnych, pozwala inicjalizowad macierz. Jeśli 
te cztery przekształcenia nie wystarczą do realizacji zadania, możemy je składad. Służy do tego klasa 
TransformGroup. Zawiera ona kolekcję składowych przekształceo (własnośd  Children), w XAML 
inicjalizacja może wyglądad tak: 

<Button.RenderTransform> 

  <TransformGroup > 

   

<RotateTransform Angle="45" /> 

   

<TranslateTransform X="50" /> 

  </TransformGroup> 

</Button.RenderTransform> 

Składanie  przekształceo  to  nic  innego  jak  iloczyn  odpowiednich  macierzy.  Ponieważ  iloczyn  nie 
zawsze  jest  przemienny  to  składając  przekształcenia  musimy  zachowad  szczególną  uwagę.  Dwa 
złożenia (różniące się tylko kolejnością mogą wywoład drastycznie różne efekty). 

<Button.RenderTransform> 

 <TransformGroup > 

  

<RotateTransform Angle="45" /> 

  

<TranslateTransform X="50" /> 

 </TransformGroup> 

</Button.RenderTransform> 

 

background image

<Button.RenderTransform> 

 <TransformGroup > 

  

<TranslateTransform X="50" /> 

  

<RotateTransform Angle="45" /> 

 </TransformGroup> 

</Button.RenderTransform> 

 

 
Efekt  ten  łatwo  wytłumaczyd  posługując  się  zapisem  macierzowym.  Oznaczmy  przez  R  macierz 
obrotu, a przez T macierz przesunięcia, wektor x oznaczał będzie wektor położenia.  
Pierwsza wersja równoważna jest wykonaniu mnożenia postaci xRT, to znaczy najpierw x wyrażony 
jest w obróconym układzie a następnie środek układu ulega przesunięciu. 
Druga wersja przekształcenia równoważna jest mnożeniu xTR, czyli punkt x najpierw wyrażony jest w 
układzie przesuniętym i tak uzyskane współrzędne zostają wyrażone w układzie obróconym. 
Wspomniane wcześniej  przekształcenie CompositeTransform pozwala skrócid zapis w XAML (bez 
konieczności użycia TransformGroup), typowego przypadku użycia, czyli porządku: 

1.  Skalowanie, 
2.  Gięcie 
3.  Obrót, 
4.  Przesunięcie. 

Klasa CompositeTransform zachowuje powyższą kolejnośd oraz eksponuje własności przekształceo 
składowych (skrótowy zapis skraca kod z sześciu linii do jednej), np.: 

<CompositeTransform Rotation=”45” TranslateX=”30” SkewX=”10” ScaleY=”1.5” /> 

Własnośd  RenderTransform  jest  własnością  dziedziczoną. Układy  współrzędnych  kontrolek-dzieci 
składane  są  automatycznie  z  układem  współrzędnych  rodzica.  Oznacza  to,  że  dzieci  zawsze  będą 
wyrażone w układzie współrzędnych rodzica. Pokazuje to poniższy przykład 

<StackPanel Background="Beige"  Height="80" Width="220"> 

 

<StackPanel.RenderTransform> 

   

<RotateTransform Angle="45" /> 

  </StackPanel.RenderTransform> 

  <Button  Content="Przycisk A" /> 

  <Button  Content="Przycisk B" > 

   

<Button.RenderTransform> 

   

 

<TranslateTransform X="50" /> 

   

</Button.RenderTransform> 

  </Button> 

  <Button  Content="Przycisk C" />     

</StackPanel> 

Rezultat jest już łatwy do przewidzenia, mianowcie: 

background image

 

Rys. 9 Dziedziczenie układu współrzędnych 

Własność Projection 

Format  macierzy  Projection  to  4x4.  Strukturę  tej  macierzy  ujawnia  typ  Matrix3D  obudowany 
klasą Matrix3DProjection. Struktura macierzy przechowującej przekształcenie układu jest bardzo 
podobna do struktury omawianej wcześniej macierzy 3x3, mianowicie: 

Eksponowana  jest  ostatnia  kolumna  (co  jest  ważne  z  punktu  widzenia  rzutowania),  dostępna  jest 
dodatkowa współrzędna przesunięcia (własnośd OffsetZ) oraz macierz przechowująca współrzędne 
osi układu jest formatu 3x3. 
Ponownie operowad można każdej współrzędnej tej macierzy, mianowicie: 

   <Button.Projection> 

   <Matrix3DProjection  ProjectionMatrix="2, 0, 0, 0, 

                                          0, 2, 0, 0, 

                                          0, 0, 1, 0, 

                                          10, 0, 10, 1"/> 

   </Button.Projection> 

Spowoduje  wydłużenie  wersorów  na  osi  X  i  Y  (rozciągnięcie),  dodatkowo  środek  umieszczony 
zostanie w punkcie o współrzędnych (10,0,10). 
W Silverlight 4 nie wyeksponowano interfejsu pozwalającego składad przekształcenia trójwymiarowe, 
oraz  interfejsu  pozwalającego  tworzyd  macierze  o  zadanych  własnościach  (translacja,  rotacja, 
skalowanie, macierz rzutowa itp.). Bez trudu można dokonad tego samodzielnie gdyż typ  Matrix3D 
implementuje  operator  mnożenia  macierzy.  Po  przykładowe  rozwiązania  spójrz  (Materiały 
dodatkowe….). Mimo ogromu możliwości stojących za własnymi przekształceniami 3D tematu tego 
nie rozwiniemy gdyż wykracza poza ramy niniejszych materiałów. 
Podobnie  jak  w  przypadku  RenderTransform  do  manipulacji  macierzą  Projection  stworzono 
dostarczono  prostą  klasę  PlaneProjection.  Klasa  ta  pomaga  zrealizowad  typowe  scenariusze 
użycia. 
Klasa  PlaneProjection  eksponuje  12  własności  pozwalających  kontrolowad  wynikową  macierz 
rzutową.  Niestety  nie  eksponuje  własności  odpowiedzialnej  za  kontrolę  rozwarcia  trójwymiarowej 
bryły widzenia. Własności podzielid można na cztery grupy: 

 

Kontrola obrotów – RotationX, RotationY, RotationZ, 

 

Kontrola 

środka 

obortu 

– 

CenterOfRotationX, 

CenterOfRotationY, 

CenterOfRotationZ, 

 

Kontrola  położenia  środka  układu  w  układzie  globalnym  (środek  w  centrum  ekranu)  – 
GlobalOffsetX, GlobalOffsetY, GlobalOffsetZ, 

background image

 

Kontrola  położenia  środka  układu  w  układzie  lokalnym  (środek  w  centrum  kontrolki)  – 
LocalOffsetX, LocalOffsetY, LocalOffsetZ. 

O ile pierwsze działanie dwóch pierwszych grup jest jasne, to różnica pomiędzy lokalnym a globalnym 
przesunięciem jest subtelna. Spójrzmy na następujący fragment: 

<StackPanel VerticalAlignment="Center"> 

    <Image Width="200" Source="Tulips.jpg"> 

        <Image.Projection> 

            

<PlaneProjection  

   

 

 

RotationY="60"/> 

        </Image.Projection> 

    </Image> 

    <Image Width="200" Source="Penguins.jpg"> 

        <Image.Projection> 

            <PlaneProjection  

   

 

 

RotationY="60"/> 

        </Image.Projection> 

    </Image>         

</StackPanel> 

 

 

Łatwo  zauważyd,  że  oba  obrazy  rozmieszczone  przez  StackPanel  jeden  pod  drugim  posiadają 
wspólną  oś  obrotu.  Przypisując  jednakową  wartośd  przesunięcia  odpowiednio  lokalnemu  i 
globalnemu przesunięciu X, czyli: 

<StackPanel VerticalAlignment="Center"> 

    <Image Width="200" Source="Tulips.jpg"> 

        <Image.Projection> 

            

<PlaneProjection 

   

 

 

LocalOffsetX="100" 

   

 

 

RotationY="60"/> 

        </Image.Projection> 

    </Image> 

    <Image Width="200" Source="Penguins.jpg"> 

        <Image.Projection> 

            <PlaneProjection 

   

 

 

GlobalOffsetX="100" 

   

 

 

RotationY="60"/> 

        </Image.Projection> 

    </Image>         

</StackPanel> 

 

 

background image

Rezultat  winien  byd  przejrzysty.  Przesunięcie  lokalne  stosowane  jest  przed  wykonaniem  obrotu 
(pozwala  umiejscowid  środek  układu  współrzędnych  niekoniecznie  w  centrum  kontrolki),  natomiast 
przesunięcie globalne po wykonaniu obrotu (pozwala umiejscowid obiekt w przestrzeni).  

Interakcja z użytkownikiem 
System  służący  budowie  interfejsów  użytkownika  byłby  niepełny  gdyby  nie  pozwalał  na  interakcję 
użytkownika  ze  zbiorem  komponentów.  Jak  zostało  wspomniane  na  początku  rozdziału  podstawy 
tego interfejsu wprowadza klasa UIElement. Interakcja kodu Silverlight z użytkownikiem odbywa się 
przy  pomocy  systemu  zdarzeo.  Zdarzenie  to  z  jednej  strony  specjalny  typ  delegacji  (patrz  słowo 
kluczowe  
event  języka  C#)  a  z  drugiej  to  proces  wykonywania  kodu  w  pewnych  szczególnych 
sytuacjach  (np.  użytkownik  wcisnął  lewy  przycisk  myszy  gdy  kursor  znajdował  się  na  powierzchnią 
kontrolki).  
 
Delegowanie kodu wykonywanego przy wyzwoleniu zdarzenia można dokonad na co najmniej dwa 
sposoby. Pierwszy to zwyczajowe delegowanie z poziomu języka C#  (z użyciem operatora +=), np. 

 Button1.MouseEnter += Obsluga_zdarzenia; 

Delegację możemy oczywiście usunąd z uzyciem operatora -=, mianowicie: 

 Button1.MouseEnter -= Obsluga_zdarzenia; 

Drugim  sposobem  jest  przypisanie  nazwy  metody  odpowiedniej  własności  kontrolki  w  sposób 
atrybutowy, np.: 

 <Button MouseEnter=”Obsluga_zdarzenia” … /> 

Visual  Studio  wspiera  ten  typ  przypisania  obsługi  pozwalając  automatycznie  utworzyd  namiastkę 
kodu metody obsługi zdarzenia (zapewnia to niezbędną zgodnośd typów). 
 
Oddelegowany  kod, wykonany  będzie  w  chwili gdy  zaistnieje  pewna  szczególna  sytuacja  (związana 
np. ze związkiem kursora myszy oraz komponentu, albo naciśnięciem przycisku klawiatury). Proces 
ten  nazywa  się  wyzwoleniem  zdarzenia.  Jeśli  o  wyzwoleniu  zdarzenia  poinformowane  zostaną 
również  inne  kontrolki  (np.  bezpośredni  rodzic  kontrolki)  mówimy  o  rozgłaszaniu  zdarzenia  (ang. 
Event Routing). Rozgłaszanie zdarzeo jest bardzo ważną cechą Silverlight dokładne omówienie tego 
zagadnienia  pozostawiamy  jednak  na  później.  Na  potrzeby  tego  rozdziału  możesz  przyjąd,  że 
zdarzenie wyzwalane jest na kontrolce której dotyczy. 
Zdarzenia  eksponowane  przez  UIElement,  dotyczące  interakcji  kursora  myszy  oraz  kontrolek 
zebrano w poniższej tabeli. 
MouseEnter 

wyzwalane  gdy  kursor  myszy  znalazł  się  nad  powierzchnią  kontrolki 
(wcześniej był poza) 

MouseLeave 

Wyzwalane  gdy  kursor  myszy  opuścił  powierzchnie  kontrolki 
(wcześniej znajdował się nad powierzchnią) 

MouseLeftButtonDown 

Wyzwalane gdy wciśnięto lewy przycisk myszy oraz kursor znajduje się 
nad powierzchnią kontrolki 

MouseLeftButtonUp 

Wyzwalane  gdy  puszczono  lewy  przycisk  myszy  nad  powierzchnią 
kontrolki 

MouseMove 

Wyzwalane gdy kursor porusza się nad powierzchnią kontrolki 

MouseRightButtonUp 

Wyzwalane gdy puszczono prawy przycisk myszy oraz kursor znajduje 
się nad powierzchnią kontrolki 

MouseRightButtonDown  Wyzwalane  gdy  wciśnięto  prawy  przycisk  myszy  oraz  kursor  znajduje 

się nad powierzchnią kontrolki 

background image

MouseWheel 

Wyzwalane gdy zmieniła się pozycja rolki myszy oraz kursor znajduje 
się nad powierzchnią kontrolki 

Istnieje  jeszcze  jedno  specjalne  zdarzenie  związane  z  kursorem  myszy,  mianowicie 
LostMouseCapture.  Zdarzenie  to  związane  jest  z  procesem  przechwytywania  kursora  myszy
Proces  ten  przydatny  jest  w  zadaniach  związanych  z  manipulacją  w  czasie  rzeczywistym  pozycjami 
kontrolek,  zauważ  mianowicie  gdy  łapiesz  za  boczny  pasek  do  przesuwania  dokumentów  w 
przeglądarce  i  przesuwasz  kursor  myszy  zmiana  położenia  kursora  raportowana  jest  do  kontrolki 
nawet wtedy, gdy kursor znajduje się poza jej powierzchnią. Gdy zwalniasz przycisk sytuacja wraca do 
normalności. 
Aby nad dowolną kontrolką przechwycid kursor myszy powinieneś wykonad metodę CaptureMouse. 
Metoda ta zwraca wartośd logiczną informując czy udało się przechwycid kursor myszy. Proces ten 
może się nie powieśd gdy np.: nie jest wciśnięty lewy przycisk myszy lub inna kontrolka przechwyciła 
kursor  myszy  i  jeszcze  nie  zwolniła.  Od  momentu  przechwycenia  kursora  zachowanie  myszy 
raportowane jest kontrolce nawet gdy kursor nie znajduje się nad jej powierzchnią. 
Aby zwolnid przechwytywanie kursora należy wywoład metodę ReleaseCaptureMouse. Zdarzenie 
LostMouseCapture  wywoływane  jest  właśnie  w  odpowiedzi  na  utratę  przechwyconego  kursora 
myszy. 
Kursor myszy nie jest jedynym elementem mogącym wywoływad reakcje kontrolek Silverlight. Innym 
interfejsem jest użycie klawiatury. Pomiędzy kontrolkami możemy nawigowad z użyciem klawiatury 
(np. przycisk TAB lub kursor numeryczny). W jednej chwili może byd aktywna tylko jedna kontrolką 
mówimy wtedy że posiada fokus.  
W tabeli zebrano zdarzenia związane z tym procesem. 
GotFocus 

Wyzwalane gdy fokus został ustawiony na kontrolkę 

LostFocus 

Wyzwalane gdy kontrolka utraciła fokus 

Gdy kontrolka posiada fokus oraz wciśnięto przycisk klawiatury inny niż specjalny (inny niż służący do 
zmiany elementu posiadającego fokus) wyzwalane są następujące zdarzenia: 
KeyDown 

Wyzwalane gdy naciśnięto przycisk 

KeyUp 

Wyzwalane gdy zwolniono przycisk 

 
Klasa FrameworkElement wprowadza dwa dodatkowe typy zdarzeo: 
Loaded 

Wyzwalane  gdy  zainicjalizowana    kontrolka  została  dołączona  do 
drzewa obiektów twojej aplikacji 

Unloaded 

Wyzwalane  gdy  kontrolka  została  wycięta  z  drzewa  komponentów 
wizualnych 

Zdarzenia te są niezmiernie ważne dla zapewnienia spójności działania aplikacji. Silverlight tworząc 
kontrolki  dołącza  je  do  wspólnego  drzewa  komponentów.  W  tej  strukturze  rodzic  jest  elementem 
nadrzędnym swoich dzieci. Silverlight rozpoczyna tworzenie obiektów na podstawie np. kodu XAML 
od  elementów  które  nie  posiadają  dzieci  (nazywanych  liśdmi).  Jedną  z  ważnych  przesłanek  do 
wyzwolenia  zdarzenia  Loaded  jest  to,  że  wszystkie  dzieci  kontrolki  zostały  poprawnie  utworzone 
(każda  w  szczególności  wyzwoliła  zdarzenie  Loaded).  Wtedy  kontrolka-rodzic  może  wyzwolid 
zdarzenie  Loaded,  informujące  o  tym,  iż  została  poprawnie  utworzona.  Temat  ten  rozwiniemy  w 
kolejnych modułach. 
Gdy  kontrolka  usunięta  zostaje  z  drzewa  wyświetlania  wyzwalane  jest  zdarzenie  Unloaded, 
następnie  każda  jej  kontrolka-dziecko  wyzwala  zdarzenie  Unloaded  itd.  Standardowy  scenariusz 
użycia  tego  zdarzenia  jest  następujący:  jeśli  kontrolka  korzystała  z  danych  pochodzących  od 
zewnętrznej  usługi  (serwisu),  delegowanie  kodu  w  odpowiedzi  na  zajście  zdarzenia  Unloaded 
zdarzenia pozwala na zamknięcie połączenia z tym serwisem. 
Pozostałe zdarzenia standardowe omówione zostaną w następnych modułach. 

background image

Funkcjonalna lista komponentów 
Poznałeś już podstawowe możliwości kontrolek. Wraz z Silverlight 4 dostajesz do dyspozycji ponad 60 
różnych  komponentów.  Wszystkie  SA  nie  tylko  gotowe  do  użycia  ale  można  dostosowad  je  do 
własnych  potrzeb.  Na  bazie  tych  kontrolek  tworzyd  można  nowe  bardziej  skomplikowane  bądź 
realizujące specyficzne cele. 

Poniżej zestawiono wybrane grupy komponentów realizujące typowe scenariusze użycia. 

Przyciski: 

Nazwa 

Opis 

Button 

Definiuje wyzwala zdarzenie Click 

RepeatButton 

Specjalny  przycisk  który  powtarza  wyzwolenie  zdarzenia  Click  w 
krótkich odstępach czasu 

HyperlinkButton 

Specjalny przycisk umożliwiający nawigację do zadane strony 

 

Komponenty grupujące: 

Nazwa 

Opis 

Canvas 

Umożliwia jawne pozycjonowanie kontrolek-dzieci 

StackPanel 

Pozwala układad kontrolki zależnie od orientacji (pozioma – pionowa) 

Grid 

Pozwala komponowad kontrolki w regularnej postaci (wiersze – kolumny) 

 

Komponenty dekorujące: 

Nazwa 

Opis 

Border 

Pozwala zdefiniowad obramowanie kontrolki-dziecka 

Viewbox 

Wymusza wypełnienie (byd może z rozciągnięciem) swojej zawartości 

 

Komponenty nawigacyjne: 

Nazwa 

Opis 

Page 

Kontrolka  pomocnicza  pozwalająca  obudowad  zawartośd  do  postaci 
możliwej w nawigacji przez kontrolkę Frame 

Frame 

Pozwala  nawigowac  między  swoimi  stronami  (kontrolki  Page)  przy 
pomocy metod GoBack oraz GoForward 

 

Wyświetlanie grafiki: 

Nazwa 

Opis 

Image 

Dzięki własności Source może wyświetlac obrazy 

MultiScaleImage 

Wprowadza możliwośc oglądania obrazu z różną skalą szczegółowości 

MediaElement 

Pozwala odtwarzad dźwięki oraz obraz wideo 

 

background image

Komponenty informacyjne 

Nazwa 

Opis 

TextBlock 

Pozwala wyświetlad porcje tekstu 

ProgressBar 

Pozwala raportowad postęp przy pomocy paska 

RichTextBox 

Pozwala wyświetlad tekst formatowany 

Label 

Pozwala wyświetlad objaśnienia 

 

Tekstowe komponenty wejściowe: 

Nazwa 

Opis 

TextBox 

Pozwala wyświetlad i edytowad proste fragmenty tekstu 

PassswordBox 

Pozwala wprowadzad i ukrywad ważne informacje tekstowe 

RichTextBox 

Pozwala wyświetlad i edytowad złożone fragmenty tekstu 

WebBrowser 

Pozwala wyświetlad i nawigowad w dokumentach HTML 

 

Powyższa  lista  nie  wyczerpuje  wszystkich  możliwości,  a  cechy  poszczególnych  komponentów 
wyjaśnimy w kolejnych rozdziałach. 

 

 

 
 

background image

 

Przykładowe rozwiązanie 

Projektując  narzędzie  do  projektowania  książek  zawierających  zdjęcia  z  wakacji  chcemy  dad 
użytkownikom  prosty  przejrzysty  interfejs  pozwalający  na  manipulacje  elementami  strony  foto 
książki. Nasze zadanie to szybko zbudowad prototyp takiego interfejsu użytkownika.  

Prototypowy interfejs do aranżacji zdjęd na stronie powinien: 

 

Pozwalad  przesuwad  zdjęcie  (zarówno  przy  pomocy  kursora  myszy,  jak  i  przy  pomocy 
wyspecjalizowanych przycisków), 

 

Pozwalad na obrót i skalowanie zdjęcia względem środka elementu (przy pomocy przycisków). 

Załóżmy, że elementy pozycje  elementów strony fotoksiążki (obrazy, teksty), będziemy kontrolowali 
przy pomocy przekształceo:  skalowaniaobrotu oraz  przesunięcia (w tej kolejności). Przekształcenia 
te będą złożone razem przy pomocy TransformGroup. Finalne przekształcenie przypisane będzie do 
własności RenderTransform. 

Ponieważ elementy strony fotoksiążki będą tworzone dynamicznie złą praktyką byłoby nadawanie im 
unikatowych  nazw.  Lepszym  pomysłem  jest  stworzenie  specjalnej  klasy  która  związana  będzie  z 
operacjami  na  elemencie  strony.  Nazwijmy  tę  klasę  Kontroler.  W  swej  najprostszej  postaci  jej 
szkielet wyglądad może następująco: 

public class Kontroler 

  private static RotateTransform rotacja; 

  private static TranslateTransform translacja; 

  private static ScaleTransform skala; 

  private static FrameworkElement lastselected = null; 

  … 

Potrzebne  przekształcenia  będziemy  przechowywali  w  odpowiednich  zmiennych  statycznych. 
Dodatkowo przechowywany będzie ostatnio wybrany element.  

Pierwsza  ważna  metoda  pomocnicza  klasy  Kontroler  powinna  pozwolid  dołączyd  odpowiednie 
przekształcenia do dowolnego FrameworkElement. Ma ona postad: 

private void Dolacz_Uklad(FrameworkElement elem) 

  TransformGroup tg = new TransformGroup(); 

  ScaleTransform sc = new ScaleTransform(); 

  … 

  tg.Children.Add(sc); 

  RotateTransform rt = new RotateTransform(); 

  … 

  tg.Children.Add(rt); 

  TranslateTransform tt = new TranslateTransform(); 

  … 

  tg.Children.Add(tt); 

background image

  elem.RenderTransform = tg; 

Przekształcenia  tworzone  są  w  odpowiedniej  kolejności.  Ich  własności można  ustalid  np.  w  sposób 
losowy. 

Załóżmy, że element po wybraniu będzie posiadał przezroczystośd (własnośd Opacity) ustawioną na 
75%.  Wbrew  pozorom  najpierw  winniśmy  zaimplementowad  operacje  odpowiedzialne  za 
odznaczenie elementu mianowicie: 

public static void CleanTransforms() 

  if (lastselected != null) 

  { 

   

lastselected.Opacity = 1.0; 

   

lastselected = null; 

  } 

  rotacja = null; 

  translacja = null; 

  skala = null; 

Teraz możemy przystąpid do implementacji podstawowych operacji związanych z wyborem nowego 
elementu, tj.: 

 

Odznaczenie elementu zaznaczonego, 

 

Zaznaczenie nowego elementu wraz z wydobyciem potrzebnych przekształceo. 

 Służyła będzie temu metoda RegisterElementTransforms, o szkielecie: 

public static void RegisterElementTransforms(UIElement elem) 

CleanTransforms(); 

  if (elem != null && elem.RenderTransform is TransformGroup) 

  { 

   

if (elem is FrameworkElement) 

   

   

 

lastselected = elem as FrameworkElement; 

   

 

lastselected.Opacity = 0.75; 

   

   

TransformGroup tg = elem.RenderTransform as TransformGroup; 

   

foreach (Transform tran in tg.Children) 

   

if (tran is TranslateTransform) translacja = tran as       
 

 

 

 

 

 

TranslateTransform; 

   

 

if (tran is RotateTransform) rotacja = tran as RotateTransform; 

   

 

if (tran is ScaleTransform) skala = tran as ScaleTransform; 

background image

   

  } 

Metoda  ta  najpierw  usuwa  wszystkie  przekształcenia  oraz  przywraca  przeźroczystośd  elementu 
wcześniej wybranego (o ile był wybrany).  Następnie zapisuje referencje do nowego elementu oraz 
zmienia  jego  przezroczystośd.  Zauważmy,  że  wyróżniany  obiekt  musi  posiadad  RenderTransform 
typu TransformGroup. Ostatnim krokiem jest przegląd i wydobycie potrzebnych przekształceo. 

Możemy teraz zaimplementowad metody pomocnicze odpowiedzialne za: 

 

Przesuwanie (Lewo, Prawo, Góra, Dół), np.: 

public static void PrzesunLewo() 

  if (translacja != null) 

   

 

translacja.X -= 1; 

 

Obracanie, np.: 

public static void ObrocLewo() 

  if (rotacja != null) 

   

 

rotacja.Angle -= 1; 

 

Skalowanie, np.: 

public static void Powieksz() 

  if (skala != null) 

  { 

   

skala.ScaleX += 0.01; 

   

skala.ScaleY += 0.01; 

   } 

 } 

Dodatkowo  będziemy  potrzebowali  metody  która  pomoże  zrealizowad  poruszanie  obiektu  przy 
pomocy kursora myszy, mianowicie: 

public static void AddToElementPosition(Point p) 

  if (translacja != null) 

  { 

   

translacja.X += p.X; 

   

translacja.Y += p.Y; 

  } 

background image

Skoro  potrzebne  zaplecze  jest  już  gotowe  to  możemy  przystąpid  do  budowy  części  wizualnej 
interfejsu.  

Podzielmy powierzchnię na dwie części. Cześd lewa wyświetlała będzie przyciski edycyjne (kontrola 
położenia,  obrotu  oraz  skali),  częśd  prawa  natomiast  będzie  zawierała  powierzchnię  wyświetlającą 
zdjęcia oraz napisy. Do podziału użyjemy panelu typu Grid, w XAML wyglądad może to następująco: 

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

<Grid.RowDefinitions> 

   

<RowDefinition  /> 

</Grid.RowDefinitions> 

<Grid.ColumnDefinitions> 

   

<ColumnDefinition Width="200" /> 

   

<ColumnDefinition /> 

</Grid.ColumnDefinitions> 

… 

<Grid> 

Mamy  więc  dwie  kolumny  oraz  jeden  wiersz.  Lewa  kolumna  posiada  ustaloną  szerokośd.  Przyciski 
sterujące zbierzmy w panelu typu  StackPanel (domyślnie układa on elementy pionowo). Wyboru 
położenia  kontrolki  w  panelu  typu  Grid  dokonujemy  przyklejając  do  obiektu  własności 
Grid.Column  oraz  Grid.Row.  Więcej  o  doczepianiu  własności  dowiesz  się  w  module  własności 
zależne i doczepiane. W tej chwili ważne jest to, że jeśli bezpośrednie dziecko panelu Grid posiada te 
własności to zostanie umieszczone w odpowiednim miejscu siatki, poza tym panelem własności te są 
ignorowane.  

Umiejscowiony panel z przyciskami wyglądad może tak: 

<StackPanel Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" 
HorizontalAlignment="Center" Height="Auto"> 

    <Button Margin="10 10 10 10" Click="Button_Click">Dodaj</Button> 

    <TextBlock>Pozycja:</TextBlock> 

    <StackPanel Orientation="Horizontal" > 

        <RepeatButton Width="65" Height="65" Margin="5 0 5 5"> 

   

 

 

 

 

Góra</RepeatButton> 

        <RepeatButton Width="65" Height="65" Margin="5 0 5 5”> 

   

 

 

 

 

Dół</RepeatButton> 

    </StackPanel> 

    <StackPanel Orientation="Horizontal" > 

        <RepeatButton Width="65" Height="65" Margin="5 0 5 5"> 

   

 

 

 

 

Lewo</RepeatButton> 

        <RepeatButton Width="65" Height="65" Margin="5 0 5 5"> 

   

 

 

 

 

Prawo</RepeatButton> 

    </StackPanel> 

    <TextBlock>Obrót:</TextBlock> 

    <StackPanel Orientation="Horizontal" > 

        <RepeatButton Width="65" Height="65" Margin="5 0 5 5" > 

background image

   

 

 

 

 

w Lewo</RepeatButton> 

        <RepeatButton Width="65" Height="65" Margin="5 0 5 5"> 

   

 

 

 

 

w Prawo</RepeatButton> 

    </StackPanel> 

    <TextBlock>Skala:</TextBlock> 

    <StackPanel Orientation="Horizontal" > 

        <RepeatButton Width="65" Height="65" Margin="5 0 5 5"> 

   

 

 

 

 

Powiększ</RepeatButton> 

        <RepeatButton Width="65" Height="65" Margin="5 0 5 5"> 

   

 

 

 

 

Zmniejsz</RepeatButton> 

    </StackPanel> 

</StackPanel> 

Przycisk Dodaj służył będzie to testowego rozmieszczania elementów. 

Prawa  częśd  interfejsu  to  powierzchnia  na  której  rozmieszczamy  zdjęcia,  w  tym  celu  użyjemy 
kontrolki Canvas dekorowanej czarnym obramowaniem, mianowicie: 

<Border Grid.Column="1" Grid.Row="0"  

   

Width="500" Height="500" HorizontalAlignment="Center"  

 

   

VerticalAlignment="Center"  

   

BorderThickness="2" BorderBrush="Black"> 

    <Canvas Background="Gray" Name="kartka" /> 

</Border> 

Powinniśmy uzyskad rezultat zbliżony do następującego: 

 

Rys. 10 Finalny layout 

Byd  może  wygląd  nie  należy  do  najpiękniejszych,  niemniej  grafik  z  użyciem  Microsoft  Expression 
Blend może łatwo uczynid go wspaniałym. Nasze zadanie to nadad interfejsowi pełną funkcjonalnośd. 

W odpowiedzi na naciśnięcie przycisku Dodaj testowo stwórzmy obrazek albo linię tekstu (losowo). 
Powinniśmy obsłużyd zdarzenie Click, na przykład w pokazany poniżej sposób: 

private void Button_Click(object sender, RoutedEventArgs e) 

FrameworkElement elem = null; 

if (rd.Next() % 2 == 0) 

background image

   

Image img = new Image(); 

   

… 

   

elem = img; 

else 

   

TextBlock tb = new TextBlock(); 

   

… 

   

elem = tb; 

Kontroler.Dolacz_Uklad(elem); 

elem.MouseLeftButtonDown += Wybrano_Obrazek; 

elem.MouseMove += RuchMyszy; 

elem.MouseLeftButtonUp += KoniecEdycji; 

kartka.Children.Add(elem); 

Po  stworzeniu  obiektu  dobudowujemy  ustalamy  mu  żądany  zestaw  przekształceo  przygotowaną 
wcześniej  metodą  Dolacz_Uklad  klasy  Kontroler.  Kolejny  krok  to  dodanie  obsługi  zdarzeo 
związanych  z  kursorem  myszy.  Tak  udekorowany  obiekt  dodajemy  do  panelu  kontrolek  o  nazwie 
kartka.  Obsługa  zdarzeo  wciśnięto  lewy  przycisk,  poruszono  myszą  oraz  zwolniono  lewy  przycisk 
myszy, winna zapewnid ruch obiektu oraz rejestrację jego przekształceo. 

Metoda Wybrano_Obrazek może mied postad: 

private Point pozycja; 

private bool isMouseDown = false; 

private void Wybrano_Obrazek(object sender, MouseButtonEventArgs e) 

    Kontroler.RegisterElementTransforms(e.OriginalSource as UIElement); 

    pozycja = e.GetPosition(kartka); 

    isMouseDown = true; 

Oczywiście  w  pierwszej  kolejności  wydobywamy  z  elementu  wszystkie  potrzebne  przekształcenia 
(dzięki  wcześniej  przygotowanej  metodzie  RegisterElementTransforms)  następnie  zapamiętujemy 
pozycję  ekranową  wciśnięcia  przycisku  myszy  oraz  notujemy  fakt  wciśnięcia  przycisku.  Względem 
zapamiętanej pozycji kursora będziemy liczyli przesunięcia obiektu. 

Po zwolnieniu przycisku nie czyścimy układu współrzędnych (nie wywołujemy CleanTransforms) gdyż 
byd  może  użytkownik  wykona  jakieś  operacje  z  użyciem  panelu  bocznego  przycisków.  Kod metody 
KoniecEdycji może byd zatem bardzo prosty: 

private void KoniecEdycji(object sender, MouseButtonEventArgs e) 

    isMouseDown = false; 

background image

 Odpowiedniej uwagi wymaga natomiast metoda RuchMyszy: 

private void RuchMyszy(object sender, MouseEventArgs e) 

    if (isMouseDown) 

    { 

        Point mousePos = e.GetPosition(kartka); 

        Point delta = new Point(mousePos.X - pozycja.X,   

   

 

 

 

     mousePos.Y - pozycja.Y); 

        Kontroler.AddToElementPosition(delta); 

        pozycja = mousePos; 

    } 

Ponieważ metoda zdarzenie MouseMove będzie wyzwolone nad elementem nawet gdy nie wciśnięto 
nad  nim  przycisku  myszy,  winniśmy  sprawdzid  czy  takowy  przycisk  został  wciśnięty.  Jeśli  tak, 
znajdujemy przesunięcie względem ostatnio zapamiętanej pozycji kursora. Nakazujemy przesunięcie 
wybranego w chwili wciśnięcia lewego przycisku myszy elementu. Ostatni krok to jako zapamiętanie 
bieżącej pozycji kursora myszy.  

Metody  Wybrano_Obrazek,  RuchMyszy  oraz  KoniecEdycji  przypisz  dodatkowo  do  obiektu  kartka
Pozwoli to między innymi odznaczyd element (gdy wciśnięto przycisk na kartce ale nie na obrazku). 
Zauważ, że niezależnie nad jakim elementem zdarzenie zostanie obsłużone, to zmiana pozycji będzie 
dotyczyła elementu wybranego w chwili wciśnięcia lewego przycisku myszy! 

Po uruchomieniu stworzonego kodu powinieneś uzyskad następujący rezultat: 

 

Rys. 11 Ostateczny interfejs 

Działa  wybór  oraz  przesuwanie  elementów  strony.  Pozostaje  dołączyd  funkcjonalnośd  prawego 
panelu edycyjnego. Wciśnięcie przycisku łączymy z odpowiednią metodą kontrolera, np.: 

private void obroc_lewo(object sender, RoutedEventArgs e) 

    Kontroler.ObrocLewo(); 

background image

Ponieważ zastosowaliśmy przyciski typu RepeatButton to zaznaczony obiekt będzie obracał się tak 
długo  jak  długo  będziemy  utrzymywali  przycisk  w  stanie  wciśniętym  (zdarzenie  Click  będzie 
wyzwalane w krótkich odstępach czasu). 

Możemy  teraz  obrócid  zaprojektowaną  stronę  foto  książki  z  użyciem  własności  Projection. 
Testowy interfejs jest już gotowy i pozwala rozmieszczad elementy w obrębie pojedynczej kartki. 

Porady praktyczne 

 

Pamiętaj,  że  rozdział  ten  nie  wyczerpał  informacji  o  własnościach  dołączonych  z  Silverlight 
kontrolek. Przed użyciem kontrolki pamiętaj aby zapoznad się z jej możliwościami 

 

Niektóre  panele  mogą  ignorowad  własności    wyrównania,  np.  Grid  z  ustaloną  szerokością 
kolumn 

 

Częstym  błędem  jest  kopiowanie  fragmentów  kodu  XAML  zawierającego  nazwane  elementy. 
Elementy musza mied unikatowe nazwy. 

 

Składanie przekształceo poprzedź rozpisaniem ciągu przekształceo na kartce, aż do momentu 
gdy będziesz pewien wyniku. Eksperymenty wprowadzają zamieszanie gdy liczba przekształceo 
przekracza dwa. 

 

Do obcinania elementów wystających poza stronę możesz użyd własności Clip, mianowicie po 
zmianie  rozmiaru  wyzwalane  jest  zdarzenie  SizeChanged,  możesz  dopisad  obsługę 
następującej postaci:  

private void kartka_SizeChanged(object sender, SizeChangedEventArgs e) 

    RectangleGeometry rg = new RectangleGeometry(); 

    rg.Rect = new Rect(0, 0, e.NewSize.Width, e.NewSize.Height); 

    kartka.Clip = rg; 

Uwagi dla studenta 

Jesteś przygotowany do realizacji laboratorium jeśli: 

 

Rozumiesz podstawy składni XAML, 

 

Rozumiesz zasady kompozycji rodzic-dziecko, 

 

Potrafisz kontrolowad pozycję kontrolki, 

 

Umiesz składad przekształcenia, 

 

Umiesz wykorzystad system zdarzeo, 

 

Potrafisz nazywad ważne elementy interfejsu użytkownika, 

 

Potrafisz nazwad klasy inne niż typu FrameworkElement, 

 

Wiesz jak działa przekształcenie rzutowe. 

Pamiętaj o zapoznaniu się z uwagami i poradami zawartymi w tym module. Upewnij się, że rozumiesz 
omawiane w nich zagadnienia. Jeśli masz trudności ze zrozumieniem tematu zawartego w uwagach, 
przeczytaj ponownie informacje z tego rozdziału i zajrzyj do notatek z wykładów. 

 
 

background image

 

Laboratorium podstawowe 

Zadanie 1 (czas realizacji 25 minut) 

Zbliżają się targi fotograficzne a twoja firma uzyskała zlecenie na budowę prototypowego interfejsu 
do budowy kartek z życzeniami. Użytkownik interfejsu posiadając zdjęcia powinien mied możliwośd 
umieścid zdjęcie na jednej stronie kartki z życzeniami, a na drugiej tekstu życzeo. Kartka drukowana 
będzie na dołączonej do komputerów twojego klienta drukarce dwustronnej. Prototyp nie musi mied 
możliwości fizycznego drukowania.  

 
 

background image

 

Zadanie 2 (czas realizacji 20 minut) 

Po sukcesie poprzedniego produktu, klient z branży fotograficznej zamówił w Twojej firmie interfejs 
do przetwarzania łamanych kartek z życzeniami. Do wymagao dołączył podgląd kartki w 3D. 

background image

 

Laboratorium rozszerzone 

Zadanie 1 (czas realizacji 45 minut) 

Stwórz aplikację Silverlight  w której kontrolki będą odsuwały się od kursora myszy. 

Wskazówka. Wykorzystaj metodę TranformTo do uzyskania współrzędnych w układzie związanych 
z inną kontrolką. 

Zadanie 2 (czas realizacji 45 minut) 

Napisz  aplikację  Silverlight  służącą  do  stworzenia  kartek  z  życzeniami.  Aplikacja  ta  konfigurowalna 
jest z poziomu parametrów przekazanych z HTML. Konfigurowad można zarówno typ produktu (patrz 
laboratorium podstawowe) jak i jego rozmiary. 

Zadanie 3 (czas realizacji 90 minut) 

Stwórz interfejs który pozwoli rozmieszczad zdjęcia na stronach książki o zadanej liczbie stron. 

Zadanie 4 (czas realizacji 90 minut) 

Stwórz interfejs który pozwoli rozmieszczad zdjęcia w foto-książce oraz na jej okładce.