Programowanie aplikacji pod nowy interfejs Windows 8 w C# lub Visual Basic - Szybki start: tworzenie interfejsu użytkownika przy użyciu XAML  Udostępnij na: Facebook

Tłumaczenie na podstawie Quickstart: creating a user interface with XAML: Paweł Sołtysiak

Opublikowano: 2012-04-17

Pisząc aplikację w pod nowy interfejs Windows 8 przy użyciu Microsoft Visual Basic, C# lub C++, aby móc zdefiniować interfejs użytkownika, możesz wykorzystać Extensible Application Markup Language (w skrócie XAML). XAML to deklaratywny język, który możesz użyć do tworzenia różnych elementów, pokazywanych na ekranie, takich jak: kontrolki, tekst czy rysunki. Jeżeli do tej pory zajmowałeś się programowaniem aplikacji internetowych, możesz zauważyć podobieństwo pomiędzy XAML, a językiem HTML. Podobnie jak HTML, XAML składa się z elementów oraz atrybutów, a ponieważ jest oparty na XML, tak więc musi spełniać ściśle określone zasady XML. Możesz zastanawiać się: „Dlaczego, w celu utworzenia interfejsu użytkownika, powinienem zajmować się XAML, skoro mogę po prostu użyć narzędzi, takich jak Visual Studio lub Expression Blend"? Pomimo istnienia programów pozwalających na generowanie znaczników XAML, warto wiedzieć, co dzieje się "pod maską". Możesz zostać również zmuszony do wprowadzania zmian wewnątrz wygenerowanego kodu. Czasami łatwiej jest napisać ręcznie kod interfejsu użytkownika, szczególnie, jeśli chce się mieć pełną kontrolę nad tym, co dzieje się w programie.

Jak ten temat odnosi się do innych zagadnień? Zobacz:

Wymagania

Zakładamy, że potrafisz pisać proste aplikacje w stylu nowego interfejsu Windows 8 przy użyciu Visual Basic, C# lub C++. Możesz zapoznać się z artykułem: Building your first Windows Metro style app using C#, C++, or Visual Basic.

Przykłady z XAML

Poniżej znajduje się przykład, tworzący przycisk:

<Grid x:Name="ContentPanel" Margin="12,0,12,0">
    <Button Height="72" Width="160" Content="Kliknij mnie" />
</Grid>

Użycie elementu <Button> spowoduje wykorzystanie kontrolki Button. Właściwości Width i Height określają rozmiar kontrolki, odpowiednio szerokość i wysokość przycisku. Element <Grid> został wygenerowany w momencie utworzenia nowej aplikacji, przy użyciu języka C++, C# lub Visual Basic, wewnątrz Visual Studio, i jest wykorzystywany w celu umieszczenia różnych obiektów w interfejsie użytkownika. Zobacz Quickstart: Defining Layouts, aby dowiedzieć się więcej o układzie XAML.

Możesz użyć Visual Studio w celu wygenerowania XAML. Dla przykładu, możesz przeciągnąć przycisk z okienka Toolbox na przestrzeń projektową.

Poniżej, znajduje się kod XAML, który mógł zostać wygenerowany przez Visual Studio (Twój kod może się różnić od podanego):

<Grid x:Name="ContentGrid">
    <Button Content="Button" Height="72" HorizontalAlignment="Left"
     Margin="152,273,0,0" Name="button1" VerticalAlignment="Top" Width="160" />
</Grid>

Visual Studio dodaje kilka dodatkowych atrybutów, takich jak HorizontalAligment i Margin, aby ustawić pozycję przycisku. Jeżeli wykorzystujesz zależności pomiędzy elementami, te atrybuty mogą przeszkadzać w prawidłowym umieszczeniu przycisku.

Najważniejszą cechą, powiązaną z wykorzystaniem języka deklaratywnego, takiego jak XAML, jest rozdzielenie kodu, który tworzy interfejs użytkownika oraz kodu, który zajmuje się logiką aplikacji. Na przykład, projektant z Twojego zespołu może utworzyć interfejs użytkownika i przekazać kod XAML do programisty, który doda kod proceduralny. Nawet jeżeli projektant i programista to ta sama osoba (bardzo często się tak zdarza), to możesz umieścić opis interfejsu użytkownika w plikach XAML (.xaml), a kod proceduralny w plikach .cs lub .vb.

XAML to kod proceduralny (wyrażony w prosty sposób)

Elementy, takie jak <Button /> posiadają odpowiadające im instancje obiektów w kodzie proceduralnym. Na przykład, poniższy kod XAML:

<Grid x:Name="ContentPanel" Margin="12,0,12,0">
    <Button Height="72" Width="160" Content="Kliknij mnie" />
</Grid>

może zostać zapisany w języku C#, C++ Visual Basic:

C#

// Utworzenie przycisku
Button myButton = new Button();
// Ustawienie właściwości
myButton.Width = 160;
myButton.Height = 72;
myButton.Content = "Kliknij mnie";
// Podpięcie przycisku do istniejącego drzewa wizualnego, określonego jako dziecko
// obiektu Grid (nazwanego 'ContentPanel'). Inaczej mówiąc, ustawiamy
// pozycję przycisku wewnątrz interfejsu użytkownika
ContentPanel.Children.Add(myButton);

C++

// Utworzenie przycisku
Button^ myButton = ref new Button();
// Ustawienie właściwości
myButton->Width = 160;
myButton->Height = 72;
myButton->Content = "Kliknij mnie";
// Podpięcie przycisku do istniejącego drzewa wizualnego, określonego jako dziecko
// obiektu Grid (nazwanego 'ContentPanel'). Inaczej mówiąc, ustawiamy
// pozycję przycisku wewnątrz interfejsu użytkownika
ContentPanel->Children->Append(myButton);

VB

' Utworzenie przycisku
Dim myButton As New Button()
' Ustawienie właściwości
myButton.Width = 160
myButton.Height = 72
myButton.Content = "Kliknij mnie"
' Podpięcie przycisku do istniejącego drzewa wizualnego, określonego jako dziecko
' obiektu Grid (nazwanego 'ContentPanel'). Inaczej mówiąc, ustawiamy
' pozycję przycisku wewnątrz interfejsu użytkownika
ContentPanel.Children.Add(myButton)

Dla interfejsu użytkownika, XAML jest łatwiejszy do czytania oraz bardziej zwięzły niż kod proceduralny. W celu utworzenia interfejsu użytkownika w sposób dynamiczny, czasem trzeba wykorzystać kod proceduralny.

Właściwości

Są dwa sposoby na ustawienie właściwości w XAML:

  • wykorzystanie składni, w sposób ustawiania atrybutu,
  • wykorzystanie składni, w sposób ustawiania właściwości.

Składnia ustawienia atrybutu jest następująca: attributte="wartość". Taki sposób zapisu pojawiał się we wcześniejszych przykładach i może wyglądać podobnie do zapisu wykorzystywanego w HTML. W następnym przykładzie, poprzez ustawienie atrybutu Fill na jeden z predefiniowanych kolorów, utworzymy prostokąt i wypełnimy go kolorem:

<Rectangle Fill="Red" />

Ewentualnie, możemy określić kolor poprzez użycie składni do ustawienia właściwości:

<Rectangle>
    <Rectangle.Fill>
        <SolidColorBrush Color="Red" />
    </Rectangle.Fill>
</Rectangle>

W tym przypadku, w celu ustawienia właściwości, zamiast posługiwać się predefiniowanym napisem "Red", określasz jawnie wykorzystany obiekt - SolidColorBrush, który jest typem wymaganym przez właściwość Fill. Z tego przykładu mogłeś wywnioskować, że wykorzystanie składni, w celu ustawienia właściwości, jest tylko dłuższym zapisem ustawienia atrybutu. Nie wszystkie właściwości obiektu mogą zostać określone poprzez użycie tekstu, który można przypisać do atrybutu. Na przykład, jeżeli potrzebujesz ustawić wiele właściwości obiektu wewnątrz innego obiektu, będziesz potrzebował użyć tej rozbudowanej składni. W poniższym przykładzie utworzymy prostokąt (Rectangle), a zamiast wypełnić go jednolitym kolorem czerwonym, wypełnimy go gradientem:

<!-- Ten prostokąt został narysowany z przekątnym, linowym gradientem. -->
<Rectangle Width="200" Height="200">
    <Rectangle.Fill>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
            <GradientStop Color="Yellow" Offset="0.0" />
            <GradientStop Color="Red" Offset="0.25" />
            <GradientStop Color="Blue" Offset="0.75" />
            <GradientStop Color="LimeGreen" Offset="1.0" />
        </LinearGradientBrush> 
    </Rectangle.Fill>
</Rectangle>

Właściwość Fill korzysta ze złożonego obiektu - LinearGradientBrush, aby utworzyć gradient. W takim przypadku, jesteś zmuszony do użycia rozbudowanej składni w celu ustawienia właściwości.

XAML i drzewo wizualne

W XAML istnieją elementy, takie jak <Button> i <Grid>, które mogą zawierać inne elementy (węzły) wewnątrz nich (dzieci). Taka relacja rodzic - dziecko określa sposób umiejscowienia obiektów na ekranie oraz wskazuje, jak mają odpowiadać na działania użytkownika. Rozważ poniższy przykład:

<Grid x:Name="ContentPanel" Background="Red" Grid.Row="1" Margin="12,0,12,0">
    <StackPanel Margin="20" Background="Blue" >
        <TextBlock Name="firstTextBlock" FontSize="30">Pierwszy TextBlock</TextBlock>
        <TextBlock Name="secondTextBlock" FontSize="30">Drugi TextBlock</TextBlock>
        <TextBlock Name="thirdTextBlock" FontSize="30">Trzeci TextBlock</TextBlock>
    </StackPanel>
</Grid>

Niebieski StackPanel znajduje się wewnątrz czerwonego Grid. Elementy TextBlock są umieszczone wewnątrz StackPanel (te elementy TextBlock są dziećmi StackPanel). Dodatkowo, elementy TextBlock są umieszczone jeden po drugim, w kolejności takiej, w jakiej zostały zadeklarowane w XAML.

Poniższy diagram przedstawia zależności pomiędzy elementami:

Oprócz ustalenia, w jaki sposób zawartość ma zostać wyświetlona, możesz także zdefiniować, w jaki sposób mają zostać przetworzone zdarzenia za pomocą drzewa wizualnego. Wiele zdarzeń, które są powiązane interfejsem użytkownika i danymi wejściowymi od użytkownika, „idzie w górę” według ułożenia drzewa. Na przykład, możesz zdefiniować obsługę zdarzenia w StackPanel, które wywoływane jest, kiedy użytkownik wciśnie/kliknie na którykolwiek obiekt TextBlock. W następujący sposób można dodać obsługę zdarzenia PointerPressed, nazwanego "commonHandler", do StackPanel, występującego w diagramie:

<Grid Background="Red" x:Name="ContentPanel" Margin="12,0,12,0">
    <StackPanel Margin="20" Background="Blue" PointerPressed="commonHandler">
        <TextBlock Name="firstTextBlock" FontSize="30" >Pierwszy TextBlock</TextBlock>
        <TextBlock Name="secondTextBlock" FontSize="30" >Drugi TextBlock</TextBlock>
        <TextBlock Name="thirdTextBlock" FontSize="30" >Trzeci TextBlock</TextBlock>
    </StackPanel>
</Grid>

Poniżej, znajduje się kod proceduralny, który obsługuje zdarzenie:

C#

private void commonHandler(object sender, PointerEventArgs e)
{
FrameworkElement feSource = e.OriginalSource as FrameworkElement;
switch (feSource.Name)
{
    case "firstTextBlock":
        firstTextBlock.Text = firstTextBlock.Text + " kliknięty!";
        break;
    case "secondTextBlock":
        secondTextBlock.Text = secondTextBlock.Text + " kliknięty!";
        break;
    case "thirdTextBlock":
        thirdTextBlock.Text = thirdTextBlock.Text + " kliknięty!";
        break;
    }
}

C++

virtual void CommonHandler(Platform::Object^  sender, 
                    Windows::UI::Xaml::Input::PointerEventArgs^ e);

//BlankPage.xaml.cpp
void BlankPage::CommonHandler(Platform::Object^  sender, 
                    Windows::UI::Xaml::Input::PointerEventArgs^ e)
{
    FrameworkElement^ feSource = safe_cast<FrameworkElement^>(e->OriginalSource);

    if (feSource->Name == "firstTextBlock")
    {
        firstTextBlock->Text = firstTextBlock->Text + " kliknięty!";
    }
    else if (feSource->Name == "secondTextBlock")
    {
        secondTextBlock->Text = secondTextBlock->Text + " kliknięty!"; 
    }
    else if(feSource->Name == "thirdTextBlock")
    { 
        thirdTextBlock->Text = thirdTextBlock->Text + " kliknięty!"; 
    }  
}

VB

Private Sub commonHandler(ByVal sender As System.Object, _
ByVal e As PointerEventArgs)
    Dim feSource As FrameworkElement = e.OriginalSource
    Select Case feSource.Name
        Case "firstTextBlock"
            firstTextBlock.Text = firstTextBlock.Text & " kliknięty!"
        Case "secondTextBlock"
            secondTextBlock.Text = secondTextBlock.Text & " kliknięty!"
        Case "thirdTextBlock"
            thirdTextBlock.Text = thirdTextBlock.Text & " kliknięty!"
    End Select
End Sub

Kiedy uruchomisz ten przykład i wciśniesz/klikniesz TextBlock, zdarzenie zostanie przechwycone przez TextBlock, ale później „pójdzie w górę” do jego rodzica (StackPanel), który obsługuje zdarzenie.

Poniższy diagram przedstawia, jak zdarzenie „idzie w górę” w drzewie:

Ponieważ opisane zdarzenie cały czas idzie górę wewnątrz drzewa, możesz obsłużyć zdarzenie PointerPressed także wewnątrz elementu Grid.

Szybki poradnik po atrybutach XAML oraz rozszerzeń składni

W momencie tworzenia lub czytania kodu XAML, który został utworzony przez Visual Studio, wykorzystywane są często konwencje, unikalne dla XAML, które nie są odzwierciedlone bezpośrednio przez język programowania. Poniżej, znajduje się spis kilku konwencji:

  • atrybut x:Name. Dodaj ten atrybut do każdego obiektu XAML, do którego chcesz się odwołać w części code-behind. Napis, który wpisujesz wewnątrz x:Name staje się nazwą dla utworzonej instancji wewnątrz częściowej klasy, która jest współzależna z XAML, i dzięki temu możesz odwołać się do instancji poprzez nazwę. Visual Studio lub Expression Blend często dodają atrybut x:Name do XAML automatycznie,
  • atrybut x:Key. Dodaj ten atrybut do obiektów XAML, wówczas gdy definiujesz zasoby, które są zapisane w XAML wewnątrz ResourceDictionary,
  • symbol x:, w obu x:Key oraz x:Name, identyfikuje te atrybuty, jako podmioty zdefiniowane wewnątrz przestrzeni nazw, która sama jest językiem XAML. Dzięki temu te atrybuty mogą zostać zastosowane wobec każdego innego obiektu XAML, włączając w to elementy, które reprezentują klasy utworzone przez Ciebie,
  • {Binding} stanowi rozszerzenie składni XAML, które można użyć do ustanowienia wiązania pomiędzy referencjami w XAML. W zasadzie, {Binding} w XAML jest równoznaczny z użyciem klasy Binding w kodzie, a konkretnie, ustanowienie wiązania za pomocą {Binding} w XAML jest jak wywołanie metody o nazwie SetBinding. {Binding} może przejąć serię argumentów, które nie zostaną tutaj opisane. Zobacz Quickstart: Data binding to controls, aby dowiedzieć się więcej na temat wiązania danych,
  • {TemplateBinding} jest wyspecjalizowaną formą wiązania. Łączy ona właściwości szablonów z właściwościami klasy, która wykorzystuje szablon. {RelativeSource} jest relacyjnym rozszerzeniem składni, który pozwala na ustawienie właściwości RelativeSource w składni,
  • {StaticResource} stanowi rozszerzenie składni, które pozwala na użycie każdego zasobu zdefiniowanego jako XAML, wewnątrz ResourceDictionary. {StaticResource} wymaga jednego argumentu, który wykorzystuje napisy wewnątrz x:Key. Pozwala to na zidentyfikowanie zasobu w ResourceDictionary,
  • jeżeli kiedykolwiek będziesz potrzebował zdefiniować wartość null w XAML, użyj napisu {x:Null}. To rozszerzenie składni zwraca wartość null, kiedy zostanie wykonane.

Powiązane tematy

Roadmap for creating Metro style apps using C#, C++, or Visual Basic