Button jako UserControl na stylach w Silverlight 2.0

A tym artykule postaram się opisać w jaki sposób możemy stworzyć własne kontrolki użytkownika (UserControls) w Silverlight 2.0. Do wszystkich etapów zamieszczony jest przykładowy kod oraz demo.

Misja na dziś:

1.       Utworzymy prostą kontrolkę i wyświetlimy w aplikacji.

2.       Dodamy właściwości kontrolki.

3.       Zdefiniujemy style oraz wewnętrzne metody.

4.       Udostępnimy wywoływanie eventów z kontrolki.

5.       Zamkniemy naszą kontrolkę w assembly, tak aby mogła być używana wielokrotnie w różnych projektach.

 

1.  Najprostsza kontrolka jaką tylko się da stworzyć

Tworzymy nowy projekt Silverlight 2.0 “Silverlight Application”. Jeśli nie macie zainstalowanego dodatku do Visual Studio, możecie pobrać Silverlight Tools Beta 1 for Visual Studio 2008, które zawiera wszystko co potrzebne aby rozpocząć zabawę z SL2.0. Po więcej informacji zapraszam do mojego poprzedniego artykułu Silverlight 2.0 (Beta1) już dostępny!

Do projektu dodajemy kontrolkę użytkownika („User Control”). Prawym na projekcie, Add -> New Item.

  

W menu, które się pojawi wybieramy Silverlight User Control i wpisujemy nazwę naszej kontrolki.

 

W kodzie kontrolki wrzucamy wszystko, co chcemy aby zawierała, w moim wypadku jest to zwykły TextBlock z napisem Hello World!!

<UserControl x:Class="MyButton.CustomButton"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    Width="400" Height="300">

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

        <TextBlock Text="Hello World!!" />

    </Grid>

</UserControl>

 

Aby kontrolka mogła być widoczna na innej stronie, w kodzie xaml powinniśmy dodać deklaracje przestrzeni nazw, czyli zdefiniować nasz namespace. W moim przykładzie kontrolka znajduje się w przestrzeni nazw „MyButton”, w związku z tym w kodzie strony Page.xaml dodałem: xmlns:uc="clr-namespace:MyButton". Od tej pory mogę wywoływać kontrolkę poprzez znacznik <uc:CustomButton /> , gdzie „CustomButton” to nazwa klasy kontrolki którą dodałem powyżej.

Najprostszy przykłady użycia kontrolki może wyglądać tak:

Kod Page.xaml:

<UserControl x:Class="MyButton.Page"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    xmlns:uc="clr-namespace:MyButton"

    Width="400" Height="300">

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

        <uc:CustomButton />

    </Grid>

</UserControl>

Uruchamiamy program i powinniśmy zobaczyć naszą kontorlkę.

Demo  

Source code do tego etapu:

Source code

2. Dodajemy właściwości dla kontorlki

Kolejnym etapem będzie rozszerzenie funkcjonalności stworzonej kontrolki o możliwość definiowania jej parametrów, np. wysokość, tło czy tekst.

Zanim zaczniemy dodawać właściwości przeróbmy kontrolkę z TextBoxa na coś bardziej przydatnego, np. Button. W tym celu przy pomoc programu Expression Blend utworzyłem kontrolkę, która zawiera w sobie: ramkę (Border), a w ramce znajduje się przycisk (Button). Ponieważ w nowym SL2.0 można wypełniać przyciski nie tylko tekstem, umieszczamy w środku zagnieżdżony panel (StackPanel). W panelu umieściłem w/w TextBox, czyli nasze Hello World.

W „CustomButton.xaml”:

Zastępujemy kod:

        <TextBlock Text="Hello World!!" />

kodem:

<Border BorderThickness="1,2,3,4" Padding="5,5,5,5" CornerRadius="20,20,20,20" VerticalAlignment="Stretch" Background="#FFBD5757">

                <Button x:Name="mojButton" Cursor="Hand" Height="Auto" Width="Auto" >

                <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">

                                               <TextBlock x:Name="mojTekst" Text="Hello World!!"  />                                       

                </StackPanel>

            </Button>

        </Border>

Dodatkowo, aby poprawić wygląd dodałem także tło w gridzie, zbudowane na gradiencie:

<Grid x:Name="LayoutRoot" Margin="1" >

                <Grid.Background>

                               <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

                                               <GradientStop Color="#FF000000" Offset="0.504"/>

                                               <GradientStop Color="#FFFFFFFF" Offset="1"/>

                                               <GradientStop Color="#FFFFFFFF" Offset="0"/>

                               </LinearGradientBrush>

                </Grid.Background>

<Border BorderThickness…

</Border>       

    </Grid>

</UserControl>

Jak możecie zauważyć powyżej, nadałem także nazwy użytym kontrolkom, czyli dla Button-a jest to x:Name="mojButton" a dla TextBox-a x:Name="mojTekst".

Następnie przejdzmy do kodu (code behind), w moim wypadku jest to „CustomButton.xaml.cs”. W kodzie tym, dodajmy kilka właściwości, np. wspomniany tekst, wysokość oraz szerokość. Przykładowy kod:

namespace MyButton

{

    public partial class CustomButton : UserControl

    {

        public double Wysokosc { get { return mojTekst.Height; } set { mojTekst.Height = value; } }

        public double Szerokosc { get { return mojTekst.Width; } set { mojTekst.Width = value; } }

        public string Text { get { return mojTekst.Text; } set { mojTekst.Text = value; } }

        public CustomButton()

        {

            InitializeComponent();

        }

    }

}

Wróćmy teraz do kodu strony Page.xaml i ustawmy nowe atrybuty kontrolki.

<UserControl x:Class="MyButton.Page"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    xmlns:uc="clr-namespace:MyButton"

    >

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

        <StackPanel x:Name="panelKontrolek" Background="#FF354D61">

        <uc:CustomButton Wysokosc="120" Tekst="duza"  Szerokosc="250" />

        <uc:CustomButton Wysokosc="90" Szerokosc="190" Tekst="mala" />

        <uc:CustomButton Tekst="domyslna" />

            </StackPanel>

    </Canvas>  

</UserControl>

 

Dodatkowo, tak jak widziecie, usunąłem grida i zastąpiłem go Canvas (x:Name="LayoutRoot") z StackPanel-em (x:Name="panelKontrolek" ) w środku. Canvas pozwala nam na dowolne wrzucanie elementów, a StackPanel dba o to aby wszystkie zostały wyświetlone.

W powyższym kodzie dodałem cztery kontrolki, dla części z nich ustawiłem wszystkie parametry, a dla niektórych tylko wybrane. Efekty działania tego kodu możemy zobaczyć na końcu tego działu.

 Kontrolki możemy dodawać także w code behinde aplikacji, czyli pliku .cs. Aby dodać kontorlkę w pliku Page.xaml.cs należy utworzyć obiekt typu CustomButton, ustawić jego właściwości oraz dodać do canvy „LayoutRoot”.

public Page()

        {

            InitializeComponent();

            CustomButton cb = new CustomButton();

            cb.Tekst = "z code behind";

            cb.Wysokosc = 90;

            cb.Szerokosc = 190;

            panelKontrolek.Children.Add(cb);

        }

demo

Source code do tego etapu:

Source code

3. Style oraz wewnętrzne metody kontrolki

W tym kroku pokażę jak możemy w łatwy sposób dostosować stworzoną kontrolkę do naszych potrzeb. Najpierw dodajmy prosty styl kontrolce:

<UserControl x:Class="MyButton.CustomButton"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    Width="400" Height="300">

    <UserControl.Resources>

        <Style TargetType="TextBlock" x:Key="mojStyl">

            <Setter Property="Foreground" Value="Green" />

            <Setter Property="Cursor" Value="Wait" />

            <Setter Property="Text" Value="Ciekawe ktory tekst sie wstawi?" />           

        </Style>       

    </UserControl.Resources>

   

<TextBlock x:Name="mojTekst" Text="Hello World!!" Height="22.537109375" Width="127" Style="{StaticResource mojStyl}"   />

</UserControl>

Styl został umieszczone w części “ UserControl.Resources”, a więc będzie widoczny w obrębie tej kontrolki. W stylu ustawiłem kolor, rodzaj kursora oraz tekst dla wszystkich TextBox-ów dla których ten styl zostanie przypisany. Efekt jest widczony, choć nie jest to nic specjalnego.

Zalecam zainteresować się tematem stylów, których możliwości są dużo większe, niż  zaprezentowałem powyżej. Przy pomocy stylów możemy w prosty sposób zdefiniować domyślny wygląd kontrolek w aplikacji poprzez stworzenie template-ów, możemy definiować specyficzne zachowanie kontrolek oraz nadpisywać wcześniej zdefiniowane. Temat ten to materiał na dobrych kilka artykółów w związku z tym zatrzymam się na tym co widzimy powyżej ;).

Aby zdefiniować zachowanie kontorlki, np. na najechanie na nią myszką wystarczy dodać metody obsługujące te zdarzenia:

<Button x:Name="mojButton" Cursor="Hand" Height="Auto" Width="Auto" MouseEnter="mojButton_MouseEnter"  MouseLeave="mojButton_MouseLeave">

 

Do zdarzeń tych musimy stworzyć obsługujące je metody, kod metod umieszczamy w pliku .cs, w moim przykładzie jest to „CustomButton.xaml.cs”.

private void mojButton_MouseEnter(object sender, MouseEventArgs e)

{

        najedz.Begin();

}

 

private void mojButton_MouseLeave(object sender, MouseEventArgs e)

{

        zjedz.Begin();

}

Jak widać powstały dwie metody obłsugujące zdarzenia najechania myszką oraz zjechania myszką z button-a. Dodatkowo przy każdym ich wystąpieniu wywoływana jest animacja. Są to animacje o nazwach „najedz” i „zjedz”. Kod takich animacji najlepiej wygenerować w programie Expression Blend (pisałem o nim w którymś z poprzednich artykółów). Kod moich animacji wygląda następująco:

<Storyboard x:Name="najedz">

                <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mojButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" BeginTime="00:00:00">

                               <SplineDoubleKeyFrame KeyTime="00:00:02" Value="0.8"/>

                </DoubleAnimationUsingKeyFrames>

                <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mojButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" BeginTime="00:00:00">

                               <SplineDoubleKeyFrame KeyTime="00:00:02" Value="0.8"/>

                </DoubleAnimationUsingKeyFrames>

        </Storyboard>

        <Storyboard x:Name="zjedz">

                <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mojButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" BeginTime="00:00:00">

                               <SplineDoubleKeyFrame KeyTime="00:00:02" Value="1"/>

                </DoubleAnimationUsingKeyFrames>

                <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mojButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" BeginTime="00:00:00">

                               <SplineDoubleKeyFrame KeyTime="00:00:02" Value="1"/>

                </DoubleAnimationUsingKeyFrames>

        </Storyboard>

Animacje te umieszczone są pomiędzy znacznikami <UserControl.Resources> </UserControl.Resources>, na tym samym poziomie co < Style>.

demo

Source code do tego etapu:

Source code

4.  Dodawanie i wywoływanie event-ów z kontrolki.

W tym etapie pokaże w jaki sposób możemy wywoływać metody obiektów, które zawierają w sobie kontrolkę. Jak to zrobić? A no możemy zrealizować to tworząc własnego eventa.

W pliku .cs kontrolki („CustomButton.xaml.cs”) dodajemy event:

public event EventHandler ZmienTekst;

Ponieważ chcemy go uruchomić, to dodajemy do kontrolki np. obsługę kliknięcia myszką (MouseLeftButtonDown):

<Button x:Name="mojButton" Cursor="Hand" Height="Auto" Width="Auto" MouseEnter="mojButton_MouseEnter"  MouseLeave="mojButton_MouseLeave" RenderTransformOrigin="0.5,0.5"

                    MouseLeftButtonDown="mojButton_MouseLeftButtonDown" >

W kodzie .cs kontrolki („CustomButton.xaml.cs”) obsługujemy zdarzenie kliknięcia, czyli implementujemy metodę „mojButton_MouseLeftButtonDown”. Metodę tą implementujemy tak, aby wywołała stworzony Event.

private void mojButton_MouseLeftButtonDown (object sender, MouseButtonEventArgs e)

{

            // zrób co masz zrobić w kontrolce

            if (!e.Handled && null != ZmienTekst)

            {

                ZmienTekst(this, EventArgs.Empty);

            }

}

W tym momencie wszystkie metody z innych kontrolek, które podpieły się pod stworzony event „ZmienTekst” zostaną wywołane po kliknięciu muszką w Button „mojButton”.

Aby wywołać zewnętrzną metodę z event-a, w pliku Page.xaml dodajemy wywołanie eventa oraz metodę, która go obsłuży:

<uc:CustomButton Tekst="domyslna" ZmienTekst="CustomButton_ZmienTekst" />

Page.xaml.cs:

private void CustomButton_ZmienTekst(object sender, EventArgs e)

{

            ((CustomButton)sender).Tekst = "Nowy tekst z Page.xaml";

            LayoutRoot.Background = new SolidColorBrush(Color.FromArgb(255, 200, 200, 200));

 }

Powyższa metoda zrzutuje sender na obiekt kontrolki i ustawi wcześniej zefinioniowaną przez nas właściowość „Tekst”. Metoda ta także zmienia kolor tła w naszej aplikacji na jakiś szary.

W ten o to prosty spobób, zreazliwaliśmy komunikację pomiędzy obiektami, które zawierają w sobie kontrolkę oraz samą kontorlką. Jeśli chcemy wywołać metody kontroki z kontrolek które ją zawierają, wystarczy zdefiniować publiczną metodę. Natomiast, jeśli chcemy z kontorlki wywołać metodę w obiekcie do którego ona należy, należy stworzyć event i zdefiniować połączenie eventa z tą metodą.

 

demo

Source code do tego etapu:

Source code

 

5. Zamykamy kontrolkę w assembly

No i na końcu chciałbym pokazać Wam w jaki sposób możemy nasze kontorlki skompilować do dll i podłączyć do dowolnego projektu. Zamknięcie takie zabezpiecza nas przed zmianami w kodzie oraz ułatwia ponowne użycie w innych projektach.

Aby to zrobić, stwórzmy nowy projekt ale tym razem nie „Silverlight Application”, a „Silverlight Class Library”.

Po utworzeniu, w solution explorer skasujmy plik Class.cs. Tworzymy kontrolkę silverlight-ową, a więc nie będziemy potrzebować pliku klasy. Następnie dodajemy do projektu kontrolkę silvelright-ową („Silverlight User Control”).

Tak jak w punkcie pierwszym, do kontrolki dodajemy TextBox z jakimś napisem, np. :

<UserControl x:Class="KontrolkaJakoAssembly.Kontrolka"

    xmlns="http://schemas.microsoft.com/client/2007"

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

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

        <StackPanel>

            <TextBlock Text="Hello World!!" />

        </StackPanel>

    </Grid>

</UserControl>

Projekt kompilujemy i w katalogu ClientBin powinien pojawić się nam plik dll. Plik ten możemy podłączyć do dowolnego projektu silverlight-owego i uzywać stworzonej kontrolki na tyle sposobów na ile nam się tylko zamarzy.

Aby dodać dll do innego projektu, klikamy prawym w Solution Explorerze i wybieram opcję „Add Reference”. Jeżeli nasz projekt jest w innym Solution to przechodzim do zakładki trzeciej (Browse) i wskazujemy stworzony plik dll, jeśli natomiast projekt kontrolki znajduje się w tym samy Solution, to możemy wskazać cały projek w zakładce drugiej (Projects).

W kodzie .cs powinniśmy w tym momencie mięć możliwość utworzenia obiektu o typie naszej kontrolki, np.:

KontrolkaJakoAssembly.Kontrolka kontrolka = new KontrolkaJakoAssembly.Kontrolka();

, gdzie „KontrolkaJakoAssembly” to nazwa projektu kontorlki, a „Kontrolka” to nazwa klasy. Jeśli chcemy używać kontrolki w kodzie xaml musimy zrobić podobnie jak to było w punkcie pierwszym, czyli dodać namespace.

xmlns:kuc="clr-namespace:KontrolkaJakoAssembly;assembly=KontrolkaJakoAssembly"

<kuc:Kontrolka />

Source code do tego etapu:

Source code

 

Materiały na których się wzorowałem:

-          http://silverlight.net/learn/labs.aspx  Silverlight 2 Hands-On Labs, polecam!

 

Jacek Ciereszko