System zdarzeń - wyzwalanie i nasłuchiwanie

Transkrypt

System zdarzeń - wyzwalanie i nasłuchiwanie
System zdarzeń - wyzwalanie i nasłuchiwanie
Informacje o module
Opis modułu
W tym rozdziale dowiesz się czym są zdarzenia silverlight, jakie rodzaje
zdarzeo obsługuje oraz jakie strategie informowania o zajściu zdarzenia
wykorzystano tworząc strukturę komponentów wizualnych. Dowiesz się jak
zaprojektowad własny system zdarzeo. Nauczysz się wykorzystywad
elastycznośd systemu zdarzeo Silverlight w projektowaniu logiki twojej
aplikacji.
Cel modułu
Celem modułu jest przedstawienie koncepcji programowania z
wykorzystaniem systemu zdarzeo i komend. Nadrzędnym celem jest
zrozumienie różnic pomiędzy wyzwoleniem zdarzenia, jego obsługą oraz
strategią roznoszenia informacji o jego zajściu.
Uzyskane kompetencje
Po zrealizowaniu modułu będziesz:
wiedział czym są zdarzenia oraz jakie koncepcje programistyczne rządzą
ich zachowaniem,
potrafił wykorzystad siłę systemu zdarzeo komponentów Silverlight,
rozumiał jak budowad, wyzwalad i nasłuchiwad własnych zdarzeo.
Wymagania wstępne
Przed przystąpieniem do pracy z tym modułem powinieneś:
znad podstawy języka C#
rozumied mechanizm delegowania metod,
Przygotowanie teoretyczne
Przykładowy problem
Chcesz zaprojektowad aplikację pozwalająca przetwarzad dokumenty tekstowe. Z każdym
dokumentem wiążesz proste menu obsługujące operacje nao przeprowadzane. Niemniej chcesz
zaprojektowad aplikację elastycznie, aby umożliwid przenoszenie fragmentów treści pomiędzy
oknami, zapisywanie wszystkich dokumentów, zamykanie itp. Aplikację tego typu stworzyd można w
sposób piękny inżynieryjnie albo w sposób ekstremalny (tzw. kod spaghetti). Niezależnie którą
metodę wybierzesz powinieneś dopasowad się do wzorców informowania o zajściu określonych
zdarzeo. Dodatkowo powinieneś umied tworzyd własne wzorce zdarzeo oraz metody je obsługujące.
Przygotowanie teoretyczne
Technicznie, zdarzenie to nic innego jak zmienna typu delegowanego poprzedzona słowem
kluczowym event, np.:
public event SimpleOne NaszEvent;
Słowo kluczowe event pozwala na wywołanie delegowanego kodu tylko z poziomu właściciela
zmiennej. Jeśli typ delegowany nie zwraca żadnej wartości, wtedy przy pomocy operatorów „+=”
oraz „-=” możemy zarejestrowad (wyrejestrowad) więcej niż jedną metodę.
Delegowany kod wykonywany jest (zdarzenie jest wyzwalane) przy spełnieniu określonego warunku
np.:
if ( zdjęcia_wybrane.Count > 0 ) NaszEvent(zdjęcia_wybrane);
Podsumowując, system zdarzeo wykorzystuje w standardowy mechanizm delegowania kodu który
znasz z C#. W poprzednim module poznałeś zdarzenia zdefiniowane w klasie UIElement
odpowiedzialne za interakcję z kursorem myszy czy klawiaturą.
W Silverlight zwyczajowo zakłada się że wzorzec delegacji posiada format
void NazwaTypu(Object sender, EventArgs arg)
gdzie:
sender – referencja do obiektu który wyzwala zdarzenie,
arg – pozwala przekazad informacje o specyficznych własnościach zdarzenia.
Podobnie w zwyczaju jest umieścid metodę pozwalającą klasom potomnym wyzwolid zdarzenie
(wzorzec OnEvent), czyli np.:
protected void OnMojeZdarzenie(EventArgs arg)
{
If (MojeZdarzenie !=null ) MojeZdarzenie(this, arg);
}
Przykładem zdarzenia które wykorzystuje standardowe mechanizmy CLR jest LayoutUpdated klasy
FrameworkElement, zdefiniowane jako:
public event EventHandler LayoutUpdated
Wyzwalane jest wtedy gdy zaszła zmiana wielkości lub położenia komponentu lub jego kontrolkidziecka. Mimo tego, że zdarzenie to ma prototyp:
public delegate void EventHandler(Object sender, EventArgs e)
Nie możemy zakładad, że sender wskazywał będzie na kontrolkę na która wykonała zmianę wyglądu.
Zdarzenie to zawsze wyzwalane jest z pierwszym argumentem wynoszącym null. Zachowanie to
tłumaczy się potrzebą generowania kodu który będzie bezpośrednio związany z kontrolką. Jeśli znasz
WPF (Windows Presentation Foundation) to pewnie zauważyłeś, że tam zdarzenie zawiera niepustą
referencję na obiekt na którym zaszło. Dodatkowo jest to zdarzenie innego typu niż zwyczajowy
EventHandler, jest to mianowicie RoutedEventHandler.
Rozgłaszanie zdarzeń
Niektóre zdarzenia po ich wyzwoleniu informują inne kontrolki o fakcie wyzwolenia zdarzenia. Proces
ten nazywany rozgłaszaniem zdarzeo. Weźmy pod uwagę następujący fragment kody XAML:
<StackPanel>
<TextBlock Text=”Wybierz tło” />
<StackPanel>
<TextBlock Text=”Zwykłe”>
<Image… />
<Image… />
</StackPanel>
<StackPanel>
<TextBlock Text=”Obrazy”>
<Image… />
</StackPanel>
</StackPanel>
Definiuje on drzewo wizualne postaci:
StackPanel
TextBlock
TextBlock
StackPanel
Image
StackPanel
Image
TextBlock
Image
Rys. 1 Drzewo wizualne
Jeśli na którymś z obiektów Image wyzwolono zdarzenie MouseLeftButtonDown wtedy oczywiście
wykonany jest kod obsługujący to zdarzenie. Dodatkowo jednak to zdarzenie obsłużone jest na jego
bezpośrednim ojcu, a następnie na ojcu ojca itd.
StackPanel
MouseLeftButtonDown
TextBlock
StackPanel
StackPanel
MouseLeftButtonDown
TextBlock
Image
Image
TextBlock
Image
MouseLeftButtonDown
Rys. 2 Rozgłaszanie zdarzenia
Strategia takiego rozgłaszania komunikatów w górę znana jest np. z WPF (pod nazwą bąbelkowania,
ang. Bubling). Jest to jedyna standardowo wbudowana w Silverlight strategia rozgłaszania
komunikatów.
Warto podkreślid, że o zajściu zdarzenia informowani są tylko bezpośredni rodzice. Proces
rozgłaszania przypomina ścieżkę czyli inne kontrolki niż te leżące na ścieżce nie zostaną
poinformowane o zajściu MouseLeftButtonDown.
Częstym scenariuszem użycia jest obsługa zdarzenia tylko na ważnym panelu, który możemy ukryd po
wybraniu któregoś elementu.
Pojawiają się naturalne pytania:
Jak znaleźd obiekt na którym zdarzeni zostało wyzwolone na początku ścieżki rozgłaszania?
Czy rozgłaszanie można zablokowad?
Technicznie zdarzenia które mogą być (nie wszystkie są) rozgłaszane dziedziczą z klasy
RoutedEvent. Ogólny wzorzec delegacji ma postad:
public delegate void RoutedEventHandler(Object sender, RoutedEventArgs e)
Jedyną różnicą jest typ argumentu mamy tutaj mianowicie klasę RoutedEventArgs. Klasa ta
dziedziczy z EventArgs, ale udostępnia nam własnośd OriginalSource. Odczyt możliwy jest
zawsze i winien wskazywad na kontrolkę na której wyzwolono zdarzenie (początek ścieżki
rozgłaszania komunikatu).
Odpowiedź na pytanie jak zablokowad rozgłaszanie komunikatu nie jest tak łatwa. Niektóre typy
argumentów eksponują własnośd Handled typu logicznego. Przypisanie tej własności wartości true
skutkuje często zatrzymaniem rozgłaszania. Nieco dokładniej – rozgłaszanie jest kontynuowane, ale
kod wykonywany jest warunkowo (warunek to !Handled).
Jeśli rozgłaszania nie można powstrzymad przy pomocy Handled (bo np. nie istnieje taka własnośd),
zawsze można porównad referencje sender oraz OriginalSource, pozwoli to wykonywad kod
tylko na kontrolce na której zdarzenie zaszło.
W Silverlight 4 nie można definiowad własnych zdarzeo rozgłaszanych. Można natomiast
zaprojektowad własny system zdarzeo pozwalający na wykonanie podobnej strategii, więcej na ten
temat znajdziesz w module „Własności zależne i doczepiane”.
W poniższej tabeli zabrano standardowe zdarzenia których zajście jest rozgłaszane.
Zdarzenie
KeyDown
KeyUp
Typ argumentu
KeyEventArgs
Handled
TAK
MouseLeftButtonDown
MouseRightButtonDown
MouseButtonEventArgs
MouseLeftButtonUp
(patrz moduł poprzedni)
TAK
MouseRightButtonUp
MouseMove
MouseEventArgs
NIE
MouseWheel
MouseWheelEventArgs
TAK
DragEnter
DragLeave
DragEventArgs
DragOver
(patrz dalsza częśd modułu)
TAK
Drop
GotFocus
RoutedEventArgs
NIE
LostFocus
Używając standardowego systemu zdarzeo Silverlight 4 pamiętad należy, iż implementacja kontrolki
może mied wpływ na odziedziczony system zdarzeo. Standardowym przykładem jest kontrolka
Button. Przycisk dziedziczy z klasy ButtonBase w której zdefiniowano nowe zdarzenie Click.
Dodatkowo przycisk posiada własnośd ClickMode której można przypisad jedną z trzech wartości:
Hover – Click wyzwalany jest gdy kursor znajdzie się nad kontrolką,
Press - Click wyzwalany jest gdy przyciśnięto lewy przycisk myszy,
Release – Click wyzwalany jest gdy zwolniono wcześniej wciśnięty przycisk myszy.
Standardowe zdarzenie MouseLeftButtonUp zostanie wyzwolone tylko w przypadku pierwszym
(Hover), pozostałe dwa automatycznie ustawiają własnośd Handled. Pokazuje to zmiany w strategii
wyzwalania które mogą wprowadzad kontrolki na gruncie ich implementacji.
Mimo tego, że delegacja Click jest typu RoutedEventHandler oraz argument posiada własnośd
Handled, zdarzenie nie rozgłasza swego wyzwolenia.
Obsługa przeciągnij i upuść
Silverlight wspiera obsługę mechanizmu Przeciągnij i Upuśd (ang. Drag and Drop). W chwili obecnej
przeciągad można lokalne pliki klienta do aplikacji. Jak wspomniano wcześniej mechanizm oparto
zdarzenia rozgłaszane. Argument wszystkich metod jest typu DragEventArgs w szczególności
eksponuje on własnośd Data. Własnośd zawiera referencję do obiektu implementującego interfejs
IDataObject. Interfejs ten umożliwia wydobycie danych na potrzeby naszej aplikacji.
Zdarzenia pozwalające obsłużyd przeciąganie to:
DragEnter – wyzwalane gdy nad powierzchnią kontrolki znalazł się obiekt możliwy do
opuszczenia (można wtedy np.: wygenerowad podgląd tego co stanie się gdy obiekt będzie
opuszczony),
DragLeave – wyzwalane gdy obiekt możliwy do opuszczenia znalazł się poza powierzchnią
kontrolki,
DragOver - wyzwalane gdy obiekt znajduje się nad powierzchnią kontrolki.
Dodatkowo po opuszczeniu pliku nad powierzchnią kontrolki wywoływane jest zdarzenie Drop. Aby
mechanizm działał nad powierzchnią kontrolki musi zostad ustawiona wartośd AllowDrop oraz
kontrolka musi byd widoczna.
Dostęp do danych za pośrednictwem interfejsu IDataObject, który w szczególności zawiera
następujące metody:
GetData – pozwala wydobyd dane. Jedynym prawidłowym argumentem jest w chwili obecnej
DataFormat.FileDrop.
GetFormats – zwraca format danej lub formaty do których można daną konwertowad.
GetDataPresent – informuje czy dane posiadają zadany format lub mogą byd do niego
konwertowane
Przykładowe wydobycie danych może odbywad się następująco:
IDataObject dane = e.Data;
If (data.GetDataPresent(DataFormats.FileDrop)) {
FileInfo[] pliki = data.GetData(DataFormats.FileDrop) as FileInfo[];
foreach (var plik in pliki) { … }
}
Wzorzec komendy
Ważnym elementem pozwalającym oddzielid cześd wizualną aplikacji od logiki biznesowej jest
system komend. Komendy (and. Command) pozwalają na komunikację między kontrolkami nie tylko
w obrębie ścieżki rozgłaszania komunikatu.
Ogólnie rzecz ujmując w chwili zajścia określonego zdarzenia Silverlight może wywoład pewną
metodę podobnie jak wywołuje się zwykłe zdarzenie. Skoro tak, to jaka jest różnica pomiędzy
zwykłym zdarzeniem a komendą?
Komendy mogą mied wpływ na element który je wykonuje (mogą np. zabronid wykonania). Inną
istotną cechą jest możliwośd definiowania komend na podstawie interfejsów i danych. Nazwy
komend wraz ich argumentami definiowane są z poziomu składni XAML, a szczegółowe znaczenie
nadawane jest przez bieżącą implementację zdefiniowaną przez własnośd DataContext dziedziczoną z
klasy UIElement.
Komendy pozwalają na realizację wzorca projektowego MVVM (ang. Model-View View-Model) czy
wzorca MVC (ang. Model View Controller), dzięki temu szczególnego znaczenia nabierają przy
projektach większej skali.
Kontrolki które wspierają komendy to: Button oraz HyperlinkButton. Posiadają one własności:
Command typu ICommand – interfejs implementujący komendę,
CommandParameter typu Object – argument przekazany komendzie.
Interfejs ICommand ma postad:
Public interface ICommand {
void Execute(Object Parameter);
bool CanExecute(Object Parameter);
event EventHandler CanExecuteChanged;
}
Metoda Execute powinna zawierad kod wykonywany w chwili uruchomienia (wyzwolenia)
komendy. Argumentem komendy w chwili wyzwolenia staje się CommandParameter.
Metoda CanExecute uzyskuje ten sam argument co metoda Execute ale nie powoduje wykonania
wyzwolenia komendy informuje czy dla tego argumentu można komendę wykonad. Gdy zwróci
wartośd false, oba przyciski przejdą w stan wyłączony (tj. !Enabled).
Zdarzenie CanExecuteChanged wyzwolone zostanie wtedy gdy zmieni się wartośd metody
CanExecute (np.: poprzednie argumenty były prawidłowe, a nowy argument jest niezrozumiały).
Standardowy przypadek użycia to zamiana tła kontrolek na czerwony gdy podano nieprawidłowy
argument.
Silverlight nie wymusza stosowania określonego wzorca projektowego a tym samym nie dostarcza
gotowych implementacji interfejsu ICommand. Posiadamy pełną swobodę projektowania. Można
pokusid się o stworzenie złożonego systemu komend pozwalających np.: grupowad i wyzwalad inne
komendy.
Temat komend rozwinięty jest w module Łączenie danych i automatyzacja wyświetlania.
Kontekst danych i odświeżanie zawartości
Ważną klasę zdarzeo stanowią zdarzenia związane z odświeżaniem zawartości komponentów których
zawartośd generowana jest na podstawie kolekcji. Ponieważ zagadnienie to jest blisko związane z
budową systemu komend zarysujemy tutaj technikę tworzenia takich komponentów oraz
odświeżania ich zawartości.
W klasie UIElement istnieje specyficzna własnośd DataContext. Własnośd ta najogólniej rzecz
biorąc pozwala z dowolnym komponentem wiązad zbiór danych (np.: klasę). Tak utworzona
przestrzeo danych widoczna jest przez wszystkie kontrolki dzieci komponentu któremu przypisano
DataContext.
Załóżmy, że posiadamy implementację następującego interfejsu:
public interface WyborTla {
ICommand checkCommand { set; get; }
ObservableCollection<DaneTestowe> data { get; }
};
Z poziomu kodu XAML powinniśmy dołączyd nasza implementację do zasobów aplikacji, można
uczynid to w poniższy sposób:
<UserControl.Resources>
<loc:TestowyViewModel x:Key="testvm" />
</UserControl.Resources>
Zasoby to nic innego jak słownik. Dołączony zasób powinien mied zdefiniowany domyślny konstruktor
(bezargumentowy). Każdy zasób winien byd nazwany dzięki x:Key.
Tak opakowane dane możemy przypisad do kontekstu danych dowolnej kontrolki, np.:
<Grid DataContext="{StaticResource testvm}">
</Grid>
Zapis powyższy nakazuje wyszukanie zasobu o nazwie testvm oraz a następnie przypisanie tego
zasobu jako wartości DataContext.
Innym sposobem dostępu do danych jest wiązanie danych (ang. Binding). Na przykład jeśli kontrolkądzieckiem powyższego panelu Grid jest Button wtedy możemy z bieżącego kontekstu danych
wybrad komendę której nazwę zawiera interfejs WyborTla, np.:
<Grid DataContext="{StaticResource testvm}">
<Button Command="{Binding checkCommand}" />
</Grid>
Użycie słowa Binding powoduje dowiązanie wartości Command to obiektu przechowywanego nie w
zasobach ale w lokalnym kontekście danych. Dowiązanie wykonane zostanie na etapie uruchomienia.
Więcej o zasobach i wiązaniu danych znajdziesz w module zasoby i wiązanie danych.
Jednym z paneli które pozwalają na automatyzację pracy ze zbiorami elementów jest panel
ListBox. Zawiera on własnośd ItemsSource którą możemy dowiązad do dowolnej kolekcji
elementów. W poniższym przykładzie
<ListBox ItemsSource="{Binding data}" >
</ListBox>
Kolekcja wyszukiwana jest w bieżącym kontekście danych. Komponent ListBox przegląda element
po elemencie takiej kolekcji i do swojej zawartości dodaje kontrolkę powstałą na bazie wzorca.
Wzorzec kontrolki, typu DataTemplate, należy przypisad własności ItemTemplate. Np.:
<DataTemplate>
<StackPanel>
<TextBlock Text="Silverlight" />
<Image Width="200" Height="200" />
</StackPanel>
</DataTemplate>
Oznacza to, że wzorzec kontrolki składał się będzie z obiektu StackPanel na którym znajdzie się
TextBlock a następnie Image. Więcej o wzorcach i stylach dowiesz się w module Style i
automatyzacja wyświetlania.
Najważniejsze z punktu widzenia jest to, że w chwili tworzenia dowolnej kontrolki na podstawie
ItemTemplate jej kontekst danych rozszerzany jest o dane elementu kolekcji przypisanej do
ItemSource. Zatem skoro kolekcja data jest typu ObservableCollection<DaneTestowe>, to
każda budowana kontrolka będzie miała dostęp do danych elementów kolekcji. Załóżmy, że klasa
DaneTestowe ma postad:
public class DaneTestowe
{
public String nazwa;
public Uri uri;
}
Wtedy prawidłowe jest wiązanie:
<ListBox ItemsSource="{Binding data}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding nazwa}" />
<Image Width="200" Height="200"
Source="{Binding uri}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Dochodzimy zatem do meritum sprawy. O ile wyświetlenie nie stanowi problemu, to co się stanie gdy
kolekcja data zmieni swoją zawartośd?
Ze względów wydajnościowych zawartośd kolekcji data jest wstępnie klonowana, dopiero później
rozpoczyna się proces Budowy kontrolek. Oznacza to, że jeśli zawartośd kolekcji zmieni się, może to
pozostad bez skutku ekranowego. Z jednej strony można dzięki temu przygotowad nową tablicę
danych podczas gdy użytkownik widzi poprzednią zawartośd (np.: etap ładowania danych), ale z
drugiej kiedyś należy poinformowad ListBox aby zaktualizował zawartośd. Na szczęście typ
ObservableCollection implementuje zdarzenie CollectionChanged. Ponieważ na etapie
wiązania ListBox deleguje kod do obsługi tego zdarzenia. Każda zamiana zawartości kolekcji
skutkuje zamianą zawartości komponentu ListBox.
Klasa ListBox umożliwa wybór jednego lub wielu elementów, zaimplementowano bowiem
zdarzenie SelectionChanged. Dostęp w wybranego elementu uzyskuje się przez własnośd
SelectedItem. Dostęp do grupy wybranych elementów możliwy jest przez własnośd
SelectedItems.
Przykładowe rozwiązanie
Chcemy zaprojektowad wyszukiwarkę teł do foto-książki. Tła pobierane mają byd domyślnie z serwera
niemniej aby nie pobierad wszystkich teł natychmiast chcemy pobrad tylko ich nazwy wraz z
towarzyszącym im URI do pliku. Użytkownik wprowadzi fragment nazwy która go interesuje i będzie
musiał wcisnąd przycisk aby obrazy pobrad obrazy pobrad. Przy okazji wykonamy pierwszy krok
projektując kod testowy przypominający wzorzec MVVM.
Załóżmy, że mamy do zaimplementowania interfejs WyborTla (pojawił się wcześniej w
przygotowaniu praktycznym), następującej postaci:
public interface WyborTla {
ICommand checkCommand { set; get; }
ObservableCollection<DaneTestowe> data { get; }
event EventHandler BackgroundSelected;
void PictureSelected(String name);
};
Komenda checkCommand realizowała będzie wybór tła, data będzie punktem wejścia do danych
wyświetlanych przez ListBox, a BackgroundSelected będzie to zdarzenie wyzwalane gdy
wybrano tło.
Załóżmy, że to klasa danych implementowała dziedziczyła będzie z ObservableCollection, np.:
public class TestowyViewModel : ObservableCollection<DaneTestowe>, WyborTla
{
public TestowyViewModel()
{
DaneTestowe[] dane = { new DaneTestowe("Tulipany",
new Uri("Tulips.jpg",…)),
new DaneTestowe("Pingwiny",
new Uri("Penguins.jpg",…))
};
…
}
}
Ponieważ tworzymy wersję testową powinniśmy utworzyd dane testowe.
Tworzona komenda powinna posiadad referencje do klasy TestowyViewModel oraz, dla potrzeb
prototypu, referencję do kolekcji danych z których mamy wybrad tło. Nazwijmy klasę komendy
CheckCMD, może ona mied definicję:
protected class CheckCMD : ICommand
{
public event EventHandler CanExecuteChanged;
private DaneTestowe[] t;
private TestowyViewModel tloi;
public CheckCMD(DaneTestowe[] tab, TestowyViewModel tloi)
{
t = tab;
this.tloi = tloi;
}
}
Metoda CanExecute powinna zadziaład wyłącznie wtedy gdy argumentem jest niepusty łaocuch.
Metoda ta winna stwierdzid czy istnieją tła posiadające w nazwie podany fragment znaków.
Przykładowo:
public bool CanExecute(Object param)
{
if (param is String)
{
String selector = param as String;
if (selector!="")
foreach (DaneTestowe dt in t)
{
if (dt.nazwa.Contains(selector)) return true;
}
}
return false;
}
Odrobinę ciekawsza jest metoda Execute która winna dodad do danych wszystkie elementy które
posiadają w nazwie zadany ciąg znaków, przykładowo:
public void Execute(Object param)
{
String selector = param as String;
if (selector != "")
{
tloi.Clear();
foreach (DaneTestowe dt in t)
{
if (dt.nazwa.Contains(selector)) tloi.Add(dt);
}
}
}
Pozostaje zaimplementowad dostęp do danych, czyli:
public ObservableCollection<DaneTestowe> data {
get
{
return this;
}
}
Właśnie zakooczyliśmy budowę zaplecza naszego rozwiązania. Kolejny krok budowa części wizualnej.
Większośd zaprezentowano już w części teoretycznej, teraz więc pokusimy się o przedstawienie
znaczącej zawartości kontrolki:
<UserControl.Resources>
<loc:TestowyViewModel x:Key="testvm" />
</UserControl.Resources>
<StackPanel DataContext="{StaticResource testvm}" >
<ListBox ItemsSource="{Binding data}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding nazwa}" />
<Image Source="{Binding uri}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Name="textBox" VerticalAlignment="Top"
Text="wpisz fragment nazwy" />
<Button Content="Pobierz" Name="Przycisk"
Command="{Binding checkCommand}"
CommandParameter="{Binding Path=Text, ElementName=textBox}"/>
</StackPanel>
Jedyna zmianą jest wiązanie argumentu komendy z własnością Text komponentu o nazwie
textBox.
Pozostaje obsługa samego wyboru tła. Realizujemy to obsługując zdarzenie SelectionChanged
zależnie od potrzeb aplikacji możemy wyzwolid zdarzenie BackgroundSelected z jednym obrazem
tła albo z wieloma.
Rys. 3 Rezultat do ulepszenia przez grafika
Porady praktyczne
Często założenie o sposobie wyzwalania zdarzenia zależy od implementacji kontrolki. Brak
wyzwolenia albo wyzwolenie w innym momencie, to podstawowe problemy implementacyjne
(patrz Button) dlatego uważnie badaj kontrolki których używasz.
Problemem są różnice w wyzwalaniu komunikatów pomiędzy WPF a Silverlight, np.: WPF
zdarzenia MouseEnter oraz MouseLeave wyzwalane są nawet w przypadku nieruchomego
kursora (wystarczy, że np. animowany element znajdzie się pod kursorem myszy) natomiast w
Silverlight zdarzenia te wyzwalane są tylko w przypadku poruszania kursorem myszy. Gdy nie
jesteś pewien działania a znasz już WPF zaprojektuj odpowiedni test w Silverlight.
Nie zawsze zdarzenia typu RoutedEventHandler rozgłaszają informacje o wyzwoleniu.
Na potrzeby mechanizmu Drag and Drop powinieneś ustawid parametr windowless na
wartośd true. Czasem będziesz musiał dopisad wrapper w JavaScirpt poza aplikacją
Silverlight aby przenoszenie działało prawidłowo.
Komendy to doskonały mechanizm rozgłaszania wyzwolenia zdarzenia. Przykładem komend
wbudowanych jest mechanizm Kopiuj/Wklej.
Uwagi dla studenta
Jesteś przygotowany do realizacji laboratorium jeśli:
Rozumiesz pojęcie wyzwalania zdarzenia,
Rozumiesz pojęcie komendy oraz jej wyzwalania,
Potrafisz zakooczyd proces rozgłaszania zdarzenia,
Potrafisz dowiedzied się który komponent wyzwolił zdarzenie rozgłaszane,
Umiesz zaprojektowad kod wykorzystujący system komend,
Potrafisz zaprojektowad własne zdarzenia,
Potrafisz wyzwolid własne zdarzenia,
Wiesz jak działa mechanizm przeciągnij i upuśd.
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.
Dodatkowe źródła informacji
Więcej o zdarzeniach w Silverlight 4 znajdziesz:
http://msdn.microsoft.com/en-us/library/cc189018%28v=vs.95%29.aspx
Więcej o sposobach przygotowania komend znajdziesz:
http://msdn.microsoft.com/en-us/library/ff921126%28PandP.20%29.aspx
Laboratorium podstawowe
Zadanie 1 (czas realizacji 20 minut)
Twój nowy klient poprosił o prototyp interfejsu do wyboru odcienia płytek podłogowych. Jego
życzeniem jest paleta kolorów rozłożonych dookoła wspólnej osi obrotu przesuwając kursor obracasz
elementy. Zatwierdzasz wybór odpowiednim przyciskiem. Ponieważ jego firma udostępnia
oprogramowanie na ekranach dotykowych starej konstrukcji (ang. SingleTouch) możesz wykorzystad
tylko zdarzenia MouseLeftButtonDown, MouseLeftButtonUp oraz MouseMove.
Zadanie 2 (czas realizacji 25 minut)
Twój główny klient pragnie wzbogacid oprogramowanie o menu służące do wybory rodzaju pisma.
Jednak nie chce klasycznego menu gdzie osobno wybiera się czcionkę, jej wielkośd, styl oraz kolor.
Przygotował pieczołowicie tablice danych krojów które jego odbiorcy będą mieli do wyboru.
Wizualna cześd menu powinna wyświetlad podgląd kroju na tekście przykładowym. Masz jak zawsze
niewiele czasu na realizację tego zadania.
Laboratorium rozszerzone
Zadanie 1 (czas realizacji 40 minut)
Zaprojektuj typ generyczny ułatwiający pracę z komendami. Typ powinien byd tworzony na
podstawie klasy oczekiwanego argumentu. Dodatkowo powinno byd możliwe rejestrowanie metod
wykonanych przy okazji wykonania metody Execute.
Zadanie 2 (czas realizacji 45 minut)
Zaprojektuj typ generyczny ułatwiający pracę z komendami złożonymi. Komenda złożona winna
wykonywad kolekcję komend prostych.
Wskazówka. Możesz założyd, że jeśli co najmniej jedna komenda może byd wykonana to również
komenda złożona może byd wykonana. Natomiast Execute wykonaj tylko na komendach dla których
CanExecute jest prawdą.
Zadanie 3 (czas realizacji 90 minut)
Do zadania 3 z poprzedniego modułu (foto-książka) dołącz możliwośd przeciągania zdjęd z zasobów
użytkownika.
Wskazówka. Powinieneś zaimplementowad alternatywę do przycisku dodawania zdjęd.
Zadanie 4 (czas realizacji 90 minut)
Do zadania 3 z poprzedniego modułu (foto-książka) dołącz możliwośd wyboru teł.
Wskazówka. Dowolny panel gdy jest niewidoczny (!Visible), nie bierze udziału w przydziale
miejsca. W ten sposób schowasz Panel wyboru tła gdy nie będzie potrzebny.

Podobne dokumenty