Samouczek Silverlight – część 2

3 maja, 2010 Kategorie: Silverlight

Układ interfejsu użytkownika i nawigacja

 

To druga część (część 1) samouczka Silverlight dla początkujących przetłumaczonego na język polski (autor oryginału – Tim Heuer).

Zrozumienie zarządzania układu interfejsu w aplikacjach XAML jest ważnym elementem efektywnego tworzenia w technologii Silverlight. Dla większości użytkowników Internetu jest to największe wyzwanie, chyba, że jesteś czarodziejem CSS.

Zrozumieć znaczniki układu strony

Silverlight dostarcza elastyczny system układu interfejsu użytkownika. Istnieją dynamiczne i statyczne modele stylów. Najczęściej używane kontrolki to:

  • Canvas
  • StackPanel
  • Grid

Przyjrzyjmy się tym kontrolkom umieszczając w nich inne elementy, aby zobaczyć jak one funkcjonują. Dla demonstracji użyjemy prostego elementu przycisku. Będziemy pracować w tym samym projekcie jaki utworzyliśmy w części 1 samouczka. Dla ułatwienia użyjemy pliku Home.xaml (będzie to kod do wyrzucenia z projektu, także możesz go sobie dowolnie modyfikować).

 

Canvas

Canvas to najprostszy układ. Używany jest do umieszczania elementów używając współrzędnych absolutnych. Pozycjonujesz elementy w Canvas używając Attached Properties (dodanych atrybutów). Pozwalają one nadrzędnej kontrolce (w tym przypadku Canvas) rozszerzyć atrybuty elementu (w tym przypadku przyciskom). Przykładowo, możemy rozmieścić kilka przycisków w Canvas (umieść poniższy kod pod drugim znacznikiem Textblock w pliku Home.xaml, uprzednio usuń kod przycisku dodany w części 1):

<Canvas> <Button Canvas.Top="50" Canvas.Left="50" Content="Button 1" FontSize="18" Width="150" Height="45" /> <Button Canvas.Top="150" Canvas.Left="20" Content="Button 2" FontSize="18" Width="150" Height="45" /> <Button Canvas.Top="70" Canvas.Left="80" Canvas.ZIndex="99" Content="Button 3" FontSize="18" Width="150" Height="45" /> </Canvas>

 

kiedy uruchomimy aplikacje zobaczymy:

image

Jak widać jest pozycjonowanie przy użyciu współrzędnych absolutnych. Zauważ w kodzie przycisku 3, że można także zdefiniować oś Z co powoduje, że przycisk 3 jest widoczny nad przyciskiem 1. To może być przydatne przy projektowaniu gier lub dokładnym odzwierciedlaniu zjawisk fizycznych. Canvas jest przydatne gdy elementy aplikacji są bardzo statyczne lub gdy masz pełna kontrole nad rozmiarem aplikacji. W innych przypadkach lepiej użyć StackPanel lub Grid.

StackPanel

StackPanel to sposób na ułożenie elementów jeden po drugim, w pionie lub poziomie (standardowo w pionie). Używając naszych 3 przycisków i poniższego kodu (dodanego zamiast Canvas, pod drugim znacznikiem TextBlock w pliku Home.xaml):

<StackPanel> <Button Margin="10" Content="Button 1" FontSize="18" Width="150" Height="45" /> <Button Margin="10" Content="Button 2" FontSize="18" Width="150" Height="45" /> <Button Margin="10" Content="Button 3" FontSize="18" Width="150" Height="45" /> </StackPanel> 

uzyskamy efekt:

image

Możemy także zmienić układ przycisków na poziomy używając poniższego kodu (tylko zmiana atrybutu Orientation w znaczniku StackPanel):

<StackPanel Orientation="Horizontal"> <Button Margin="10" Content="Button 1" FontSize="18" Width="150" Height="45" /> <Button Margin="10" Content="Button 2" FontSize="18" Width="150" Height="45" /> <Button Margin="10" Content="Button 3" FontSize="18" Width="150" Height="45" /> </StackPanel> 

uzyskamy efekt:

image

StackPanel pozwala na szybkie ułożenie elementów jeden po drugim bez konieczności dbania o ich współrzędne.

 

Grid

Najbardziej elastycznym układem jest Grid. Jest struktura składająca sie z kolumn i wierszy. Większość deweloperów w internecie zna znacznik <table> gdzie zawartość umieszczana jest w znacznikach <tr> i <td>, XAML Grid jest inny. Definiujesz ogólną strukturę Grid i używasz Attached Proprties (dodanych atrybutów) aby wskazać elementom ich pozycje.

Popatrz na poniższy kod (zauważ bezpośrednią definicje wierszy Grid (Grid.RowDefinition i Grid.ColumnDefinition, tego zazwyczaj się nie robi, ale zawarte jest w kodzie dla łatwiejszego zrozumienia koncepcji):

<Grid ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition Height="60" /> <RowDefinition Height="60" /> <RowDefinition Height="60" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="175" /> <ColumnDefinition Width="175" /> <ColumnDefinition Width="175" /> </Grid.ColumnDefinitions> <Button Grid.Column="0" Grid.Row="0" Content="Button 1" FontSize="18" Width="150" Height="45" /> <Button Grid.Column="2" Grid.Row="0" Margin="10" Content="Button 2" FontSize="18" Width="150" Height="45" /> <Button Grid.Column="1" Grid.Row="2" Margin="10" Content="Button 3" FontSize="18" Width="150" Height="45" /> </Grid> 

 

Definiujemy Grid z 3 kolumnami i 3 wierszami o ustalonej szerokości i wysokości. Zauważ, że w atrybutach przycisków, ustawiamy je w Grid używając dodanych atrybutów (Grid. column i Grid.Row). Uzyskany efekt:

image

Praca z układami to element gdzie bardzo przydatny może być Expression Blend. Zauważ jak można użyć suwaków do wizualnego zdefiniowania atrybutów kolumn/wierszy, a następnie stworzenia pliku XAML na podstawie naszego wizualnie stworzonego układu:

image

Strzałki wskazują suwaki oraz kłódkę sygnalizującą stały rozmiar kolumn/wierszy (nie może być zmieniony). Tak naprawdę bazowy szablon projekty, który wybraliśmy używa wszystkich wymienionych powyżej możliwości układu jako bazy dla naszej aplikacji.

 

Konstruowanie naszej aplikacji

Teraz możemy zacząć budowę naszej aplikacji. Oto szkielet, który chcemy zaprojektować:

image

Mamy miejsce gdzie użytkownik może wpisać szukane słowa, listę rezultatów wyszukiwania. Odnośniki na górze strony pozwolą na obejrzenie historii i statystyki wyszukiwań.

Szablon nawigacyjny, który wybraliśmy przy tworzeniu nowego projektu, przygotował już za nas ogólny układ. W pliku MainPage.xaml dokonamy kilku zmian. W okolicach linii 29 w pliku MainPage.xaml usuwamy logo aplikacji

<ContentControl Style="{StaticResource LogoIcon}"/> 

 

oraz zmieniamy ApplicationnameTextBlock na Wyszukiwarka Twitter.

<TextBlock x:Name="ApplicationNameTextBlock" Style="{StaticResource ApplicationNameStyle}" Text="Wyszukiwarka Twitter"/> 

 

Za chwilę powrócimy do nawigacji, lecz najpierw stwórzmy odpowiednie widoki (Views). W strukturze projektu, w katalogu Views stwórzmy nową stronę poprzez prawy klik w Visual Studio i wybraniu nowej Silverlight Page, nazwijmy ją Seach.xaml:

image

image

Stworzyliśmy pustą stronę ze standardowym układem Grid. Tutaj odtworzymy stronę wyszukiwania zaprojektowaną powyżej. Informacje nagłówkowe pochodzą z pliku MainPage.xaml ponieważ używamy elementu Frame do obsługi nawigacji.

 

Struktura nawigacyjna Silverlight

Spróbujemy teraz zrozumieć mechanizm nawigacyjny Silverlight. Jak sobie przypominacie, nowy projekt utworzyliśmy korzystając z szablonu nawigacyjnego. W zestawie otrzymaliśmy MainPage.xaml oraz kilka widoków (Views) – Home, About. Struktura nawigacyjna Silverlight składa się z 3 części: UriMapper, Frame i Page

UriMapper

Wyobrażam sobie UriMapper jako swego rodzaju kierunkowskaz. Nie jest wymagany ale ukrywa techniczne szczegóły i jednocześnie ułatwia dotarcie do celu. Zamiast używania dosłownego /Views/Home.xaml punktu docelowego URI, możemy zastąpić go prostszym /Home punktem docelowym, który jest bardziej czytelny, jednocześnie nie zdradzając żadnych szczegółów konfiguracyjnych. Dodatkowo, w przyszłości, może wskazywać na inny punkt docelowy. Możecie zobaczyć UriMapper jako element Frame w MainPage.xaml:

<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed"> <navigation:Frame.UriMapper> <uriMapper:UriMapper> <uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/> <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/> </uriMapper:UriMapper> </navigation:Frame.UriMapper> </navigation:Frame> 

 

 

Frame

Jeśli znasz ASP.NET, możesz traktować element Frame jak kontrolkę ContentPlaceHolder. Frame to obszar , w którym można nawigować. Definiujesz standardowy widok ale nawigacja może odbywać się tylko w nim. Patrząc na powyższy kod, widzimy, że w naszej aplikacji standardowym widokiem (atrybut Source) jest /Home.

Page

Ostatnim podstawowym elementem nawigacyjnym jest Page, który stworzyliśmy w poprzednim kroku (Search.xaml). Jest to po prostu zawartość wyświetlana w elemencie Frame. Bardzo podobne do podstawowych elementów UserControl, które możemy dodać, odróżniają się możliwością interakcji z nawigacją. W naszej aplikacji traktujmy widoki (Views) jako elementy Page.

Możecie dowiedzieć sie więcej oglądając video samouczki (ang.)

Cały koncept jest w sumie prosty do zrozumienia jak tylko się zacznie go używać. Ma też ogromne możliwości. Mechanizm ten pozwala na istnienie bezpośrednich odnośników do pod-stron aplikacji Silverlight, a nie tylko do głównej strony.

 

Tworzenie interfejsu użytkownika w naszym widoku wyszukiwania

Zakończmy tworzenie interfejsu użytkownika w nowo utworzonej stronie Search.xaml. Być może w tym momencie zastanawiasz się do czego służą elementy {StaticResource XXXXXXXXX} widoczne w plikach XAML. Zajmiemy się nimi w części 5 – style i szablony, także na razie zapominamy o nich.

Patrząc na projekt, widzimy, że potrzebujemy miejsca na wpisywanie tekstu (text input area), przycisku (button) i tabeli wyświetlającej dane (data display grid).

W tym momencie pan Tim sugeruje użycie Blend do stosownego zmodyfikowania pliku Search.xaml. Jako, że Blend jest płatnym narzędziem oraz przyznam się, że dla mnie niezbyt intuicyjnym. Pominę fragment użycia tego narzędzia i skupimy sie na czystej modyfikacji kodu XAML.

Co mamy do zrobienia. Zmodyfikować w Search.xaml element Grid, aby posiadał 2 wiersze. Jeden wiersz na wpisanie szukanej frazy i przycisk, drugi dla wyświetlenia rezultatów. W górnym wierszu dodamy StackPanel, a w nim TextBox i Button, z poziomą orientacją.

Następnie w drugim wierszu dodamy DataGrid, który będzie wyświetlał rezultaty wyszukiwania. Ponieważ DataGrid nie jest podstawowym elementem Silverlight i jest dostępny w bibliotekach SDK, musimy dodać referencje do biblioteki. Blend robi to automatycznie, a my zrobimy to w kodzie XAML:

<navigation:Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="WyszukiwarkaTwitter.Views.Search" mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480" Title="Search Page"> <Grid x:Name="LayoutRoot"> <Grid.RowDefinitions> <RowDefinition Height="32"/> <RowDefinition/> </Grid.RowDefinitions> <StackPanel HorizontalAlignment="Left" Margin="0,-32,0,0" VerticalAlignment="Top" Grid.Row="1" Orientation="Horizontal"> <TextBox x:Name="SearchTerm" FontSize="14.667" Margin="0,0,10,0" Width="275" TextWrapping="Wrap"/> <Button x:Name="SearchButton" Width="75" Content="Szukaj"/> </StackPanel> <sdk:DataGrid x:Name="SearchResults" Grid.Row="1"/> </Grid> </navigation:Page> 

 

Co się zmieniło:

  • dodana referencja do sdk – xmlns:sdk (referencja z oryginalnego kodu samouczka, nie działała u mnie)
  • definicja x:Class została przeniesiona z piewrszwj lini navigation:Page do referencji sdk
  • dodano definicje wierszy Grid – Grid.Rowdefinition
  • dodano StackPanel w pierwszym wierszu Grid – StackPanel, a wnim TextBox i Button
  • dodano w drugim wierszu Grid, DataGrid z SDK

W rezultacie:

image

Zwróćcie uwagę na nadane każdemu elementowi nazwy (znacznik x:Name), TextBox (okionko wpisywania szukanego tekstu) nazywa się SearchTerm, Button (przycisk) to SearchButton, a DataGrid (rezultaty wyszukiwania) to SearchResults. Nazwy te ułatwią używanie tych elementów w kodzie.

 

Zmieniamy nawigacje i UriMapper

Po stworzeniu widoku Search (która tak naprawdę jest stroną główna naszej aplikacji), wykonamy kilka zmian w naszej strukturze nawigacyjnej. w MainPage.xaml odnajdź element Frame i zamieńmy standardowy widok aplikacji z Home.xaml na Search.xaml (znaczniki Source i pierwszy MappedUri).

<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" Source="/Search" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed"> <navigation:Frame.UriMapper> <uriMapper:UriMapper> <uriMapper:UriMapping Uri="" MappedUri="/Views/Search.xaml"/> <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/> </uriMapper:UriMapper> </navigation:Frame.UriMapper> </navigation:Frame> 

 

 

Ponieważ nie potrzebujemy już widoku Home.xaml, usuniemy go z projektu (prawy klik na pliku):

image

Teraz dodamy nowy widok (view) i nazwiemy go History.xaml.

Na koniec umieścimy odnośnik w MainPage.xaml do nowo utworzonego widoku:

<Border x:Name="LinksBorder" Style="{StaticResource LinksBorderStyle}"> <StackPanel x:Name="LinksStackPanel" Style="{StaticResource LinksStackPanelStyle}"> <HyperlinkButton x:Name="Link1" Style="{StaticResource LinkStyle}" NavigateUri="/Search" TargetName="ContentFrame" Content="szukaj"/> <Rectangle x:Name="Divider1" Style="{StaticResource DividerStyle}"/> <HyperlinkButton x:Name="Link2" Style="{StaticResource LinkStyle}" NavigateUri="/History" TargetName="ContentFrame" Content="historia"/> <Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/> <HyperlinkButton x:Name="Link3" Style="{StaticResource LinkStyle}" NavigateUri="/About" TargetName="ContentFrame" Content="o nas"/> </StackPanel> </Border> 

 

Końcowy rezultat:

image

Po uporaniu się z podstawowym układem stron, czas na połączenie z danymi w części 3!

Znaczniki: , , ,

3 komentarze do “Samouczek Silverlight – część 2”

  1. dinsdale
    19 listopada, 2012 o 22:35
    1

    Witam.

    Zaczynam swoją przygodę z Silverlight i na razie kopuję i wklejam Twój kod 😉

    W tej części są 2 nieścisłości:
    – piszesz UniMapper,a faktycznie wydaje się to być UriMapper
    – coś jest nie tak przy referencjach dla DataGrid, zastosowanie Twojego kodu wywołuje błędy:
    „The type 'sdk:DataGrid’ was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built.”
    „The type or namespace name 'DataGrid’ could not be found (are you missing a using directive or an assembly reference?)”; chyba, że to mi czegoś w konfiguracji brakuje…

    Pozdrawiam

  2. 11 grudnia, 2012 o 10:24
    2

    Siema,

    masz racje UniMapper = UriMapper. Zaraz poprawie wpis.
    Co do DataGrid to potrzebna jest referencja dodatkowa (patrz xmlns:sdk ).
    Ponadto od czasu tego samouczka zmieniły się wersje Silverlight, więc nie dam głowy, że teraz to działa. Ostatnio zarzuciłem temat Silverlighta i generalnie bloga, więc pewnie bedę zwijał stronkę. Ciesze się, że czasami się przydała.

    Pozdrawiam, Irek

  3. 14 stycznia, 2013 o 12:07
    3

    Nie zwijaj strony, nie ma wiele polskojezycznych tutków o SL więc jest tutaj kilka ciekawych informacji 🙂

    Pozdrawiam i powodzenia życzę
    Michał

Wpisz komentarz